Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New type hierarchy for ValueSupport #945

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions docs/src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ The `ValueSupport` sub-types defined in `Distributions.jl` are:

**Type** | **Element type** | **Descriptions**
--- | --- | ---
`Discrete` | `Int` | Samples take discrete values
`Continuous` | `Float64` | Samples take continuous real values
`CountableSupport{T}` | `T` | Samples take any discrete values
`ContiguousSupport{T <: Integer}` | `T` | Samples take any contiguous integer values
`Discrete = ContiguousSupport{Int}` | `Int` | Samples take `Int` values
`ContinuousSupport{T <: Number}` | `T` | Samples take continuous values
`Continuous = ContinuousSupport{Float64}` | `Float64` | Samples take continuous `Float64` values
richardreeve marked this conversation as resolved.
Show resolved Hide resolved

Multiple samples are often organized into an array, depending on the variate form.

Expand All @@ -69,22 +72,27 @@ abstract type Distribution{F<:VariateForm,S<:ValueSupport} <: Sampleable{F,S} en
Distributions.Distribution
```

To simplify the use in practice, we introduce a series of type alias as follows:
To simplify the use in practice, we introduce a series of type aliases as follows:
```julia
const UnivariateDistribution{S<:ValueSupport} = Distribution{Univariate,S}
const MultivariateDistribution{S<:ValueSupport} = Distribution{Multivariate,S}
const MatrixDistribution{S<:ValueSupport} = Distribution{Matrixvariate,S}
const NonMatrixDistribution = Union{UnivariateDistribution, MultivariateDistribution}

const DiscreteDistribution{F<:VariateForm} = Distribution{F,Discrete}
const ContinuousDistribution{F<:VariateForm} = Distribution{F,Continuous}
const CountableDistribution{F<:VariateForm, C<:CountableSupport} = Distribution{F,C}
const DiscreteDistribution{F<:VariateForm} = CountableDistribution{F,Discrete}
const ContinuousDistribution{F<:VariateForm} = Distribution{F,Continuous}

const DiscreteUnivariateDistribution = Distribution{Univariate, Discrete}
const ContinuousUnivariateDistribution = Distribution{Univariate, Continuous}
const DiscreteMultivariateDistribution = Distribution{Multivariate, Discrete}
const ContinuousMultivariateDistribution = Distribution{Multivariate, Continuous}
const DiscreteMatrixDistribution = Distribution{Matrixvariate, Discrete}
const ContinuousMatrixDistribution = Distribution{Matrixvariate, Continuous}
const CountableUnivariateDistribution{C<:CountableSupport} = UnivariateDistribution{C}
const DiscreteUnivariateDistribution = CountableUnivariateDistribution{Discrete}
const ContinuousUnivariateDistribution = UnivariateDistribution{Continuous}

const CountableMultivariateDistribution{C<:CountableSupport} = MultivariateDistribution{C}
const DiscreteMultivariateDistribution = CountableMultivariateDistribution{Discrete}
const ContinuousMultivariateDistribution = MultivariateDistribution{Continuous}

const CountableMatrixDistribution{C<:CountableSupport} = MatrixDistribution{C}
const DiscreteMatrixDistribution = CountableMatrixDistribution{Discrete}
const ContinuousMatrixDistribution = MatrixDistribution{Continuous}
```

All methods applicable to `Sampleable` also applies to `Distribution`. The API for distributions of different variate forms are different (refer to [univariates](@ref univariates), [multivariates](@ref multivariates), and [matrix](@ref matrix-variates) for details).
11 changes: 10 additions & 1 deletion src/Distributions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,30 @@ export

# generic types
VariateForm,
Support,
ValueSupport,
CountableSupport,
ContiguousSupport,
ContinuousSupport,
DiscontinuousSupport,
UnionSupport,
Univariate,
Multivariate,
Matrixvariate,
Discrete,
Continuous,
Discontinuous,
Sampleable,
Distribution,
UnivariateDistribution,
MultivariateDistribution,
MatrixDistribution,
NoncentralHypergeometric,
NonMatrixDistribution,
DiscreteDistribution,
ContinuousDistribution,
CountableUnivariateDistribution,
CountableMultivariateDistribution,
CountableMatrixDistribution,
DiscreteUnivariateDistribution,
DiscreteMultivariateDistribution,
DiscreteMatrixDistribution,
Expand Down
85 changes: 54 additions & 31 deletions src/common.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,37 @@ struct Multivariate <: VariateForm end
struct Matrixvariate <: VariateForm end

"""
`S <: ValueSupport` specifies the support of sample elements,
either discrete or continuous.
"""
abstract type ValueSupport end
struct Discrete <: ValueSupport end
struct Continuous <: ValueSupport end
`S <: Support{T}` specifies the support of sample elements as T,
either discrete, continuous or other.
"""
abstract type Support{N} end
const ValueSupport = Support{Float64}
struct ContinuousSupport{N <: Number} <: Support{N} end
abstract type CountableSupport{C} <: Support{C} end
struct ContiguousSupport{C <: Integer} <: CountableSupport{C} end
struct UnionSupport{N1, N2,
S1 <: Support{N1},
S2 <: Support{N2}} <:
Support{Union{N1, N2}} end

const Discrete = ContiguousSupport{Int}
const Continuous = ContinuousSupport{Float64}
const DiscontinuousSupport{I, F} =
UnionSupport{I, F, <: CountableSupport{I},
ContinuousSupport{F}} where {I <: Number, F <: Number}
const Discontinuous = DiscontinuousSupport{Int, Float64}

## Sampleable

"""
Sampleable{F<:VariateForm,S<:ValueSupport}
Sampleable{F<:VariateForm,S<:Support}

`Sampleable` is any type able to produce random values.
Parametrized by a `VariateForm` defining the dimension of samples
and a `ValueSupport` defining the domain of possibly sampled values.
and a `Support` defining the domain of possibly sampled values.
Any `Sampleable` implements the `Base.rand` method.
"""
abstract type Sampleable{F<:VariateForm,S<:ValueSupport} end
abstract type Sampleable{F<:VariateForm,S<:Support} end

"""
length(s::Sampleable)
Expand All @@ -50,13 +63,14 @@ Base.size(s::Sampleable{Multivariate}) = (length(s),)

"""
eltype(::Type{Sampleable})
eltype(::Type{Support})

The default element type of a sample. This is the type of elements of the samples generated
by the `rand` method. However, one can provide an array of different element types to
store the samples using `rand!`.
"""
Base.eltype(::Type{<:Sampleable{F,Discrete}}) where {F} = Int
Base.eltype(::Type{<:Sampleable{F,Continuous}}) where {F} = Float64
Base.eltype(::Type{<:Sampleable{F, <: Support{N}}}) where {F, N} = N
Base.eltype(::Type{<:Support{N}}) where {N} = N

"""
nsamples(s::Sampleable)
Expand All @@ -67,41 +81,50 @@ into an array, depending on the variate form.
nsamples(t::Type{Sampleable}, x::Any)
nsamples(::Type{D}, x::Number) where {D<:Sampleable{Univariate}} = 1
nsamples(::Type{D}, x::AbstractArray) where {D<:Sampleable{Univariate}} = length(x)
nsamples(::Type{D}, x::AbstractVector) where {D<:Sampleable{Multivariate}} = 1
nsamples(::Type{D}, x::AbstractArray{<:AbstractVector}) where {D<:Sampleable{Multivariate}} = length(x)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an unrelated change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was fixing the handling of numbers throughout the code and I spotted that multivariate was missing this function - this isn't a deletion... the edited code is in the next line:

nsamples(::Type{D}, x::AbstractVector{<:Number}) where {D<:Sampleable{Multivariate}} = 1

nsamples(::Type{D}, x::AbstractVector{<:Number}) where {D<:Sampleable{Multivariate}} = 1
nsamples(::Type{D}, x::AbstractMatrix) where {D<:Sampleable{Multivariate}} = size(x, 2)
nsamples(::Type{D}, x::Number) where {D<:Sampleable{Matrixvariate}} = 1
nsamples(::Type{D}, x::Array{Matrix{T}}) where {D<:Sampleable{Matrixvariate},T<:Number} = length(x)
nsamples(::Type{D}, x::AbstractMatrix{<:Number}) where {D<:Sampleable{Matrixvariate}} = 1
nsamples(::Type{D}, x::AbstractArray{<:AbstractMatrix{T}}) where {D<:Sampleable{Matrixvariate},T<:Number} = length(x)

"""
Distribution{F<:VariateForm,S<:ValueSupport} <: Sampleable{F,S}
Distribution{F<:VariateForm,S<:Support} <: Sampleable{F,S}

`Distribution` is a `Sampleable` generating random values from a probability
distribution. Distributions define a Probability Distribution Function (PDF)
to implement with `pdf` and a Cumulated Distribution Function (CDF) to implement
with `cdf`.
"""
abstract type Distribution{F<:VariateForm,S<:ValueSupport} <: Sampleable{F,S} end
abstract type Distribution{F<:VariateForm,S<:Support} <: Sampleable{F,S} end

const UnivariateDistribution{S<:ValueSupport} = Distribution{Univariate,S}
const MultivariateDistribution{S<:ValueSupport} = Distribution{Multivariate,S}
const MatrixDistribution{S<:ValueSupport} = Distribution{Matrixvariate,S}
const NonMatrixDistribution = Union{UnivariateDistribution, MultivariateDistribution}
const UnivariateDistribution{S<:Support} = Distribution{Univariate,S}
const MultivariateDistribution{S<:Support} = Distribution{Multivariate,S}
const MatrixDistribution{S<:Support} = Distribution{Matrixvariate,S}

const DiscreteDistribution{F<:VariateForm} = Distribution{F,Discrete}
const CountableDistribution{F<:VariateForm,
C<:CountableSupport} = Distribution{F,C}
const DiscreteDistribution{F<:VariateForm} = CountableDistribution{F,Discrete}
const ContinuousDistribution{F<:VariateForm} = Distribution{F,Continuous}

const DiscreteUnivariateDistribution = Distribution{Univariate, Discrete}
const ContinuousUnivariateDistribution = Distribution{Univariate, Continuous}
const DiscreteMultivariateDistribution = Distribution{Multivariate, Discrete}
const ContinuousMultivariateDistribution = Distribution{Multivariate, Continuous}
const DiscreteMatrixDistribution = Distribution{Matrixvariate, Discrete}
const ContinuousMatrixDistribution = Distribution{Matrixvariate, Continuous}
const CountableUnivariateDistribution{C<:CountableSupport} =
UnivariateDistribution{C}
const DiscreteUnivariateDistribution =
CountableUnivariateDistribution{Discrete}
const ContinuousUnivariateDistribution = UnivariateDistribution{Continuous}

const CountableMultivariateDistribution{C<:CountableSupport} =
MultivariateDistribution{C}
const DiscreteMultivariateDistribution =
CountableMultivariateDistribution{Discrete}
const ContinuousMultivariateDistribution = MultivariateDistribution{Continuous}

const CountableMatrixDistribution{C<:CountableSupport} = MatrixDistribution{C}
const DiscreteMatrixDistribution = CountableMatrixDistribution{Discrete}
const ContinuousMatrixDistribution = MatrixDistribution{Continuous}

variate_form(::Type{Distribution{VF,VS}}) where {VF<:VariateForm,VS<:ValueSupport} = VF
variate_form(::Type{T}) where {T<:Distribution} = variate_form(supertype(T))

value_support(::Type{Distribution{VF,VS}}) where {VF<:VariateForm,VS<:ValueSupport} = VS
value_support(::Type{T}) where {T<:Distribution} = value_support(supertype(T))
variate_form(::Type{<:Sampleable{VF, <:Support}}) where {VF<:VariateForm} = VF
value_support(::Type{<:Sampleable{<:VariateForm,VS}}) where {VS<:Support} = VS

# allow broadcasting over distribution objects
# to be decided: how to handle multivariate/matrixvariate distributions?
Expand Down
12 changes: 10 additions & 2 deletions src/functionals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@ end

## Assuming that discrete distributions only take integer values.
function expectation(distr::DiscreteUnivariateDistribution, g::Function, epsilon::Real)
f = x->pdf(distr,x)
(leftEnd, rightEnd) = getEndpoints(distr, epsilon)
sum(x -> f(x)*g(x), leftEnd:rightEnd)
sum(leftEnd:rightEnd) do x
pdf(distr,x) * g(x)
end
end

function expectation(distr::CountableUnivariateDistribution,
g::Function, epsilon::Real)
sum(support(distr)) do x
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this make sense, this is a loop over typically 2ˆ64 elements?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a slight editing of the original code:

    f = x->pdf(distr,x)
    (leftEnd, rightEnd) = getEndpoints(distr, epsilon)
    sum(x -> f(x)*g(x), leftEnd:rightEnd)

I don't have a strong opinion about whether it's sensible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are dropping the epsilon

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. That's because this is for non-ContiguousSupport types - they are not necessarily ordered, so we can't use getEndpoints(). At the moment, the only type that falls into this category is DiscreteNonParametric, but it will also include Dirac, neither of which are expected to have anything like 2^64 elements..

pdf(distr,x) * g(x)
end
end

function expectation(distr::UnivariateDistribution, g::Function)
Expand Down
16 changes: 8 additions & 8 deletions src/mixtures/mixturemodel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@

- probs(d): return a vector of prior probabilities over components.
"""
abstract type AbstractMixtureModel{VF<:VariateForm,VS<:ValueSupport,C<:Distribution} <: Distribution{VF, VS} end
abstract type AbstractMixtureModel{VF<:VariateForm,VS<:Support,C<:Distribution} <: Distribution{VF, VS} end

"""
MixtureModel{VF<:VariateForm,VS<:ValueSupport,C<:Distribution,CT<:Real}
MixtureModel{VF<:VariateForm,VS<:Support,C<:Distribution,CT<:Real}
A mixture of distributions, parametrized on:
* `VF,VS` variate and support
* `C` distribution family of the mixture
* `CT` the type for probabilities of the prior
"""
struct MixtureModel{VF<:VariateForm,VS<:ValueSupport,C<:Distribution,CT<:Real} <: AbstractMixtureModel{VF,VS,C}
struct MixtureModel{VF<:VariateForm,VS<:Support,C<:Distribution,CT<:Real} <: AbstractMixtureModel{VF,VS,C}
components::Vector{C}
prior::Categorical{CT}

Expand All @@ -30,9 +30,9 @@ struct MixtureModel{VF<:VariateForm,VS<:ValueSupport,C<:Distribution,CT<:Real} <
end
end

const UnivariateMixture{S<:ValueSupport, C<:Distribution} = AbstractMixtureModel{Univariate,S,C}
const MultivariateMixture{S<:ValueSupport, C<:Distribution} = AbstractMixtureModel{Multivariate,S,C}
const MatrixvariateMixture{S<:ValueSupport,C<:Distribution} = AbstractMixtureModel{Matrixvariate,S,C}
const UnivariateMixture{S<:Support, C<:Distribution} = AbstractMixtureModel{Univariate,S,C}
const MultivariateMixture{S<:Support, C<:Distribution} = AbstractMixtureModel{Multivariate,S,C}
const MatrixvariateMixture{S<:Support,C<:Distribution} = AbstractMixtureModel{Matrixvariate,S,C}

# Interface

Expand Down Expand Up @@ -125,7 +125,7 @@ the components given by ``params``, and a prior probability vector.
If no `prior` is provided then all components will have the same prior probabilities.
"""
function MixtureModel(::Type{C}, params::AbstractArray) where C<:Distribution
components = C[_construct_component(C, a) for a in params]
components = [_construct_component(C, a) for a in params]
richardreeve marked this conversation as resolved.
Show resolved Hide resolved
MixtureModel(components)
end

Expand All @@ -142,7 +142,7 @@ _construct_component(::Type{C}, arg) where {C<:Distribution} = C(arg)
_construct_component(::Type{C}, args::Tuple) where {C<:Distribution} = C(args...)

function MixtureModel(::Type{C}, params::AbstractArray, p::Vector{T}) where {C<:Distribution,T<:Real}
components = C[_construct_component(C, a) for a in params]
components = [_construct_component(C, a) for a in params]
MixtureModel(components, p)
end

Expand Down
2 changes: 1 addition & 1 deletion src/multivariate/dirichlet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Dirichlet(alpha) # Dirichlet distribution with parameter vector alpha
Dirichlet(k, a) # Dirichlet distribution with parameter a * ones(k)
```
"""
struct Dirichlet{T<:Real} <: ContinuousMultivariateDistribution
struct Dirichlet{T<:Real} <: MultivariateDistribution{ContinuousSupport{T}}
alpha::Vector{T}
alpha0::T
lmnB::T
Expand Down
4 changes: 2 additions & 2 deletions src/multivariate/mvlognormal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#
###########################################################

abstract type AbstractMvLogNormal <: ContinuousMultivariateDistribution end
abstract type AbstractMvLogNormal{T} <: MultivariateDistribution{ContinuousSupport{T}} end

function insupport(::Type{D},x::AbstractVector{T}) where {T<:Real,D<:AbstractMvLogNormal}
for i=1:length(x)
Expand Down Expand Up @@ -161,7 +161,7 @@ Mean vector ``\\boldsymbol{\\mu}`` and covariance matrix ``\\boldsymbol{\\Sigma}
underlying normal distribution are known as the *location* and *scale*
parameters of the corresponding lognormal distribution.
"""
struct MvLogNormal{T<:Real,Cov<:AbstractPDMat,Mean<:AbstractVector} <: AbstractMvLogNormal
struct MvLogNormal{T<:Real,Cov<:AbstractPDMat,Mean<:AbstractVector} <: AbstractMvLogNormal{T}
normal::MvNormal{T,Cov,Mean}
end

Expand Down
4 changes: 2 additions & 2 deletions src/multivariate/mvnormal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const ZeroMeanDiagNormal{Axes} = MvNormal{PDiagMat, Zeros{Float64,1,Axes}}
const ZeroMeanFullNormal{Axes} = MvNormal{PDMat, Zeros{Float64,1,Axes}}
```
"""
abstract type AbstractMvNormal <: ContinuousMultivariateDistribution end
abstract type AbstractMvNormal{T<:Real} <: MultivariateDistribution{ContinuousSupport{T}} end

### Generic methods (for all AbstractMvNormal subtypes)

Expand Down Expand Up @@ -169,7 +169,7 @@ isotropic covariance matrix corresponding `abs2(sig)*I`.
**Note:** The constructor will choose an appropriate covariance form internally, so that
special structure of the covariance can be exploited.
"""
struct MvNormal{T<:Real,Cov<:AbstractPDMat,Mean<:AbstractVector} <: AbstractMvNormal
struct MvNormal{T<:Real,Cov<:AbstractPDMat,Mean<:AbstractVector} <: AbstractMvNormal{T}
μ::Mean
Σ::Cov
end
Expand Down
3 changes: 2 additions & 1 deletion src/multivariate/mvnormalcanon.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ an isotropic precision matrix corresponding `J*I`.

**Note:** `MvNormalCanon` share the same set of methods as `MvNormal`.
"""
struct MvNormalCanon{T<:Real,P<:AbstractPDMat,V<:AbstractVector} <: AbstractMvNormal
struct MvNormalCanon{T<:Real,P<:AbstractPDMat,V<:AbstractVector} <: AbstractMvNormal{T}
μ::V # the mean vector
h::V # potential vector, i.e. inv(Σ) * μ
J::P # precision matrix, i.e. inv(Σ)
Expand Down Expand Up @@ -157,6 +157,7 @@ length(d::MvNormalCanon) = length(d.μ)
mean(d::MvNormalCanon) = convert(Vector{eltype(d.μ)}, d.μ)
params(d::MvNormalCanon) = (d.μ, d.h, d.J)
@inline partype(d::MvNormalCanon{T}) where {T<:Real} = T

Base.eltype(::Type{<:MvNormalCanon{T}}) where {T} = T

var(d::MvNormalCanon) = diag(inv(d.J))
Expand Down
5 changes: 3 additions & 2 deletions src/multivariate/mvtdist.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## Generic multivariate t-distribution class

abstract type AbstractMvTDist <: ContinuousMultivariateDistribution end
abstract type AbstractMvTDist{T} <: MultivariateDistribution{ContinuousSupport{T}} end

struct GenericMvTDist{T<:Real, Cov<:AbstractPDMat, Mean<:AbstractVector} <: AbstractMvTDist
struct GenericMvTDist{T<:Real, Cov<:AbstractPDMat, Mean<:AbstractVector} <: AbstractMvTDist{T}
df::T # non-integer degrees of freedom allowed
dim::Int
zeromean::Bool
Expand Down Expand Up @@ -103,6 +103,7 @@ logdet_cov(d::GenericMvTDist) = d.df>2 ? logdet((d.df/(d.df-2))*d.Σ) : NaN

params(d::GenericMvTDist) = (d.df, d.μ, d.Σ)
@inline partype(d::GenericMvTDist{T}) where {T} = T

Base.eltype(::Type{<:GenericMvTDist{T}}) where {T} = T

# For entropy calculations see "Multivariate t Distributions and their Applications", S. Kotz & S. Nadarajah
Expand Down
4 changes: 2 additions & 2 deletions src/multivariate/product.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ Product(Uniform.(rand(10), 1)) # A 10-dimensional Product from 10 independent `U
```
"""
struct Product{
S<:ValueSupport,
S<:Support,
T<:UnivariateDistribution{S},
V<:AbstractVector{T},
} <: MultivariateDistribution{S}
v::V
function Product(v::V) where
V<:AbstractVector{T} where
T<:UnivariateDistribution{S} where
S<:ValueSupport
S<:Support
return new{S, T, V}(v)
end
end
Expand Down
Loading