ExponentialFamilyManifolds

ExponentialFamilyManifolds.jl provides implementations of manifolds for the natural parameters of exponential family distributions, using Manifolds.jl. These manifolds are compatible with ManifoldsBase.jl, enabling optimization of the natural parameters of exponential family distributions using Manopt.jl.

The primary operation in the package is the get_natural_manifold function, which returns the appropriate manifold for the natural parameters of a given exponential family member type, its dimension and (if required) conditioner.

ExponentialFamilyManifolds.get_natural_manifoldFunction
get_natural_manifold(::Type{T}, dims, conditioner = nothing)

The function returns a corresponding manifold for the natural parameters of distribution of type T. Optionally accepts the conditioner, which is set to nothing by default. Use empty tuple () for univariate distributions.

julia> using ExponentialFamily, ExponentialFamilyManifolds

julia> ExponentialFamilyManifolds.get_natural_manifold(Beta, ()) isa ExponentialFamilyManifolds.NaturalParametersManifold
true

julia> ExponentialFamilyManifolds.get_natural_manifold(MvNormalMeanCovariance, (3, )) isa ExponentialFamilyManifolds.NaturalParametersManifold
true
source

The get_natural_manifold function returns NaturalParametersManifold manifold, which is a wrapper around the actual manifold (the base) for the natural parameters that stores extra useful properties and provides the necessary operations for optimization with Manopt.jl.

using ExponentialFamilyManifolds, ExponentialFamily

ExponentialFamilyManifolds.get_natural_manifold(Beta, (), nothing)
ExponentialFamilyManifolds.NaturalParametersManifold{ℝ, Distributions.Beta, Tuple{}, ManifoldsBase.ProductManifold{ℝ, Tuple{ExponentialFamilyManifolds.ShiftedPositiveNumbers{Static.StaticInt{-1}}, ExponentialFamilyManifolds.ShiftedPositiveNumbers{Static.StaticInt{-1}}}}, Nothing}((), ProductManifold(ShiftedPositiveNumbers(static(-1)), ShiftedPositiveNumbers(static(-1))), nothing)
ExponentialFamilyManifolds.NaturalParametersManifoldType
NaturalParametersManifold(::Type{T}, dims, base, conditioner)

The manifold for the natural parameters of the distribution of type T with dimensions dims. An internal structure, use get_natural_manifold to create an instance of a manifold for the natural parameters of distribution of type T.

source

Its not advised to use the NaturalParametersManifold to create a manifold, but instead use the get_natural_manifold function.


Natural manifold base

The get_natural_manifold_base function returns the base manifold without the wrapper.

ExponentialFamilyManifolds.get_natural_manifold_base(Beta, (), nothing)
ProductManifold with 2 submanifolds:
 ShiftedPositiveNumbers(static(-1))
 ShiftedPositiveNumbers(static(-1))

The base manifold, however, does not encode the information about the conditioner, hence, it cannot be used for all exponential members. Additionally, it does not encode the type of the underlying exponential family members. For instance, the LogNormal and the univariate Normal distribution share the same base manifold, yet they represent different members of the exponential family of distributions.

ExponentialFamilyManifolds.get_natural_manifold_baseFunction
get_natural_manifold_base(M::NaturalParametersManifold)
get_natural_manifold_base(::Type{T}, dims, conditioner = nothing)

Returns base manifold for the distribution of type T of dimension dims. Optionally accepts the conditioner, which is set to nothing by default.

source
get_natural_manifold_base(::Type{Bernoulli}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Bernoulli distribution.

source
get_natural_manifold_base(::Type{Beta}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Beta distribution.

source
get_natural_manifold_base(::Type{Binomial}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Binomial distribution.

source
get_natural_manifold_base(::Type{Chisq}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Chisq distribution.

source
get_natural_manifold_base(::Type{Categorical}, dims::Tuple{Int}, conditioner=nothing)

Get the natural manifold base for the Categorical distribution.

source
get_natural_manifold_base(::Type{Dirichlet}, dims::Tuple{Int}, conditioner=nothing)

Get the natural manifold base for the Dirichlet distribution.

source
get_natural_manifold_base(::Type{Exponential}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Exponential distribution.

source
get_natural_manifold_base(::Type{Gamma}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Gamma distribution.

source
get_natural_manifold_base(::Type{ExponentialFamily.GammaInverse}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the ExponentialFamily.GammaInverse distribution.

source
get_natural_manifold_base(::Type{Geometric}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Geometric distribution.

source
get_natural_manifold_base(::Type{Laplace}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Laplace distribution.

source
get_natural_manifold_base(::Type{LogNormal}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the LogNormal distribution.

source
get_natural_manifold_base(::Type{NormalMeanVariance}, ::Tuple{}, conditioner = nothing)

Get the natural manifold base for the NormalMeanVariance distribution.

source
get_natural_manifold_base(::Type{MvNormalMeanCovariance}, dims::Tuple{Int}, conditioner = nothing)

Get the natural manifold base for the MvNormalMeanCovariance distribution.

source
get_natural_manifold_base(::Type{NegativeBinomial}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the NegativeBinomial distribution.

source
get_natural_manifold_base(::Type{Pareto}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Pareto distribution.

source
get_natural_manifold_base(::Type{Poisson}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Poisson distribution.

source
get_natural_manifold_base(::Type{Rayleigh}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Rayleigh distribution.

source
get_natural_manifold_base(::Type{Weibull}, ::Tuple{}, conditioner=nothing)

Get the natural manifold base for the Weibull distribution.

source
get_natural_manifold_base(::Type{WishartFast}, ::Tuple{Int}, conditioner=nothing)

Get the natural manifold base for the WishartFast distribution.

source

Product manifolds

Some base manifolds are known as Product Manifolds, which consist of several manifolds combined together. For example, the natural parameters of a multivariate Normal distribution form a product of a Euclidean vector manifold and a symmetric negative definite matrix manifold. The partition_point function takes a plain vector of natural parameters and (typically, but not always) returns a partitioned array for each submanifold in the form of an ArrayPartition from RecursiveArrayTools.jl.

M = ExponentialFamilyManifolds.get_natural_manifold(Beta, (), nothing)
p = ExponentialFamilyManifolds.partition_point(M, [ 1.0, 2.0 ])
([1.0], [2.0])
typeof(p)
RecursiveArrayTools.ArrayPartition{Float64, Tuple{SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}, SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}}}

The partitioned point functions as a regular vector but encodes the structure of the product manifold, allowing differentiation between the submanifolds within the vector.

ExponentialFamilyManifolds.ManifoldsBase.submanifold_component(p, 1)
1-element view(::Vector{Float64}, 1:1) with eltype Float64:
 1.0
ExponentialFamilyManifolds.ManifoldsBase.submanifold_component(p, 2)
1-element view(::Vector{Float64}, 2:2) with eltype Float64:
 2.0
ExponentialFamilyManifolds.partition_pointFunction
partition_point(M::NaturalParametersManifold, p)
partition_point(::Type{T}, dims, point, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold M of type T.

source
partition_point(::Type{Bernoulli}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Bernoulli.

source
partition_point(::Type{Beta}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Beta.

source
partition_point(::Type{Binomial}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Binomial.

source
partition_point(::Type{Chisq}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Chisq.

source
partition_point(::Type{Categorical}, dims::Tuple{Int}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Categorical.

source
partition_point(::Type{Dirichlet}, dims::Tuple{Int}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Dirichlet.

source
partition_point(::Type{Exponential}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Exponential.

source
partition_point(::Type{Gamma}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Gamma.

source
partition_point(::Type{ExponentialFamily.GammaInverse}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type ExponentialFamily.GammaInverse.

source
partition_point(::Type{Geometric}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Geometric.

source
partition_point(::Type{Laplace}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Laplace.

source
partition_point(::Type{LogNormal}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type LogNormal.

source
partition_point(::Type{NormalMeanVariance}, ::Tuple{}, p, conditioner = nothing)

Converts the point to a compatible representation for the natural manifold of type NormalMeanVariance.

source
partition_point(::Type{MvNormalMeanCovariance}, dims::Tuple{Int}, p, conditioner = nothing)

Converts the point to a compatible representation for the natural manifold of type MvNormalMeanCovariance.

source
partition_point(::Type{NegativeBinomial}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type NegativeBinomial.

source
partition_point(::Type{Pareto}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Pareto.

source
partition_point(::Type{Poisson}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Poisson.

source
partition_point(::Type{Rayleigh}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Laplace.

source
partition_point(::Type{Weibull}, ::Tuple{}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type Weibull.

source
partition_point(::Type{WishartFast}, ::Tuple{Int}, p, conditioner=nothing)

Converts the point to a compatible representation for the natural manifold of type WishartFast.

source

Custom generic manifolds

ExponentialFamilyManifolds.jl introduces additional manifolds not included in Manifolds.jl. This is crucial because certain exponential family distributions have natural parameters that require specific manifolds, such as negative definite matrices for the multivariate Gaussian distribution. These manifolds do not implement every operation defined in ManifoldsBase.jl, but they do provide the essential operations needed for optimization with Manopt.jl.

Optimization example

Suppose, we have a set of samples from a certain exponential family distribution and we want to estimate the natural parameters of the distribution using the Manopt.jl package.

using ExponentialFamily, Distributions, Plots, StableRNGs

rng  = StableRNG(42)
dist = Beta(24, 6)
data = rand(rng, dist, 200)

histogram(data, xlim = (0, 1), label = "data", normalize=:pdf)
Example block output
using Manopt, ForwardDiff, ExponentialFamilyManifolds

# cost function
function f(M, p)
    ef = convert(ExponentialFamilyDistribution, M, p)
    return -sum((d) -> logpdf(ef, d), data)
end

# gradient function
function g(M, p)
    return ForwardDiff.gradient((p) -> f(M, p), p)
end

M = ExponentialFamilyManifolds.get_natural_manifold(Beta, ())
p = rand(rng, M)
q = gradient_descent(M, f, g, p)

q_ef = convert(ExponentialFamilyDistribution, M, q)
q_η  = getnaturalparameters(q_ef)
([20.396819066270247], [4.357726910129818])

Note that we performed the optimization in the natural parameters space, we can use ExponentialFamily.jl API to convert the vector fo natural parameters to the corresponding mean parameter space:

map(NaturalParametersSpace() => MeanParametersSpace(), Beta, q_η)
2-element Vector{Float64}:
 21.396819066270247
  5.357726910129818

As we can see the result is quite close to the actual distribution, which was used to generate the test data:

params(MeanParametersSpace(), dist)
(24.0, 6.0)

Let's also check the result, by plotting the estimated distribution on top of the data.

histogram(data, xlim = (0, 1), label = "data", normalize=:pdf, fillalpha = 0.3)
plot!(0.0:0.01:1.0, (x) -> pdf(dist, x), label = "actual", fill = 0, fillalpha = 0.2)
plot!(0.0:0.01:1.0, (x) -> pdf(q_ef, x), label = "estimated", fill = 0, fillalpha = 0.5)
Example block output

The difference in KL is quite small as well:

kldivergence(convert(Distribution, q_ef), dist)
0.0035633924185827226

Helpers

ExponentialFamilyManifolds.NegatedType
Negated(m)

Lazily negates the matrix m, without creating a new matrix. Works by redefining the getindex.

julia> using ExponentialFamilyManifolds

julia> m = [1 2; 3 4]
2×2 Matrix{Int64}:
 1  2
 3  4

julia> N = ExponentialFamilyManifolds.Negated(m)
2×2 ExponentialFamilyManifolds.Negated{Int64, Matrix{Int64}}:
 -1  -2
 -3  -4

julia> N[1, 2]
-2
source

Index