Skip to content

Public Documentation

Documentation for ReparameterisedDistributions's public interface.

ReparameterisedDistributions.ReparameterisedDistributions Module
julia
ReparameterisedDistributions

Parameter-convention switches for Distributions.jl: wrap a distribution so that it is parameterised by the quantities a modeller reasons about — its moments — rather than by its native parameters.

Distributions.jl parameterises each family by its native parameters: a Gamma by shape and scale, a LogNormal by the mean and standard deviation of its logarithm. A delay distribution, though, is elicited as a mean and a standard deviation, and a prior belongs on the mean. Such a prior cannot be expressed through a native leaf, because independent priors on shape and scale do not compose into a prior on the mean.

reparameterise returns a Distribution whose parameters are the moments. It evaluates and samples exactly as the native distribution does, so it can be used directly on the left of a ~; it converts to the native family through an exact closed form; and it stays differentiable, so the moments can be sampled. native reaches the native distribution — and, through it, the native parameters — when the moments alone are not enough. rescale scales a registered moment while holding the others fixed, routing through the same closed form.

Examples

julia
using ReparameterisedDistributions, Distributions

d = reparameterise(LogNormal; mean = 8.0, sd = 2.0)
(params(d), mean(d), std(d))
source

Contents

Index

Public API

ReparameterisedDistributions.AbstractReparameterisedDistribution Type
julia
abstract type AbstractReparameterisedDistribution{F<:Distributions.VariateForm, S<:Distributions.ValueSupport} <: Distributions.Distribution{F<:Distributions.VariateForm, S<:Distributions.ValueSupport}

Supertype for a distribution that stands in for a native family under a different parameterisation.

A subtype stores the alternative parameters and converts to the native family on demand, so it evaluates exactly as the native distribution does while reporting the alternative parameters as its own. The variate form and value support are carried as type parameters and taken from the family being wrapped, so a wrapper around a discrete family stays discrete.

See also


Fields

source
ReparameterisedDistributions.Reparameterised Type
julia
struct Reparameterised{D, names, N, T<:Real, F<:Distributions.VariateForm, S<:Distributions.ValueSupport} <: ReparameterisedDistributions.AbstractReparameterisedDistribution{F<:Distributions.VariateForm, S<:Distributions.ValueSupport}

A native distribution family stood up under an alternative parameterisation.

Stores the alternative parameter values in the order of the registered names, and converts to the native family through to_native whenever a density, moment or sample is asked for. params reports the alternative values, not the native ones, so it is those that the ecosystem's parameter introspection sees and that a prior can be placed on.

The family D, the parameter names and the variate form and value support are type parameters, so a wrapper around a discrete family stays discrete and the conversion is resolved at compile time.

Fields

  • vals: the alternative parameter values, in registered names order.

See also


Fields

  • vals::NTuple{N, T} where {N, T<:Real}: The alternative parameter values, in registered names order.
source
ReparameterisedDistributions.native Function
julia
native(
    d::ReparameterisedDistributions.Reparameterised{D, names}
) -> Any

The native distribution a wrapper's alternative parameters convert to.

Every density, moment and sampling method on a Reparameterised goes through this, so it is also the way to reach the native parameters — the ones the wrapped family was actually built from — when the moments alone are not enough: params(native(d)) rather than params(d). Throws a DomainError if d's parameters are invalid — see valid_moments.

Examples

julia
using ReparameterisedDistributions, Distributions

d = reparameterise(LogNormal; mean = 8.0, sd = 2.0)
native(d), params(native(d))

See also

source
ReparameterisedDistributions.reparameterise Function
julia
reparameterise(
    d::Type{D<:Distributions.Distribution};
    kwargs...
)

Wrap dist_or_type so that the keyword parameters given here are its parameters.

The keywords name an alternative parameterisation of the family, and the wrapper converts to the native family internally through an exact closed form. The result is a Distribution, so it evaluates and samples exactly as the native distribution does and can be used directly on the left of a ~ in a probabilistic model.

The point of the wrapper is that the alternative parameters remain the parameters: params reports them, and the ecosystem's parameter introspection places a prior on them, rather than on the native parameters that merely imply them. A prior on a delay's mean cannot be expressed through a native Gamma(shape, scale) leaf, because independent priors on shape and scale do not compose into a prior on the mean.

Pass either the family (LogNormal) or an instance of it, whose parameter values are ignored — only its family is taken. The keywords are order-insensitive, as keywords are everywhere else.

Arguments

  • dist_or_type: the native family to wrap, as a type or an instance.

  • check_args: whether to reject invalid parameters at construction. Left on by default. A sampler exploring an unconstrained parameter turns it off: an invalid proposal then gives logpdf == -Inf (and pdf == 0) rather than an exception raised in the middle of a gradient. Every other method still converts, so an invalid distribution has no mean, no quantile and no draw, and asking for one raises.

  • alt_params: the alternative parameters, as keywords.

Note

params reports the moments, so the usual typeof(d)(params(d)...) idiom does not rebuild one of these — the family and the parameter names live in type parameters. Rebuild through reparameterise instead. Generic code relying on that idiom will raise rather than silently misbehave. For the native parameters — the ones the wrapped family was actually built from — use params(native(d)).

Examples

julia
using ReparameterisedDistributions, Distributions

d = reparameterise(LogNormal; mean = 8.0, sd = 2.0)
params(d)
julia
using ReparameterisedDistributions, Distributions

mean(reparameterise(LogNormal; mean = 8.0, sd = 2.0))
julia
using ReparameterisedDistributions, Distributions

d = reparameterise(LogNormal; mean = 8.0, sd = 2.0)
params(native(d))

See also

  • native: the native distribution a wrapper converts to.
source
ReparameterisedDistributions.rescale Function
julia
rescale(
    d::ReparameterisedDistributions.Reparameterised{D, names},
    factor::Real;
    parameter,
    check_args
) -> ReparameterisedDistributions.Reparameterised

Scale d's named parameter by factor, holding the others fixed.

Routes through whichever moment parameterisation d was itself built under — the registered names its type already carries — so scaling a mean (or any other registered parameter) has one home rather than a caller hand-rebuilding the wrapper from params(d). An affine transform is not a substitute: for a discrete family such as NegativeBinomial, scaling the native support does not scale the mean cleanly, so the scaling has to happen in moment coordinates and convert back through the family's own closed form.

parameter must be one of d's registered names, or this throws a DomainError rather than silently applying the factor under different semantics. d must itself be a Reparameterised distribution — a native, unwrapped Distribution has no registered parameterisation to route through and is rejected with an ArgumentError naming the family to wrap first.

Arguments

  • d: the distribution to rescale.

  • factor: the multiplicative factor applied to parameter.

  • parameter: the registered name to scale. Defaults to :mean.

  • check_args: forwarded to the rebuilt wrapper; see reparameterise.

Examples

julia
using ReparameterisedDistributions, Distributions

d = reparameterise(Gamma; mean = 8.0, shape = 2.0)
mean(rescale(d, 2.0))

See also

source
ReparameterisedDistributions.solve_moment Function
julia
solve_moment(
    _::Type{D},
    _::Val{names},
    residual,
    deriv,
    bracket,
    vals
) -> Any

Solve a family's own moment equation for a scalar s, exactly and differentiably, regardless of which solver backend runs the root-find.

A numeric family calls this inside its own to_native method, passing its own residual, deriv and bracket as ordinary functions — this registers no method on anything of ours, so a downstream package supplies its own three functions exactly as this package's own Weibull registration does (see src/families.jl).

  • residual(s, vals) is the moment equation, zero at the solution.

  • deriv(s, vals) is its derivative with respect to s.

  • bracket(pvals) -> (lo, hi) is a sign-changing interval for residual, evaluated on vals stripped to its primal type.

The root-find itself runs on the primal-stripped vals, then two steps of an implicit-function-theorem correction recover the exact derivative in the caller's own type afterwards: s is a root of residual, so subtracting residual / deriv leaves the VALUE unchanged to machine precision while making the DERIVATIVE exactly -residual_vals / deriv, whatever derivative the solver's own s arrived with (a garbage one, a zero one, or none at all). Two steps, not one: measured against the Weibull (mean, sd) equation, one correction step gives a correct gradient but a Hessian wrong by 2% on the diagonal and 21% off-diagonal.

Arguments

  • the native family being converted to.

  • Val(names): the alternative parameter names.

  • residual, deriv, bracket: the family's own moment equation.

  • vals: the alternative parameter values, in names order.

Examples

The to_native method that calls this must be registered under the parameter names sorted alphabetically, as reparameterise canonicalises its keywords that way before dispatching.

solve_moment is public but not exported, so import it by name.

julia
using ReparameterisedDistributions, Distributions
using ReparameterisedDistributions: solve_moment

# A toy equation, exp(s) = 2, solved for illustration; a real family's
# residual is its own moment equation.
solve_moment(Gamma, Val((:shape,)), (s, vals) -> exp(s) - vals[1],
    (s, vals) -> exp(s), pvals -> (-10.0, 10.0), (2.0,))

See also

  • to_native: the conversion a numeric family calls this from.
source
ReparameterisedDistributions.test_reparameterisation Function
julia
test_reparameterisation(::Type{D}, names, vals; kwargs...)

Check a registered (family, parameter-name) pair against the whole registration contract.

Runs one @testset covering everything a registration has to get right: that it is registered at all and under the canonical (alphabetically sorted) parameter names; that valid_moments answers Bool and answers true at the point given; that to_native is inferred to a concrete distribution of the family and never admits nothing; that the wrapper builds, reports the given parameters as its own, keeps the family's variate form and value support, and evaluates and samples as the native distribution does; that any parameter named mean, sd or var comes back out of the built distribution; and that each invalid point given is rejected at construction and yields -Inf without it.

Written for a family registered from another package as much as for this one's own: it imports nothing private, so a downstream registration can be held to the same contract by the same code.

Test supplies this through a package extension, so a test suite reaches it with using Test. Calling it without Test loaded raises.

Arguments

  • the native family the registration converts to.

  • names: the alternative parameter names, as a tuple of Symbols, in the order they are registered under.

  • vals: valid alternative parameter values, in names order.

  • invalid: alternative parameter tuples that must be rejected. Empty by default, but passing at least one is what checks that the family's guard actually guards.

  • rtol: the relative tolerance the mean/sd/var round-trip is held to. Loose enough by default for a numeric conversion.

Examples

julia
using ReparameterisedDistributions, Distributions, Test
using ReparameterisedDistributions: test_reparameterisation

test_reparameterisation(Gamma, (:mean, :sd), (8.0, 3.0);
    invalid = ((8.0, -1.0), (-8.0, 1.0)))

See also

source
ReparameterisedDistributions.to_native Function
julia
to_native(
    _::Type{D},
    _::Val{names},
    vals
) -> Distributions.Gamma

The closed-form conversion from a family's alternative parameters to the native distribution.

Each supported (family, parameter-name) pair adds a method. A method should be exact algebra where a closed form exists, and otherwise call solve_moment. It must build the native distribution with check_args = false, so the conversion stays differentiable and a sampler probing an invalid point yields -Inf rather than throwing mid-gradient.

A method does NOT need to guard its own input: valid_moments is checked at every call site first, so this always runs on parameters already known valid and always returns a concrete D, never a Union of one and nothing. Keep it that way — do not add an early return nothing here. That split, not a stylistic preference, is why this stays two methods rather than one: an AD-hot call site that bound a Union{Nothing, D}-typed conversion result has produced a silently wrong reverse-mode gradient in this package before, and a call site here can only avoid that failure mode if every registered to_native method is unconditionally concrete.

Calling this directly, rather than through native or a wrapper's own methods, without first checking valid_moments is undefined: it may return a meaningless distribution or throw, never nothing.

Arguments

  • the native family being converted to.

  • Val(names): the alternative parameter names, as a value type so the conversion is resolved at compile time.

  • vals: the alternative parameter values, in names order.

Examples

julia
using ReparameterisedDistributions, Distributions

to_native(LogNormal, Val((:mean, :sd)), (8.0, 2.0))

See also

  • reparameterise: the public constructor that dispatches to this.

  • native: the wrapper-level accessor most callers want instead.

  • valid_moments: the guard checked before this runs.

source
ReparameterisedDistributions.valid_moments Function
julia
valid_moments(_::Type{D}, _::Val{names}, vals) -> Any

Whether a family's alternative parameters describe a member of the family, answered without converting or throwing.

Checked BEFORE to_native at every call site, hot path and construction alike — a family's own to_native method assumes its input has already passed this check, and is free to build whatever it builds without guarding, so it always returns a concrete distribution rather than a Union of one and nothing. Keeping that return type concrete is not cosmetic: an AD-hot call site that instead bound a Union{Nothing, <native type>}-typed conversion result has produced a silently wrong reverse-mode gradient in this package before, with no error and no warning; a family's own math never has to defend against that on its own so long as it registers a truthful predicate here.

nothing cannot be recovered from the native distribution's own type: some conversions are even in the invalid direction (the LogNormal and Gamma conversions square the standard deviation, so a negative one builds exactly the same, perfectly valid native distribution as its positive counterpart), so the check has to happen in the alternative parameters' own coordinates, in a method registered here, before to_native runs.

Each supported (family, parameter-name) pair adds a method alongside its to_native registration. The 3-arg fallback accepts anything, so a family that registers to_native without a matching valid_moments method is silently treated as always valid: on the check_args = false hot path nothing surfaces the omission, and logpdf/pdf/loglikelihood return a finite, wrong density instead of -Inf at an invalid point. Register both methods together.

Arguments

  • the native family being checked for.

  • Val(names): the alternative parameter names, as a value type so the check is resolved at compile time.

  • vals: the alternative parameter values, in names order.

Returns

Bool. Must not throw, and must also exclude points to_native cannot represent even though the moment itself looks fine, such as a numeric family's solvable window.

Examples

julia
using ReparameterisedDistributions, Distributions

valid_moments(LogNormal, Val((:mean, :sd)), (8.0, 2.0))
julia
using ReparameterisedDistributions, Distributions

valid_moments(LogNormal, Val((:mean, :sd)), (8.0, -1.0))

See also

  • to_native: the conversion this guards, registered alongside it.
source