Skip to content

Public Documentation

Documentation for ComposedDistributions's public interface.

ComposedDistributions.ComposedDistributions Module
julia
ComposedDistributions

The verb grammar for n-ary composition over any Distributions.jl UnivariateDistribution. Compose delays into chains (Sequential), independent branches (Parallel), fixed-probability or racing one_of outcomes (Resolve / Compete) and data-selected disjunctions (Choose); the compose front-end lowers a NamedTuple, a Tables.jl table, or a nested matrix to the same stack. Read the structure with params_table / event_names / event, build priors with build_priors, and edit the tree with update / prune / splice. Attach parameter uncertainty with uncertain (parameters that are themselves distributions, nestable):randdraws the marginal, and update collapses an uncertain leaf to its concrete template.

Hard-deps ConvolvedDistributions (a chain collapses to a convolved total via observed_distribution) and extends its convolve_series/difference generics for composed tree types; its own convolution/quadrature surface (convolved, integrate/gl_integrate, the solver-method types) is reached with a separate using ConvolvedDistributions, not re-exported here. No censoring: this is the generic composition layer.

Examples

julia
using ComposedDistributions, Distributions

# A two-step delay chain, then its parameter table.
tree = compose((onset_admit = [Gamma(2.0, 1.0), LogNormal(0.5, 0.4)],))
params_table(tree)
source

Contents

Index

Public API

ComposedDistributions.@events Macro

Declare an event-tree topology as a readable operator diagram.

@events lowers an operator diagram of event names to an EventSkeleton carrying structure only, no distributions. Fill the holes later with update(skeleton; name = dist, ...) to build the concrete composed tree, so one delay topology is reused across pathogens or settings.

The operators (parentheses group for precedence):

  • (\to, typed \to<tab>) chains events into a Sequential; a nested chain flattens into one sequential of all events.

  • | branches into a one_of outcome. Whether the node becomes a fixed-probability Resolve or a racing-hazard Compete is decided at fill time by the fill value type (see update), so | stays one syntax.

  • & runs branches in Parallel.

A bare identifier is an event name and becomes a named hole, the key the fill substitutes. A nested one_of or parallel group inside a chain is named deterministically from its branches (_or_ for one_of, _and_ for parallel, e.g. death | discharge names its enclosing step death_or_discharge); a fill names only the branch holes, never the group.

Arguments

  • body: the event diagram, either a bare expression or a begin ... end block holding exactly one diagram expression.

Examples

julia
using ComposedDistributions, Distributions

skeleton = @events begin
    onset  admission  (death | discharge)
end
tree = update(skeleton;
    onset = Gamma(2.0, 1.0),
    admission = LogNormal(0.5, 0.4),
    death = (Gamma(1.5, 1.0), 0.3),
    discharge = Gamma(2.0, 1.5))
event_names(tree)

See also

source
ComposedDistributions.@uncertain Macro

Read distribution-valued constructor arguments as parameter priors.

@uncertain expr rewrites expr so that a distribution literal passed as a positional argument to a distribution constructor becomes that parameter's prior, the natural spelling of the positional uncertain family form. It walks the whole expression, so it composes with compose, the composer verbs and the ModifiedDistributions wrappers.

Each call D(pos_args...) whose head D is a distribution type (a bare name beginning with an uppercase letter) and at least one of whose positional arguments is itself such a distribution literal is rewritten to uncertain(D, pos_args...). The runtime then sorts each positional argument: a UnivariateDistribution marks that parameter uncertain (a prior), a Real fixes it. So @uncertain LogNormal(Normal(0.0, 1.0), 0.5) is uncertain(LogNormal, Normal(0.0, 1.0), 0.5) (mu uncertain, sigma fixed at 0.5). A constructor with only literal arguments (LogNormal(0.5, 0.4)) has no distribution-valued argument and is left unchanged, and lowercase-headed calls (compose, affine, ...) are not themselves rewritten, though their arguments still are, so a modifier wraps the rewritten uncertain leaf.

A keyword-carrying constructor call and a qualified head (Base.Gamma) are left unrewritten; reach those through the explicit uncertain constructor.

Arguments

  • expr: an expression building a (possibly composed) distribution, with a distribution literal in a parameter slot standing for that parameter's prior.

Examples

julia
using ComposedDistributions, Distributions

# `shape` uncertain, `scale` fixed at 1.0.
@uncertain Gamma(Normal(0.7, 0.2), 1.0)

# A whole tree: only the Gamma leaf's shape is made uncertain.
@uncertain compose((
    onset = Gamma(LogNormal(log(2.0), 0.2), 1.0),
    admit = LogNormal(0.5, 0.4)))

See also

source
ComposedDistributions.AbstractComposedDistribution Type
julia
abstract type AbstractComposedDistribution{F<:Distributions.VariateForm, S<:Distributions.ValueSupport} <: Distributions.Distribution{F<:Distributions.VariateForm, S<:Distributions.ValueSupport}
julia
AbstractComposedDistribution{F<:VariateForm, S<:ValueSupport}

Supertype of the composer nodes that combine named child distributions into an event tree: the multivariate Sequential / Parallel / Choose and the univariate one_of family (AbstractOneOf: Resolve / Compete). Parametric on variate form so the one supertype spans both.

Required methods a concrete subtype implements (the node interface):

  • child_nleaves(c), child_logpdf(c, x, offset, n), child_rand!(out, offset, rng, c) — walk the flat event vector;

  • component_names(c) — the child names;

  • params(c) and params_table(c);

  • event_names(c) (flat) and event_tree(c) (nested);

  • Base.show(io, c).

Verify a subtype with ComposedDistributions.TestUtils.test_composed_interface.


Fields

source
ComposedDistributions.AbstractContext Type
julia
abstract type AbstractContext

Supertype of the covariate contexts a composed tree is resolved against.

A subtype carries the covariates a Varying leaf reads. Context is the concrete open-NamedTuple implementation; the abstract type is what the uncertain distributions work can extend with its own sampled-parameter context. instantiate dispatches on AbstractContext.

See also


Fields

source
ComposedDistributions.AbstractMultiChild Type
julia
abstract type AbstractMultiChild{S<:Distributions.ValueSupport} <: ComposedDistributions.AbstractComposedDistribution{Distributions.Multivariate, S<:Distributions.ValueSupport}
julia
AbstractMultiChild{S<:ValueSupport}

Supertype of the positional multi-child composers Sequential and Parallel (subtype of AbstractComposedDistribution{Multivariate, S}). These two store .components and carry their child names in a names type parameter (read with component_names), and are walked positionally by the tree machinery, so they share dispatch on ::AbstractMultiChild (the supertype the tree walkers key off). Choose (disjoint alternatives) is a sibling, not a multi-child node.


Fields

source
ComposedDistributions.AbstractOneOf Type
julia
abstract type AbstractOneOf <: ComposedDistributions.AbstractComposedDistribution{Distributions.Univariate, Distributions.Continuous}

Shared supertype of the one_of-outcome composers.

The two one_of-outcome nodes — the fixed-probability mixture Resolve (cause and timing independent) and the racing-hazard Compete (the winning probability derived from the hazards, timing coupled) — subtype AbstractOneOf. The tree walkers dispatch on it wherever the behaviour is shared (one event slot per outcome, the shared origin, the per-outcome rand) and on the concrete type only where the scoring arithmetic differs.

AbstractOneOf is the univariate arm of the composer hierarchy: it subtypes AbstractComposedDistribution{Univariate, Continuous}, so it stays a UnivariateDistribution while sharing the composed supertype the multivariate Sequential / Parallel / Choose also sit under.

Examples

julia
using ComposedDistributions, Distributions

r = resolve(:death => (Gamma(1.5, 1.0), 0.3), :disch => Gamma(2.0, 1.5))
r isa ComposedDistributions.AbstractOneOf

See also


Fields

source
ComposedDistributions.CentredPoolPrior Type
julia
struct CentredPoolPrior{P<:Pool}

The centred latent's prior marker, carried on the prior column of a centred pooled parameter's row.

It is not a fixed distribution (the population depends on the estimated hyperparameters), so DistributionsInference.jl's distribution_to_logdensity/logdensity scores it separately (_pool_centred_logprior) and skips it in the fixed per-row prior sum. A non-nothing entry, so the row still counts as estimated.

Reached by qualified name from outside this package — DistributionsInference.jl's fit-protocol extension pattern-matches on this marker to translate a centred-pooled row's prior to nothing.

See also: _centred_pool_rows, _pool_centred_logprior, pool


Fields

  • pool::Pool
source
ComposedDistributions.Choose Type
julia
struct Choose{names, A<:Tuple} <: ComposedDistributions.AbstractComposedDistribution{Distributions.Multivariate, Distributions.Continuous}

A data-selected disjunction over independent named alternatives.

Choose holds named alternatives , each an independent sub-distribution, and a selector naming the data field that picks which alternative applies to a record. Exactly one alternative is active per record, chosen by the selector value, not by a branch probability and not off a shared origin. This is the disjunctive split that neither Parallel (shared origin, product over branches) nor Resolve (shared origin, probabilistic mixture) expresses: the alternatives are genuinely independent sub-models with different origins, and the data says which one generated the record.

Scoring and model dispatch route to the selected alternative: logpdf(d, x; kind) takes the chosen name as the kind keyword (no default — a Choose has no single distribution to score without a selection). Sampling has two forms: rand(d; kind) draws the named alternative directly, while a bare rand(d) (no kind, the forward-simulation path) samples an alternative uniformly and returns a self-describing record tagging which was drawn (the selector field set to its name plus the alternative's own draw), so logpdf(d, rand(d)) round-trips with no kind argument. The selection walk is type-stable: the selected alternative is found by a hand-rolled recursion over the name tuple that barriers into the chosen alternative's concrete type, so inference of the hot-path logpdf is preserved.

An alternative may itself be any distribution or a nested composer (Sequential, Parallel, Resolve, or another Choose), so a composed tree nests inside a data-selected split. A Choose may also nest the other way, as a child of a Sequential / Parallel / compose composer: the flat, data-free value path (logpdf/rand without a kind) commits to its first alternative, so the node's flat width is that alternative's leaf count; every alternative must share that leaf count for the nested Choose to occupy one fixed flat slot, or the parent's width query errors.

For prior introspection (params_table, build_priors, update) the alternatives' parameters are namespaced per alternative: independent per-branch params live under their alternative name (index.… / sourced.…), so each branch's parameters are inventoried and sampled separately. A parameter tied across alternatives via shared(:tag, ...) is keyed once by its tag and is inventoried once and sampled once, so the tied value is shared by every alternative that uses it.

Fields

  • the alternative names (Symbols) live in the names type parameter (read with component_names).

  • alternatives: tuple of the alternative distributions, one per name.

  • selector: the row field name (Symbol) whose value selects an alternative.

See also

  • choose: friendly constructor over name => dist pairs

  • Resolve: exactly one of several shared-origin outcomes (mixture)

  • Parallel: independent shared-origin branches (product)


Fields

  • alternatives::Tuple: Tuple of the alternative distributions, one per name.

  • selector::Symbol: The row field name (Symbol) whose value selects an alternative.

source
ComposedDistributions.Compete Type
julia
struct Compete{names, D<:Tuple} <: ComposedDistributions.AbstractOneOf

Resolve risks by racing hazards: the dual of convolved under minimum instead of sum.

Given cause-specific delay distributions D_1, ..., D_n, Compete represents the first-event time T = min_k D_k together with which cause won. The marginal any-event survival is ∏_k S_k(t) and density ∑_j f_j(t) ∏_{k≠j} S_k(t), so it nests as a univariate leaf. Observing a resolved (cause j, time t) scores f_j(t) ∏_{k≠j} S_k(t). The winning probability of each cause is derived from the hazards (P(cause = j) = ∫ f_j ∏_{k≠j} S_k), not a free parameter — this is the key difference from the fixed-probability mixture Resolve.

Build it with the compete constructor by giving bare delays (no branch probabilities): compete(:death => D1, :recover => D2).

Three views must agree: rand draws a latent time per cause and returns the winning cause's named event record; logpdf is the one_of-risks likelihood (marginal or cause-resolved); and the forward convolve_series stream is the per-outcome sub-density, sub-stochastic (not renormalised).Competeships against plainDistributions.ccdf/logccdf, so any stock univariate leaf races without a package-specific interface.

Fields

  • the one_of outcome names (Symbols) live in the names type parameter (read with component_names).

  • delays: tuple of the cause-specific delay distributions.

See also

  • compete: the constructor (bare delays; no branch probabilities).

  • Resolve: the fixed-probability mixture sibling.

  • Distributions.probs: the derived per-cause winning probabilities.

  • convolved: the sum dual (events in series).


Fields

  • delays::Tuple: Tuple of the cause-specific delay distributions.
source
ComposedDistributions.Context Type
julia
struct Context{NT<:NamedTuple} <: AbstractContext

The covariate context a Varying leaf is resolved against.

A Context is an open bag of covariates (a NamedTuple) — calendar time, a region/stratum, or (for the uncertain-distributions work) sampled parameter values. instantiate reads the covariate a leaf names from it. Build one with keyword covariates. It is open (rather than a fixed time field) so it can also carry the uncertain-distributions work's sampled parameters.

Examples

julia
using ComposedDistributions

ctx = Context(time = 4.0)
ctx.covariates.time

See also


Fields

  • covariates::NamedTuple: The covariates keyed by name (time, region, sampled params, ...).
source
ComposedDistributions.EventSkeleton Type
julia
struct EventSkeleton{S<:ComposedDistributions.AbstractEventSpec}

An event-tree topology: named events and their composition structure, with no distributions attached yet.

An EventSkeleton is built by @events from a readable operator diagram: (\to) chains events into a Sequential, | branches into a one_of outcome, & runs branches in Parallel, and parentheses group for precedence. A bare identifier is an event name and becomes a named hole. Fill the holes with update(skeleton; name = dist, ...) to build the concrete composed tree; whether a | node becomes a fixed-probability Resolve or a racing-hazard Compete is decided there by the fill value type.

A skeleton carries names and structure only, so one delay topology is reused across pathogens or settings by filling it with different distributions. It is independent of Distributions.jl families and of ModifiedDistributions: the fill value at each hole is any valid leaf.

Arguments

  • spec: the root structural spec node (a Hole, a -chain, a |-one_of group, or a &-parallel group).

Examples

julia
using ComposedDistributions, Distributions

skeleton = @events begin
    onset  admission  (death | discharge)
end
tree = update(skeleton;
    onset = Gamma(2.0, 1.0),
    admission = LogNormal(0.5, 0.4),
    death = (Gamma(1.5, 1.0), 0.3),
    discharge = Gamma(2.0, 1.5))
event_names(tree)

See also


Fields

  • spec::ComposedDistributions.AbstractEventSpec
source
ComposedDistributions.NoEvent Type
julia
struct NoEvent <: Distributions.Distribution{Distributions.Univariate, Distributions.Continuous}

Marker distribution for a no-event (absorbing) outcome of a resolve node: the outcome where nothing happens and no event time is written.

A none => (NoEvent(), q) branch carries no delay; its mass q is the probability that no event occurs. On rand a no-event win yields missing (no time recorded). On logpdf an observed non-occurrence (an explicit no event by the horizon record) scores the survival term log q (mixture) or the racing-hazard survival ∏ S_k; a latent non-occurrence (a record whose no-event slot is simply missing) contributes no one_of term.

NoEvent is a degenerate placeholder, not a sampling distribution: it has no support and errors if asked for a density or a draw. It exists only to mark the absorbing branch so the one_of node carries its mass q.

See also

  • resolve: the fixed-probability constructor.

  • Resolve: the mixture one_of node.


Fields

source
ComposedDistributions.Parallel Type
julia
struct Parallel{names, C<:Tuple} <: ComposedDistributions.AbstractMultiChild{Distributions.Continuous}

Independent branches composed from any univariate distributions.

Parallel places branch distributions off one origin, with the realisation the vector of branch values . A branch may itself be a Sequential, Parallel, Resolve, Compete or Choose composer, so trees nest recursively and the nesting is the tree.

logpdf is the sum of the per-branch log-densities,

The branches are independent here: this is the plain generic composition. The shared-origin coupling (where every branch shares one latent primary event) is a censored specialisation layered on top elsewhere, not part of this type.

Fields

  • components: tuple of the branch distributions (each univariate or a nested composer).

  • the branch names (Symbols), one per component, live in the names type parameter (read with component_names); the compose front-ends thread the user's names through, positional construction assigns :branch_1, :branch_2, ....

See also


Fields

  • components::Tuple: Tuple of the branch distributions (each univariate or a nested composer).
source
ComposedDistributions.Pool Type
julia
struct Pool{group, noncentred, P<:(Distributions.UnivariateDistribution)}

A partial-pooling spec: a parameter drawn, across a group of leaves, from one shared population distribution.

Pool marks a parameter (an entry of an uncertain leaf's specs) as partially pooled across the leaves that name the same group: every member's parameter is drawn from one common population distribution whose own free parameters are the estimated hyperparameters. It is the middle of the pooling spectrum — shared/tie is complete pooling (one value everywhere) and independent uncertain specs are no pooling (K unlinked values).

The population is an ordinary distribution — usually an uncertain one, so its free parameters carry their priors through the same machinery as any uncertain leaf. A location-scale population (Normal/LogNormal) is reparameterised non-centred (one Normal(0, 1) latent per member); a general population is scored centred (each member's parameter directly against the population).

Fields

  • the pooling-group name (Symbol) lives in the group type parameter (read with pool_group); leaves naming the same group are one population.

  • population: the population distribution (its free parameters are the hyperparameters).

  • whether the non-centred (location-scale) parameterisation is used (only for a Normal/LogNormal population) lives in the noncentred type parameter (read with pool_noncentred).

See also

  • pool: the public constructor.

  • shared/tie: complete pooling (the tied extreme).

  • uncertain: builds a population with hyperparameter priors.


Fields

  • population::Distributions.UnivariateDistribution: The population distribution; its free parameters are the hyperparameters.
source
ComposedDistributions.Resolve Type
julia
struct Resolve{names, D<:Tuple, P<:Tuple, S} <: ComposedDistributions.AbstractOneOf

Resolve outcomes composed from any univariate distributions: exactly one of several outcomes occurs, governed by branch probabilities summing to one.

Resolve names each one_of outcome, its delay distribution, and the branch probability of that outcome. It lowers to a Distributions.MixtureModel (see as_mixture) over the outcome delays weighted by the branch probabilities, so the realisation is a single time and the type is univariate. A death-versus-recovery competition makes the death branch probability the case-fatality ratio.

Being univariate, a Resolve nests as a child of Sequential or Parallel. This is the plain generic composition; per-record outcome selection and censoring are not part of this type.

The branch probabilities are ordinarily fixed structure. To estimate them, attach a simplex-valued Distributions.Dirichlet prior with update(node, (branch_probs = Dirichlet(α),)): the Dirichlet is what you write, but the codec estimates the node through the Dirichlet's K-1 stick-breaking coordinates (:stick_1 … :stick_{K-1}, each a Beta, so every draw lands on the simplex and the gradient is well-defined), and the probabilities are recovered from any draw (via update / Distributions.probs). See update for the full story.

Fields

  • the one_of outcome names (Symbols) live in the names type parameter (read with component_names).

  • delays: tuple of the one_of outcome delay distributions.

  • branch_probs: tuple of the branch probabilities, summing to one.

  • branch_prob_prior: the attached Dirichlet prior when the branch probabilities are uncertain, else nothing (fixed structure).

See also


Fields

  • delays::Tuple: Tuple of the one_of outcome delay distributions.

  • branch_probs::Tuple: Tuple of the branch probabilities, summing to one.

  • branch_prob_prior::Any: The attached simplex-valued prior over the branch probabilities (a Distributions.Dirichlet), or nothing when the probabilities are fixed structure. When present the branch probabilities are estimated through the stick-breaking codec: the user writes the Dirichlet, K-1 stick coordinates are what the sampler estimates, and the probabilities are recovered from any draw (see update).

source
ComposedDistributions.Sequential Type
julia
struct Sequential{names, C<:Tuple} <: ComposedDistributions.AbstractMultiChild{Distributions.Continuous}

A chain of independent steps composed from any univariate distributions.

Sequential links events     through independent step distributions . A realisation is the flat vector of step values (one value per step). A step may itself be a Sequential, Parallel, Resolve, Compete or Choose composer, in which case it contributes its own flat sub-vector, so chains nest recursively and the nesting is the tree.

logpdf sums the per-step log-densities over the matching slices of the value vector:

This is the plain generic composition; censoring and per-record marginalisation are not part of this type. Cumulative event times, if wanted, are the running sum of the step values.

Fields

  • components: tuple of the step distributions (each univariate or a nested composer).

  • the step names (Symbols), one per component, live in the names type parameter (read with component_names); the compose front-ends thread the user's names through, positional construction assigns :step_1, :step_2, ....

See also


Fields

  • components::Tuple: Tuple of the step distributions (each univariate or a nested composer).
source
ComposedDistributions.Shared Type
julia
struct Shared{tag, D<:(Distributions.UnivariateDistribution)} <: Distributions.UnivariateDistribution{Distributions.ValueSupport}

A name-tagged leaf tied across the branches of a composed distribution.

Shared wraps a leaf distribution with a tag (a Symbol) marking it as a shared parameter group. Two Shared leaves carrying the same tag are treated as the same free parameter by the prior/params interface: params_table lists the group's parameters once (deduped by tag), a downstream composed_parameters_model samples the group once and places the sampled values in every occurrence, and update updates all occurrences from one entry. The wrapper is transparent to scoring and sampling (every distribution method delegates to the wrapped leaf), so it only changes how parameters are inventoried, sampled and reconstructed.

Fields

  • the shared-parameter group name (Symbol) lives in the tag type parameter (read with shared_tag).

  • dist: the wrapped leaf distribution.

See also


Fields

  • dist::Distributions.UnivariateDistribution: The wrapped leaf distribution.
source
ComposedDistributions.Uncertain Type
julia
struct Uncertain{VS<:Distributions.ValueSupport, L<:Distributions.Distribution{Distributions.Univariate, VS<:Distributions.ValueSupport}, S<:NamedTuple} <: Distributions.Distribution{Distributions.Univariate, VS<:Distributions.ValueSupport}

A leaf distribution whose parameters are themselves distributions.

Uncertain pairs a concrete template leaf with specs, a NamedTuple mapping parameter names (as in params_table's param column) to distributions. A spec entry may itself be an Uncertain, so parameter uncertainty nests. Parameters without a spec stay fixed at the template's values, and the template's fixed wrapper structure (truncation, censoring) is carried through every draw via free_leaf/rewrap_leaf.

The generative model is hierarchical:

with fixed parameters taken from the template. rand draws the marginal (parameters drawn internally). The rest of the univariate surface (scalar logpdf/pdf/cdf/quantile, the moments) delegates to the template, so it reports the leaf at the template's central parameter values, not the marginal. Collapse an uncertain leaf to a concrete distribution by pinning its parameters with update(tree, params).

Only rand is marginal

Every other method — logpdf/cdf/quantile/... and the moments mean/var/std — silently reports the template's central values, not the marginal. Scoring or summarising a raw Uncertain leaf therefore answers "as if" its parameters were fixed at the template. Guard a scoring/fitting loop with has_uncertain, and collapse to concrete values first with update(tree, params).

Fields

  • template: the concrete (possibly wrapped) leaf supplying the family, the fixed parameter values, and the fixed wrapper structure.

  • specs: NamedTuple of the uncertain parameters, each value a distribution (possibly itself an Uncertain).

See also

  • uncertain: the public constructor.

  • update: collapse an uncertain leaf to a concrete distribution.


Fields

  • template::Distributions.UnivariateDistribution: The concrete (possibly wrapped) template leaf: family, fixed parameter values, and fixed wrapper structure (truncation / censoring).

  • specs::NamedTuple: NamedTuple of the uncertain parameters: each key a parameter name of the template's free delay, each value a distribution (possibly Uncertain).

source
ComposedDistributions.Varying Type
julia
struct Varying{F, D<:(Distributions.UnivariateDistribution)} <: Distributions.Distribution{Distributions.Univariate, Distributions.Continuous}

A context-indexed leaf: a delay whose distribution varies with a covariate.

Varying holds a map f from a covariate value to a UnivariateDistribution (e.g. t -> Gamma(shape(t), scale)), the covariate name it reads from a Context (default :time), and a reference distribution used whenever the leaf is queried without a context. Because Varying <: UnivariateDistribution it drops into Sequential / Parallel / compose as an ordinary leaf, and every Distributions method (logpdf, cdf, mean, rand, params, ...) delegates to the reference, so a tree with varying leaves still scores and samples at its reference by default.

instantiate(d, ctx) is the step that swaps the reference for f evaluated at the context's covariate: instantiate(leaf, Context(time = 4.0)) returns f(4.0). Resolve a whole tree at a context and then score / sample / convolve the concrete result.

Warning

Because the leaf delegates to reference, scoring or sampling a tree that still holds a Varying leaf does not error — it silently uses the reference (a wrong answer against real per-record covariates). Always instantiate first, and guard a fitting loop with has_varying.

The varying map f is fixed structure (like a truncation bound or a censoring window), so the introspection interface (params_table, update) treats the reference's parameters as the free parameters and peels/rewraps through the wrapper; the coefficients of f are not (yet) inventoried (see the design note's open questions).

Fields

  • f: map from a covariate value to a UnivariateDistribution.

  • covariate: the Context field name to read (Symbol, default :time).

  • reference: the distribution used when no context is supplied.

See also


Fields

  • f::Any: Map from a covariate value to a UnivariateDistribution.

  • covariate::Symbol: The Context field name this leaf reads (default :time).

  • reference::Distributions.UnivariateDistribution: The distribution used when no context is supplied.

source
ComposedDistributions._centred_pool_rows Function

Deprecated alias for centred_pool_rows; kept transitionally so a caller already qualifying it (ComposedDistributions._centred_pool_rows, or an explicit using ComposedDistributions: _centred_pool_rows) keeps working across the rename to a leading-underscore-free public name (the org's naming convention reserves a leading underscore for internal-only names). New code should call centred_pool_rows; this alias is removed in a future cleanup once DistributionsInference.jl's fit-protocol extension has moved off it.

Arguments

Examples

julia
using ComposedDistributions, Distributions

tree = compose((north = uncertain(Gamma(2.0, 1.0);
        shape = pool(:region, Beta(2.0, 3.0))),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:region, Beta(2.0, 3.0)))))
ComposedDistributions._centred_pool_rows(tree)

See also

source
ComposedDistributions._pool_centred_logprior Function

Deprecated alias for pool_centred_logprior; kept transitionally so a caller already qualifying it (ComposedDistributions._pool_centred_logprior, or an explicit using ComposedDistributions: _pool_centred_logprior) keeps working across the rename to a leading-underscore-free public name (the org's naming convention reserves a leading underscore for internal-only names). New code should call pool_centred_logprior; this alias is removed in a future cleanup once DistributionsInference.jl's fit-protocol extension has moved off it.

Arguments

  • rows: the (path, param, pool) triples from centred_pool_rows.

  • nt: the nested NamedTuple from unflatten at the same draw.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((north = uncertain(Gamma(2.0, 1.0);
        shape = pool(:region, Beta(2.0, 3.0))),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:region, Beta(2.0, 3.0)))))
rows = ComposedDistributions.centred_pool_rows(tree)
x = fill(0.5, ComposedDistributions.flat_dimension(tree))
nt = ComposedDistributions.unflatten(tree, x)
ComposedDistributions._pool_centred_logprior(rows, nt)

See also

source
ComposedDistributions._validate_pool_groups Function

Deprecated alias for validate_pool_groups; kept transitionally so a caller already qualifying it (ComposedDistributions._validate_pool_groups, or an explicit using ComposedDistributions: _validate_pool_groups) keeps working across the rename (this was declared public with the leading underscore, which the org's naming convention reserves for internal-only names). New code should call validate_pool_groups; this alias is removed in a future cleanup once DistributionsInference.jl's fit-protocol extension has moved off it.

Arguments

Examples

julia
using ComposedDistributions, Distributions

tree = compose((north = uncertain(Gamma(2.0, 1.0);
        shape = pool(:region, Beta(2.0, 3.0))),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:region, Beta(2.0, 3.0)))))
ComposedDistributions._validate_pool_groups(tree)

See also

source
ComposedDistributions._validate_tree_names Function

Deprecated alias for validate_tree_names; kept transitionally so a caller already qualifying it (ComposedDistributions._validate_tree_names, or an explicit using ComposedDistributions: _validate_tree_names) keeps working across the rename (this was declared public with the leading underscore, which the org's naming convention reserves for internal-only names). New code should call validate_tree_names; this alias is removed in a future cleanup once DistributionsInference.jl's fit-protocol extension has moved off it.

Arguments

Examples

julia
using ComposedDistributions, Distributions

tree = compose((north = uncertain(Gamma(2.0, 1.0);
        shape = pool(:region, Beta(2.0, 3.0))),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:region, Beta(2.0, 3.0)))))
ComposedDistributions._validate_tree_names(tree)

See also

source
ComposedDistributions.as_mixture Function
julia
as_mixture(c::Resolve) -> Distributions.MixtureModel

Lower a Resolve node to a Distributions.MixtureModel.

Returns the MixtureModel over the outcome delays weighted by the branch probabilities, the marginal time-to-resolution regardless of which outcome occurs.

Examples

julia
using ComposedDistributions, Distributions

node = Resolve(:death => (Gamma(1.5, 1.0), 0.3),
    :disch => (Gamma(2.0, 1.5), 0.7))
as_mixture(node)

See also

source
ComposedDistributions.build_priors Function
julia
build_priors(table; priors, default) -> NamedTuple

Assemble the nested prior NamedTuple from a params_table inventory.

build_priors(table; priors, default) turns the flat parameter table into the nested NamedTuple that a downstream composed_parameters_model (and update) expect, so users define priors against the flat table rows rather than by hand-matching the tree.

For each row the prior is chosen in order: 2. a user priors override for that (edge, param), if present, else

  1. the row's attached prior (an uncertain parameter's spec rides the table's prior column), if present, else

  2. default(row), the per-row default (support-derived default_prior unless a different default function is given).

By default every row gets a sensible support-derived prior, so build_priors(params_table(tree)) alone yields a complete prior NamedTuple. A user overrides only the parameters they care about (brms-style partial override) through priors.

row is a NamedTuple (; edge, param, value, support) (the table's columns for that row), so a custom default can pick a prior from the parameter's support.

Arguments

  • table: a params_table inventory (any Tables.jl column table with edge, param, value, support columns).

Keyword Arguments

  • priors: per-parameter overrides, either a (edge, param) => prior mapping (e.g. a Dict) or a nested NamedTuple keyed like the tree ((onset_admit = (shape = prior,),)); only the listed parameters are overridden (default: empty).

  • default: a function row -> prior for rows not overridden (default: default_prior, deriving the prior family from the parameter's support).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))
tbl = params_table(tree)
# Support-derived defaults everywhere, overriding only one parameter.
nested = build_priors(tbl;
    priors = (onset_admit = (shape = truncated(Normal(2, 0.5); lower = 0),),))
nested.onset_admit.shape

See also

  • params_table: the flat inventory keyed against.

  • default_prior: the support-derived per-row default.

  • composed_parameters_model (downstream), update: consume the result.

source
ComposedDistributions.centred_pool_rows Function
julia
centred_pool_rows(
    dist
) -> Vector{Tuple{Tuple, Symbol, Pool}}

The centred pooled parameters' (path, param, pool) triples, in table order.

Collected once per params_table walk (typically at distribution_to_logdensity construction time), so a tree with only non-centred (or no) pooling adds no per-evaluation cost. Reached by qualified name from outside this package — DistributionsInference.jl's fit-protocol extension calls this directly to find the rows pool_centred_logprior needs to score.

Arguments

  • the composed tree whose centred-pooled rows are collected.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((north = uncertain(Gamma(2.0, 1.0);
        shape = pool(:region, Beta(2.0, 3.0))),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:region, Beta(2.0, 3.0)))))
ComposedDistributions.centred_pool_rows(tree)

See also

source
ComposedDistributions.child_logpdf Function
julia
child_logpdf(node, x, offset, n)

A composer node's contribution to the joint log density, scoring its n-wide slice x[offset + 1 : offset + n] of the flat event vector. Part of the public composer-node extension contract, alongside child_nleaves and child_rand!; see Writing a new composer node. A univariate leaf scores the one scalar at its slot; a nested node recurses into its children, passing each its own offset.

Arguments

  • node: the composer node or leaf distribution to score.

  • x: the flat event vector being scored.

  • offset: the zero-based start index of this node's slice in x.

  • n: the slice width, child_nleaves(node).

Examples

julia
using ComposedDistributions, Distributions

node = compose((onset = Gamma(2.0, 1.0), report = Gamma(1.5, 1.0)))
n = ComposedDistributions.child_nleaves(node)
x = collect(values(rand(node)))
ComposedDistributions.child_logpdf(node, x, 0, n)

See also

source
ComposedDistributions.child_nleaves Function
julia
child_nleaves(node)

Number of flat event-vector slots a composer node occupies (one per leaf below it). Part of the public composer-node extension contract, alongside child_logpdf and child_rand!; see Writing a new composer node. A univariate leaf occupies one slot; a nested node occupies the sum of its children's widths.

Arguments

  • node: the composer node or leaf distribution whose flat slot width is read.

Examples

julia
using ComposedDistributions, Distributions

node = compose((onset = Gamma(2.0, 1.0), report = Gamma(1.5, 1.0)))
ComposedDistributions.child_nleaves(node)

See also

source
ComposedDistributions.child_rand! Function
julia
child_rand!(out, offset, rng, node)

Draw a composer node in place into its slice out[offset + 1 : offset + n] of the flat output vector, where n is child_nleaves(node). Returns nothing. Part of the public composer-node extension contract, alongside child_nleaves and child_logpdf; see Writing a new composer node. A univariate leaf writes its one slot; a nested node fills its slice by recursing into its children.

Arguments

  • out: the flat output vector to write into.

  • offset: the zero-based start index of this node's slice in out.

  • rng: the random number generator to draw from.

  • node: the composer node or leaf distribution to draw.

Examples

julia
using ComposedDistributions, Distributions, Random

node = compose((onset = Gamma(2.0, 1.0), report = Gamma(1.5, 1.0)))
out = zeros(ComposedDistributions.child_nleaves(node))
ComposedDistributions.child_rand!(out, 0, Random.default_rng(), node)
out

See also

source
ComposedDistributions.choose Function
julia
choose(alternatives::Pair...; selector) -> Choose

Build a Choose data-selected disjunction from name => dist alternatives.

Each alternative is name => dist: the alternative name (a Symbol) and its independent sub-distribution. The selector keyword names the data field a record carries to pick an alternative (default :kind). At least two alternatives are required and their names must be unique.

Arguments

  • alternatives: the name => dist pairs, each an independent sub-distribution (a UnivariateDistribution or a nested composer). A single named tuple (name = dist, …) is the equivalent positional spelling for hand-written alternatives, kept separate from the selector keyword; use Pairs for data-driven or computed names.

Keyword Arguments

  • selector: the row field name (Symbol) whose value picks an alternative (default :kind).

Examples

julia
using ComposedDistributions, Distributions

# An index case (a short delay) vs a sourced case (a longer coupled delay),
# selected by the row's `:kind` field.
d = choose(:index => Gamma(2.0, 1.0),
    :sourced => Gamma(4.0, 1.5))

# Score the alternative the data names.
logpdf(d, 3.0; kind = :index)
julia
using ComposedDistributions, Distributions

# The equivalent named tuple spelling; `selector` stays a keyword.
d = choose((index = Gamma(2.0, 1.0), sourced = Gamma(4.0, 1.5)); selector = :kind)
logpdf(d, 3.0; kind = :index)

See also

source
ComposedDistributions.compete Function
julia
compete(outcomes::Pair...) -> Compete

Build a racing-hazard Compete node from bare name => delay outcomes: the cause-specific delays race, the first wins, and the winning probability of each cause is derived from the hazards (cause coupled to timing).

Each outcome is name => delay (a bare delay, no branch probability). At least two outcomes are required. To give an explicit fixed probability per outcome (a mixture where cause is independent of timing) use resolve instead.

Arguments

  • outcomes: two or more bare name => delay pairs, each giving the outcome name (a Symbol) and its cause-specific delay distribution (no branch probability). A single named tuple (name = delay, …) is the equivalent positional spelling for hand-written outcomes; use Pairs for data-driven or computed names.

Examples

julia
using ComposedDistributions, Distributions

node = compete(:death => Gamma(2.0, 3.0), :recover => Gamma(3.0, 2.0))
probs(node)
julia
using ComposedDistributions, Distributions

# The equivalent named tuple spelling for hand-written outcomes.
node = compete((death = Gamma(2.0, 3.0), recover = Gamma(3.0, 2.0)))
probs(node)

See also

  • Compete: the composer type

  • resolve: the fixed-probability sibling constructor ((delay, prob))

  • Distributions.probs: the derived per-cause winning probabilities

  • compose: the front-end that nests the node as a branch

source
ComposedDistributions.component_names Function
julia
component_names(_::Sequential{names}) -> Any

The child names of a composed distribution.

Returns the tuple of names for a composer's direct children: the step names of a Sequential chain, the branch names of a Parallel set, or the outcome names of a Resolve node. These edge names key the parameter inventory, distinct from the flat event names of _flat_event_names.

Examples

julia
using ComposedDistributions, Distributions

oa = LogNormal(1.5, 0.4)
ad = Gamma(2.0, 1.0)
tree = compose((onset_admit = [oa, ad],))
ComposedDistributions.component_names(tree)

See also

  • event_names: the public edge-name accessor

  • _flat_event_names: the flat event names

source
ComposedDistributions.compose Function

Build a nested composer stack from a friendly front-end input.

compose lowers a NamedTuple, a Tables.jl table, or a nested matrix to the same Sequential / Parallel stack. It is a constructor over the composers, not a new tree type.

Arguments

  • input: the front-end to lower, one of the three forms below.

Inputs

  • NamedTuple (named, recursive): a Parallel over the named children. A child that is itself a NamedTuple nests as a Parallel, a child that is a Vector or Tuple of distributions nests as a Sequential, and a bare UnivariateDistribution is a leaf branch.

  • Tables.jl table with name and dist columns: a Parallel over the rows, the column-table equivalent of a flat NamedTuple. An optional chain column folds rows sharing a non-zero group id into a Sequential branch, and an optional compete/prob column pair folds rows sharing a non-zero compete id into a Resolve node whose prob entries are the branch probabilities (each in and summing to one per group).

  • nested Matrix of distributions: rows are Parallel branches and the columns within a row are Sequential steps. This orientation is canonical, so a one-column matrix is parallel leaf branches (one row each) and a one-row matrix is a Parallel-of-one wrapping a Sequential of the row's columns.

A varargs-pairs spelling, compose(:a => d1, :b => d2, ...), is also available as a convenience over the NamedTuple form (see the compose(pairs::Pair{Symbol}...) method below); the NamedTuple form above stays primary.

Contract

compose always returns a composer, never a bare univariate leaf. A single branch stays a Parallel-of-one and a single step a one-element Sequential; the wrapper is never collapsed away. A bare leaf is used directly at the scoring layer, where downstream helpers such as record_distributions and composed_distribution_model accept a bare UnivariateDistribution, so callers do not need compose to pass one through.

Examples

julia
using ComposedDistributions, Distributions

# A regular 2x2 grid built three ways, all equal.
nt = (r1 = [Gamma(2.0, 1.0), LogNormal(0.5, 0.4)],
    r2 = [Gamma(1.0, 1.0), Gamma(3.0, 1.0)])
table = (name = [:a, :b, :c, :d],
    dist = [Gamma(2.0, 1.0), LogNormal(0.5, 0.4),
        Gamma(1.0, 1.0), Gamma(3.0, 1.0)],
    chain = [1, 1, 2, 2])
mat = [Gamma(2.0, 1.0) LogNormal(0.5, 0.4); Gamma(1.0, 1.0) Gamma(3.0, 1.0)]
compose(nt) == compose(table) == compose(mat)

The three front-ends are chosen to build identical stacks for the same structure, as the example above shows.

See also

source
ComposedDistributions.default_prior Function
julia
default_prior(
    row
) -> Union{Distributions.Uniform{Float64}, Distributions.Normal, Distributions.Truncated{Distributions.Normal{T}, Distributions.Continuous, T1, _A, Nothing} where {T<:Real, T1<:Real, _A<:Union{Nothing, T1}}}

Pick a default prior for a parameter row, brms-style.

default_prior(row) is the per-row default build_priors uses for rows the user does not override. row is a (; edge, param, value, support) NamedTuple (a params_table row); the prior family follows the parameter's own natural domain (classified by name), not the leaf's variate support:

  • a probability parameter, support [0, 1] (a branch_probs row) -> Uniform(0, 1).

  • a scale/shape/rate-type parameter (:sigma, :scale, :shape, :rate, ...) -> truncated(Normal(value, scale); lower = 0), positive by construction even for a location-family delay (a Normal/Affine(Normal) sigma).

  • a location parameter (:mu, :location, a Uniform bound) -> Normal(value, scale), unconstrained since the location lives on the whole line even for a positive-support delay.

  • otherwise, an unmapped name falls back to the variate support: a non-negative support -> truncated(Normal(value, scale); lower = 0), else Normal(value, scale).

The spread scale defaults to max(abs(value), 1), a weakly-informative width that scales with the parameter's magnitude.

Arguments

Examples

julia
using ComposedDistributions, Distributions

# A positive scale parameter -> a positive-truncated default.
default_prior((; edge = :onset_admit, param = :scale,
    value = 1.0, support = (0.0, Inf)))

DistributionsInference's with_priors

DistributionsInference.with_priors applies the same support-derived heuristic generically, over any fit-protocol object's parameter_rows (a flat, dotted-name row schema), not just a ComposedDistributions tree. It is a separate implementation, not a thin wrapper over this one: DistributionsInference depends on ComposedDistributions, not the reverse, so this package's own default_prior/build_priors cannot delegate to it without inverting that dependency. The two stay independent, parallel implementations of the same heuristic for their respective row shapes.

See also

  • build_priors: assembles the nested prior NamedTuple, using this as the per-row default and accepting overrides.
source
ComposedDistributions.elapsed_between Function

Distribution of the elapsed distance between two named events of a chain.

elapsed_between(chain, from, to) returns the LAW of the elapsed distance from event from to event to on a Sequential chain, as an ordinary univariate distribution: the convolution of the steps strictly between the two events. elapsed_between(chain, to) is the common origin-to-to form (the convolution of the chain prefix up to to).

This is the distribution-level counterpart to the sample-level difference of two event positions, and is deliberately NOT difference: two events on one chain descend from a shared origin, so their absolute positions are not independent and difference(chain, chain) would double-count the shared leading steps. elapsed_between convolves only the intervening steps, so it is the correct same-chain law. The result discretises and composes like any other univariate component, and recomputes when an upstream step's parameters change.

Only a plain continuous chain (every step a delay leaf or a nested plain chain, no branching) has a single elapsed-distance law between two of its events; a chain with a branching step, or a pair whose ordering has no single scalar law, is rejected with a clear error. An uncertain-leaf chain must be pinned with update first, as the template density is not the marginal.

Arguments

  • chain: a Sequential chain.

  • from: the earlier event name (origin-to-to form omits it).

  • to: the later event name.

Examples

julia
using ComposedDistributions, Distributions

chain = sequential(:origin_onset => Gamma(2.0, 1.0),
    :onset_admit => LogNormal(0.5, 0.4),
    :admit_exit => Gamma(1.5, 1.0))

# Between two named events: the single intervening step.
law = elapsed_between(chain, :onset, :admit)
rand(law)

# Origin to a named intermediate event: the convolution of the prefix.
law0 = elapsed_between(chain, :admit)
mean(law0)

See also

  • convolved: the chain-step convolution this returns.

  • event_names: the chain's event names, the valid selectors.

  • difference: the difference of two INDEPENDENT observed totals.

source
ComposedDistributions.event Function
julia
event(d)

Fetch a composed distribution's child (event/edge), or descend a name path.

event(d, path...) returns the sub-distribution of d at the named location: a single Symbol fetches a direct child (a branch of a Parallel, a step of a Sequential, an outcome delay of a Resolve, or an alternative of a Choose); multiple Symbols, or a single dotted-path Symbol (:admit_path.admit_death, as in params_table's edge column), descend the tree one name per step. Throws an ArgumentError naming the valid children if a name along the path is not a child at that level (mirroring update/prune/splice).

Arguments

  • d: the composed distribution to look up a child of (or descend into).

  • path: one or more edge/event names (Symbols) from d down to the target, or a single dotted-path Symbol.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((admit_path = compose((onset_admit = Gamma(2.0, 1.0),
        admit_death = LogNormal(0.5, 0.4))),
    onset_recover = Gamma(3.0, 1.0)))
event(tree, :onset_recover)
event(tree, :admit_path, :admit_death)

See also

source
ComposedDistributions.event_increments Function
julia
event_increments(
    d::Union{Parallel, Sequential},
    rec::NamedTuple
) -> NamedTuple

Invert event_times: convert a record of absolute positions back into the per-step increments the scorer (logpdf) consumes.

event_increments(d, event_times(d, record)) == record. Pass a Vector of records for a batch.

Arguments

  • d: the composed tree (a Sequential or Parallel) the record was drawn from; a bare leaf or one_of node errors, having no per-step chain.

  • record: a single record (NamedTuple) of absolute positions, or a Vector of such records for a batch.

Examples

julia
using ComposedDistributions, Distributions

tree = sequential(:a => LogNormal(0.5, 0.4), :b => Gamma(2.0, 1.0))
event_increments(tree, (a = 1.0, b = 3.0))

See also

source
ComposedDistributions.event_names Function
julia
event_names(
    d::Union{ComposedDistributions.AbstractOneOf, Parallel, Sequential}
) -> Union{Tuple{Vararg{Symbol}}, Tuple{Symbol, Vararg{Any}}}

The flat event names of a composed distribution.

event_names(d) returns the tuple of event names in flat depth-first order: the root origin event followed by one target event per leaf edge. An inner composer's events are exposed, so compose((path = [a, b],)) lists the inner (:onset, ...) events rather than just the (:path,) edge. Event names are derived from the edge names (an edge :onset_admit gives origin :onset and target :admit); a positional default edge contributes :event_i. These event names key a data row, distinct from the nested edge/child structure of event_tree (whose first level is the top-level child names).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = LogNormal(1.5, 0.4),
    admit_death = Gamma(2.0, 1.0)))
event_names(tree)

See also

source
ComposedDistributions.event_times Function
julia
event_times(
    d::Union{Parallel, Sequential},
    rec::NamedTuple
) -> NamedTuple

Convert a drawn record of per-step increments into absolute positions measured from the composed tree's origin.

A draw from d (rand(d)) records each event as an increment from its predecessor. event_times accumulates these into absolute positions: chain steps sum along the chain, parallel branches each measure from the shared origin, and a resolved/racing node reports the position of the outcome that fired. The result is keyed exactly like the input record and stays in unitless distance from the origin. event_increments is the inverse.

Pass a Vector of records for a batch; each row is transformed independently.

Arguments

  • d: the composed tree (a Sequential or Parallel) the record was drawn from; a bare leaf or one_of node errors, having no per-step chain.

  • record: a single drawn record (NamedTuple) of per-step increments, or a Vector of such records for a batch.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((
    path = sequential(:step_a => LogNormal(0.5, 0.4),
        :step_b => Gamma(2.0, 1.0)),
    side = Gamma(1.5, 1.0)))
event_times(tree, (path_step_a = 1.0, path_step_b = 2.0, side = 3.0))

See also

source
ComposedDistributions.event_tree Function
julia
event_tree(d::Union{Parallel, Sequential}) -> NamedTuple

The nested tree of event names of a composed distribution.

event_tree(d) returns the event-name structure as data: a nested NamedTuple keyed by child name down to the leaves, mirroring the tree. Its first level is the top-level child names (the old top-level event_names result); a Sequential/Parallel/Choose child recurses to its own nested NamedTuple, a Resolve child to its outcome names, and a leaf to its own name. Pair with event_names for the flat per-event layout that matches rand/mean/var.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((admit_path = compose((onset_admit = Gamma(2.0, 1.0),
        admit_death = LogNormal(0.5, 0.4))),
    onset_recover = Gamma(3.0, 1.0)))
event_tree(tree)

See also

  • event_names: the flat per-event names

  • event: fetch a child or subtree by name path

source
ComposedDistributions.extra_leaf_params Function
julia
extra_leaf_params(leaf) -> Any

The extra, modifier-owned parameters of a leaf, keyed by name.

A NamedTuple mapping each extra-parameter name to a (value, support) NamedTuple: value is the parameter's current value and support the (lower, upper) bounds a default prior is derived from. The default (a plain leaf, no extras) is the empty NamedTuple (;), and a Truncated peels to its untruncated inner delay. A modifier layer that owns a free parameter which is not one of the inner delay's native parameters plugs in by defining this on its own wrapper type. The thinning factor of thin(d, p) (ModifiedDistributions' ThinOp) is the first instance: it reports (thin = (value = p, support = (0.0, 1.0)),), at which point params_table surfaces a :thin row and update round-trips it.

Arguments

  • leaf: the (possibly wrapped) leaf distribution to inspect.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.extra_leaf_params(Gamma(2.0, 1.0))

See also

source
ComposedDistributions.flat_dimension Function
julia
flat_dimension(
    d::ComposedDistributions.AbstractComposedDistribution
) -> Any

The estimated parameter dimension of a composed distribution.

flat_dimension(d) is the number of scalar estimated parameters: the count of uncertain specs across the tree, i.e. the params_table rows whose prior column carries a spec. A fixed (non-uncertain) leaf contributes nothing, so a tree with no uncertain leaves has flat dimension 0. It is the length of the flat vector flatten produces and unflatten consumes. Read straight off the same compile-time layout walk unflatten uses (a literal count baked in at generation time), so it cannot drift from the codec.

Arguments

  • d: a composed distribution.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((
    onset_admit = uncertain(Gamma(2.0, 1.0); shape = LogNormal(log(2.0), 0.2)),
    admit_death = LogNormal(0.5, 0.4)))
# Public but not exported; reach it by the qualified name. Only onset_admit's
# shape is uncertain, so the dimension is 1.
ComposedDistributions.flat_dimension(tree)

See also

source
ComposedDistributions.flatten Function
julia
flatten(
    d::ComposedDistributions.AbstractComposedDistribution,
    nt::NamedTuple
) -> Any

Flatten a nested parameter NamedTuple to the estimated flat vector.

flatten(d, nt) reads nt (keyed like params(d), the shape update consumes) at each estimated params_table row (an uncertain spec's parameter) and returns those values as a Vector, in table order restricted to the spec'd rows. A fixed parameter is not read. It is the inverse of unflatten: flatten(d, unflatten(d, x)) == x.

Shares the same compile-time layout walk unflatten uses (a thin generated view over it), so the two cannot drift apart.

Arguments

  • d: the composed distribution whose table fixes the order.

  • nt: a nested parameter NamedTuple keyed like params(d).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((
    onset_admit = uncertain(Gamma(2.0, 1.0); shape = LogNormal(log(2.0), 0.2)),
    admit_death = LogNormal(0.5, 0.4)))
# The estimated vector is 1-long (onset_admit.shape); round-trip it.
# Public but not exported; reach the codec by the qualified name.
nt = ComposedDistributions.unflatten(tree, [2.0])
ComposedDistributions.flatten(tree, nt)

See also

source
ComposedDistributions.free_leaf Function
julia
free_leaf(leaf) -> Any

Innermost free delay of a (possibly wrapped) leaf.

The base identity contract: a plain leaf is its own free leaf, and a Truncated peels to its untruncated inner delay (the truncation bounds are fixed structure, not free parameters). A wrapper type (censoring in CensoredDistributions, the modifiers in ModifiedDistributions) adds its own method dispatching on its own type, so a composed leaf is transparent to the prior/params interface. Pair with rewrap_leaf, which rebuilds the same wrapper around a new inner delay.

Arguments

  • leaf: the (possibly wrapped) leaf distribution to peel.

Examples

julia
using ComposedDistributions, Distributions

free_leaf(truncated(Gamma(2.0, 1.0); upper = 10.0))

See also

source
ComposedDistributions.has_uncertain Function
julia
has_uncertain(
    d::Union{ComposedDistributions.AbstractOneOf, Choose, Parallel, Sequential}
) -> Union{Missing, Bool}

Whether a composed distribution still contains an Uncertain leaf.

An Uncertain leaf delegates every Distributions method except rand to its template until it is collapsed with update(tree, params), so scoring or summarising a raw tree that still holds an Uncertain leaf silently uses the template's central values instead of the marginal — a silent wrong answer, not an error. Guard a scoring/fitting loop with this predicate:

julia
collapsed = update(tree, fitted_params)
@assert !has_uncertain(collapsed)   # catch a forgotten update before scoring
logpdf(collapsed, x)

has_uncertain walks the tree (through Sequential/Parallel/Choose/the one_of composers, and through wrapper leaves via the _uncertain_specsrouting hook so ashared/modifier-wrapped uncertain leaf is still seen) and returnstrueas soon as any leaf carries a spec; a fully collapsed tree returnsfalse.

Arguments

  • d: the composed distribution, node, or leaf to check.

Examples

julia
using ComposedDistributions, Distributions

u = uncertain(Gamma(2.0, 1.0); shape = LogNormal(log(2.0), 0.2))
tree = compose((onset_admit = u, admit_death = LogNormal(0.5, 0.4)))
has_uncertain(tree)   # an uncertain leaf remains

collapsed = update(tree, (onset_admit = (shape = 3.0, scale = 1.5),
    admit_death = (mu = 0.7, sigma = 0.5)))
has_uncertain(collapsed)   # resolved: false

See also

source
ComposedDistributions.has_varying Function
julia
has_varying(d::Varying) -> Bool

Whether a composed distribution still contains an un-resolved Varying leaf.

A Varying leaf delegates every Distributions method to its reference until the tree is resolved with instantiate(tree, ctx), so scoring or sampling a raw tree that still holds a Varying leaf silently uses the reference (e.g. the t = 0 delay) instead of the per-record value — a silent wrong answer, not an error. Guard a scoring/sampling call in a fitting loop with this predicate:

julia
resolved = instantiate(tree, Context(time = t))
@assert !has_varying(resolved)   # catch a forgotten instantiate before scoring
logpdf(resolved, x)

has_varying walks the tree and returns true as soon as any leaf is a Varying; a fully stationary or fully-instantiated tree returns false.

Arguments

  • d: the composed distribution, node, or leaf to check.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset = varying(t -> Gamma(2.0, 1.0 + 0.1t)),
    admit = LogNormal(0.5, 0.4)))
has_varying(tree)                                    # a varying leaf remains
has_varying(instantiate(tree, Context(time = 5.0)))  # resolved: false

See also

source
ComposedDistributions.inspect Function
julia
inspect(io::IO, d)

Print a composed distribution's full nested detail.

inspect(io, d) walks the same tree as show but prints each leaf's full text/plain representation (every field), so it is the opt-in companion to the compact structural show. A composer node prints its header and recurses; a leaf prints its detailed representation indented under its name. Writes to io (default stdout) and returns nothing.

Arguments

  • io: the IO stream to print to (default stdout).

  • d: the composed distribution (or bare leaf) to inspect.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))
inspect(tree)

See also

source
ComposedDistributions.instantiate Function
julia
instantiate(d, _::Nothing) -> Any

Resolve a composed tree (or a leaf) against a Context.

instantiate(d, ctx) walks a composed distribution and returns the same tree with every Varying leaf replaced by its distribution at the context's covariate; a fixed leaf is returned unchanged (the identity default), so a stationary tree is untouched and passing nothing is always a no-op. The result is a fully concrete composer that scores, samples and convolves exactly as a hand-built stationary tree would — non-stationarity is resolved here, before those steps, so the convolution / renewal layer receives a concrete kernel per context.

Arguments

  • d: a composer, a leaf, or a Varying leaf.

  • ctx: a Context of covariates, or nothing (a no-op).

Examples

julia
using ComposedDistributions, Distributions

chain = sequential(:onset_admit => varying(t -> Gamma(2.0, 1.0 + 0.1t)),
    :admit_death => LogNormal(0.5, 0.4))
at_day5 = instantiate(chain, Context(time = 5.0))  # concrete, stationary chain
observed_distribution(at_day5)                     # the convolution kernel at t = 5

See also

source
ComposedDistributions.leaf_ctor Function
julia
leaf_ctor(leaf) -> Type

The constructor that rebuilds a leaf's free delay from a positional tuple of parameter values, in leaf_param_names order (excluding any trailing extra_leaf_params, which _update_leaf splits off and re-attaches around the rebuild).

Reconstruction is how an updated parameter vector becomes a distribution again: update, the unflatten then update posterior read-back, uncertain's pinning path, a tied leaf's signature, and a pooled population's template all rebuild a leaf this way. The base identity returns the inner delay's type constructor, which is right for a Distributions.jl family whose params are its constructor arguments.

A leaf type whose free parameters are not its native constructor arguments overrides this. A moment-parameterised wrapper is the motivating case: it reports moments (a mean and a standard deviation) as its parameters, and it carries its family in a type parameter, so the bare UnionAll cannot be called positionally. Such a type returns a callable that supplies whatever the value tuple alone does not carry.

An override must return an egal-stable callable: two structurally identical leaves must return === constructors, since _tie_signature groups tied leaves by this value. A callable closing over a type parameter is egal-stable; one closing over a runtime value is not, and would make tie wrongly reject two compatible leaves. Prefer a callable struct over an anonymous closure.

Arguments

  • leaf: the leaf whose free delay is rebuilt.

Examples

julia
using ComposedDistributions, Distributions

ctor = ComposedDistributions.leaf_ctor(Gamma(2.0, 1.0))
ctor(3.0, 1.5)

See also

  • free_leaf: peel to the inner free delay.

  • rewrap_leaf: re-apply the fixed structure around a rebuilt delay.

source
ComposedDistributions.leaf_detail_lines Function
julia
leaf_detail_lines(leaf) -> Vector{SubString{String}}

Per-leaf inspect detail lines for a (possibly wrapped) leaf.

The leaf-detail extension point inspect reads through _inspect_leaf: the generic method returns the leaf's full text/plain show, split into lines so a multi-line struct dump stays aligned under its tree prefix. A leaf-wrapper type (censoring in CensoredDistributions, the modifiers in ModifiedDistributions) adds its own method dispatching on its own type, to surface the inner free delay's detail instead of the wrapper's raw struct dump. Pair with uncertain_specs, the sibling extension hook for the prior column.

Arguments

  • leaf: the (possibly wrapped) leaf distribution to render.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.leaf_detail_lines(Gamma(2.0, 1.0))

See also

source
ComposedDistributions.leaf_mean Function
julia
leaf_mean(leaf) -> Any

Mean of a (possibly wrapped) leaf, seen through its fixed wrapper structure.

The default is that of the inner free delay (mean(free_leaf(leaf))): a plain leaf is its own free leaf, a Convolved free-leafs to itself and reuses its additive mean, and an Uncertain leaf free-leafs to its template (so a tree containing one reports the template moment; guard with has_uncertain first if that matters). A modifier whose transform changes the moment (an Affine scale/shift) overrides this to report its own analytic moment.

Arguments

  • leaf: the (possibly wrapped) leaf distribution whose mean is read.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.leaf_mean(Gamma(2.0, 1.0))

See also

  • leaf_var: the matching per-leaf variance.
source
ComposedDistributions.leaf_param_names Function
julia
leaf_param_names(leaf) -> Tuple

The estimable parameter names of a (possibly wrapped) leaf.

The inner free delay's param_names, padding with positional fallbacks (:param_1, ...) so every value has a label even when the family is unmapped, then the names of any extra_leaf_params appended in order. A censored or modified leaf delegates to its free delay (free_leaf), so the fixed wrapper structure never appears, while a thinning modifier's :thin factor rides the trailing extra-parameter slot. These names are the coordinates params_table, uncertain and build_priors key on.

Arguments

  • leaf: the (possibly wrapped) leaf distribution whose parameter names are read.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.leaf_param_names(Gamma(2.0, 1.0))

See also

source
ComposedDistributions.leaf_var Function
julia
leaf_var(leaf) -> Any

Variance of a (possibly wrapped) leaf, seen through its fixed wrapper structure.

The variance dual of leaf_mean: the inner free delay's variance by default (var(free_leaf(leaf))), overridden by a modifier whose transform changes the moment (an Affine).

Arguments

  • leaf: the (possibly wrapped) leaf distribution whose variance is read.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.leaf_var(Gamma(2.0, 1.0))

See also

source
ComposedDistributions.missing_covariates Function
julia
missing_covariates(d, ctx::AbstractContext) -> Vector

The covariate names required_covariates(d) lists that a Context does not carry.

missing_covariates(d, ctx) returns the (possibly empty) vector of covariate names required by d but absent from ctx, so a caller can validate a data source up front — confirming every needed covariate column is present before the first instantiate — rather than discovering a gap reactively, one covariate at a time, mid-resolution.

Arguments

  • d: the composed distribution, node, or leaf to inspect.

  • ctx: the Context to check.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset = varying(t -> Gamma(2.0, 1.0 + 0.1t)),
    admit = LogNormal(0.5, 0.4)))
missing_covariates(tree, Context(region = "a"))
missing_covariates(tree, Context(time = 4.0))

See also

source
ComposedDistributions.observed_distribution Function
julia
observed_distribution(
    d::Distributions.UnivariateDistribution
) -> Distributions.UnivariateDistribution

The univariate scalar a downstream observation observes for a composer.

An observation model observes one quantity, so lowering a composer first reduces it to that quantity:

  • a Convolved or Resolve is already univariate (the observed sum, resp. the marginal time-to-resolution) and is returned unchanged;

  • a Sequential chain's observed quantity is the total elapsed time from origin to the terminal event, the convolution of its steps, returned as a Convolved.

A Parallel has several independent endpoints and so no single observed scalar; it is not lowered here.

Examples

julia
using ComposedDistributions, Distributions

seq = Sequential(Gamma(2.0, 1.0), LogNormal(0.5, 0.4))
observed_distribution(seq)

See also

  • convolved: the chain-step convolution
source
ComposedDistributions.occurrence_probability Function
julia
occurrence_probability(c::Resolve) -> Any

The probability that any (non-no-event) outcome occurs for a fixed-probability Resolve node: one minus the no-event branch mass.

See also: Distributions.probs

source
julia
occurrence_probability(c::Compete) -> Any

The probability that any (non-no-event) outcome occurs for a one_of node.

For a racing-hazard Compete node occurrence_probability is the sum of the derived per-cause split Distributions.probs returns (one for proper, eventually-certain causes; the resolved mass for a defective node). For a fixed-probability Resolve node it is one minus the no-event branch mass.

Arguments

  • c: the Compete node whose any-event probability to read.

Examples

julia
using ComposedDistributions, Distributions

node = compete(:death => Gamma(2.0, 3.0), :recover => Gamma(3.0, 2.0))
occurrence_probability(node)

See also

  • Distributions.probs: the per-outcome winning split this sums.
source
ComposedDistributions.parallel Function
julia
parallel(branches::Pair...) -> Parallel

Compose univariate distributions into Parallel branches.

Lowercase verb mirroring sequential / resolve: the public constructor for a Parallel branch set. Pass branch distributions positionally (default names :branch_1, :branch_2, ...) or name => dist pairs to name the branches; a branch may itself be a Sequential, Resolve, Compete, Choose or nested set. Prefer this verb over the bare struct constructor.

Arguments

  • branches: the branch distributions, either as positional distributions or as name => dist pairs naming each branch. A single named tuple (name = dist, …) is the equivalent positional spelling for hand-written branches; use Pairs for data-driven or computed names.

Examples

julia
using ComposedDistributions, Distributions

d = parallel(:admit => Gamma(2.0, 1.0), :notif => LogNormal(1.0, 0.5))
event_names(d)
julia
using ComposedDistributions, Distributions

# The equivalent named tuple spelling.
d = parallel((admit = Gamma(2.0, 1.0), notif = LogNormal(1.0, 0.5)))
event_names(d)

See also

source
ComposedDistributions.param_names Function
julia
param_names(
    _::Distributions.Normal
) -> Tuple{Symbol, Symbol}

The scalar parameter names of a leaf distribution, matched positionally to params(leaf).

Distributions.jl exposes parameter values through params but not their names, so the common families are mapped explicitly here; anything unmapped falls back to :param_1, :param_2, ....

A leaf type whose free parameters are not the native family's overrides this, in step with leaf_ctor: the two together fix the coordinates that params_table, uncertain, build_priors and the flat codec work in. A moment-parameterised wrapper naming a mean and a standard deviation, rather than a shape and a scale, is the motivating case.

Arguments

  • the leaf distribution whose parameter names are read.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.param_names(Gamma(2.0, 1.0))

See also

source
ComposedDistributions.param_priors Function
julia
param_priors(tree; kwargs...) -> NamedTuple

Build the nested prior NamedTuple straight from a composed distribution.

param_priors(tree; priors, default) is a thin convenience over build_priors(params_table(tree)): it reads the parameter inventory of the composed distribution tree and assembles the nested prior NamedTuple in one call, forwarding the same keyword surface. It adds no prior logic of its own.

The result is spec-shaped (a nested NamedTuple of distributions keyed like the tree), so it feeds update directly: update(tree, param_priors(tree)) promotes every free parameter to uncertain with its default prior — the explicit estimate-everything path under uncertain-first (a bare tree estimates nothing). Pass priors to swap in your own spec for named parameters.

Arguments

  • tree: a composed distribution from compose.

Keyword Arguments

  • priors: per-parameter overrides, either a (edge, param) => prior mapping or a nested NamedTuple keyed like the tree; only the listed parameters are overridden (default: empty).

  • default: a function row -> prior for rows not overridden (default: default_prior).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))
priors = param_priors(tree)
priors.onset_admit.shape

See also

source
ComposedDistributions.params_table Function
julia
params_table(
    d::Union{ComposedDistributions.AbstractOneOf, Choose, Parallel, Sequential}
) -> ComposedDistributions.ParamsTable{@NamedTuple{edge::Vector{Symbol}, param::Vector{Symbol}, value::Vector{Any}, support::Vector{Any}, prior::Vector{Any}}}

Flatten a composed distribution's parameters into a prior-definition table.

params_table(d) returns a Tables.jl column table (a ParamsTable wrapping a NamedTuple of equal-length column vectors, so Tables.istable(params_table(d)) is true and it prints as a padded table); wrap it in DataFrame for a DataFrame. It has one row per scalar free parameter of the composed distribution d, with columns:

  • edge: the dotted path of names to the parameter's edge/leaf (e.g. :onset_admit, or :resolution.branch_probs inside a Resolve).

  • param: the parameter name (e.g. :mu, :sigma; positional :param_i where the family is unmapped).

  • value: the current parameter value.

  • support: the (minimum, maximum) variate support of that edge's distribution, the domain a prior over the edge must respect (from minimum/ maximum/support).

  • prior: the attached prior of an uncertain parameter (its spec distribution), or nothing for a fixed parameter. build_priors uses a non-nothing entry ahead of its per-row default.

Define priors against the rows of this table instead of hand-matching parameter names. Built from params (nested, name-keyed values) plus the edge distributions' support.

For a Choose node the alternatives' independent per-branch params are namespaced per alternative (index.… / sourced.…), one row-group per alternative. A parameter tied across alternatives via shared(:tag, ...) is inventoried once under its tag edge and sampled once, so a value tied across the index and sourced branches appears as a single row-group.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = LogNormal(1.5, 0.4),
    admit_death = Gamma(2.0, 1.0)))
tbl = params_table(tree)
tbl.edge  # a column; wrap the table in `DataFrame(tbl)` for a DataFrame

See also

source
ComposedDistributions.pool Function
julia
pool(
    group::Symbol;
    ...
) -> Pool{_A, true, P} where {_A, P<:(Uncertain{Distributions.Continuous, Distributions.LogNormal{Float64}})}
pool(
    group::Symbol,
    population::Distributions.UnivariateDistribution;
    noncentred
) -> Pool

Declare a parameter partially pooled across the leaves of a group, drawn from a shared population distribution.

pool(group, population) returns a Pool spec to place inside an uncertain leaf where a prior would go, e.g.

julia
uncertain(Gamma(2.0, 1.0);
    shape = pool(:district,
        uncertain(LogNormal(0.0, 1.0); mu = Normal(0.0, 1.0),
            sigma = truncated(Normal(0.0, 1.0); lower = 0.0))))

reading as: shape is partially pooled across the :district leaves — each district's shape is drawn from one shared LogNormal population whose (mu, sigma) are estimated. The population is an ordinary distribution; build it with uncertain so its free parameters carry their priors through the same machinery as any uncertain leaf (those become the hyperparameter rows <group>.mu, <group>.sigma, ...). The leaves that name the same group are one population, grouped by tag the way shared groups tied leaves. pool(group) uses a default estimated-LogNormal population.

A location-scale population (Normal/LogNormal) is reparameterised non-centred — one Normal(0, 1) latent per member, reconstructed loc + scale*z (Normal) or exp(loc + scale*z) (LogNormal) — keeping the CensoredDistributions-compatible [hyper..., z...] flat vector. A general population takes the centred path (each member's parameter scored directly against the population). Pass noncentred = false to force the centred form on a location-scale population; noncentred = true is rejected for a general one.

rand on a pooled leaf draws that one parameter's marginal from the population; the joint prior-predictive of a whole pooled tree (population shared across members) comes from sampling the flat priors and rebuilding with update(tree,unflatten(tree, x)).

Arguments

  • group: the pooling-group name (Symbol).

  • population: the shared population distribution (default: an estimated LogNormal). Its free parameters are the hyperparameters.

Keyword Arguments

  • noncentred: force the parameterisation. Defaults to true for a location-scale (Normal/LogNormal) population, false otherwise.

Examples

julia
using ComposedDistributions, Distributions

# Three districts' onset->death delays with a partially pooled shape, drawn
# from a shared estimated-LogNormal population.
model = compose((
    north = uncertain(Gamma(2.0, 1.0); shape = pool(:district)),
    east  = uncertain(Gamma(2.0, 1.0); shape = pool(:district)),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:district))))
# 2 hyperparameters + 3 latents = 5 estimated parameters.
ComposedDistributions.flat_dimension(model)

See also

  • Pool: the spec type.

  • uncertain: builds the population with hyperparameter priors.

  • shared/tie: the complete-pooling (tied) extreme.

source
ComposedDistributions.pool_centred_logprior Function
julia
pool_centred_logprior(rows, nt) -> Any

Sum each centred member's log-density against its population reconstructed at the current hyperparameters (read from the flattened draw nt).

Reached by qualified name from outside this package — DistributionsInference.jl's fit-protocol extension calls this to score a composed tree's centred-pooled population term.

Arguments

  • rows: the (path, param, pool) triples from centred_pool_rows.

  • nt: the nested NamedTuple from unflatten at the same draw.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((north = uncertain(Gamma(2.0, 1.0);
        shape = pool(:region, Beta(2.0, 3.0))),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:region, Beta(2.0, 3.0)))))
rows = ComposedDistributions.centred_pool_rows(tree)
x = fill(0.5, ComposedDistributions.flat_dimension(tree))
nt = ComposedDistributions.unflatten(tree, x)
ComposedDistributions.pool_centred_logprior(rows, nt)

See also

source
ComposedDistributions.pool_group Function
julia
pool_group(_::Pool{group}) -> Any

The pooling-group name (Symbol) of a Pool spec.

Examples

julia
using ComposedDistributions

spec = pool(:district)
ComposedDistributions.pool_group(spec)

See also: pool_noncentred, pool

source
ComposedDistributions.pool_noncentred Function
julia
pool_noncentred(_::Pool{group, noncentred}) -> Any

Whether a Pool spec uses the non-centred (location-scale) parameterisation.

Examples

julia
using ComposedDistributions

spec = pool(:district)
ComposedDistributions.pool_noncentred(spec)

See also: pool_group, pool

source
ComposedDistributions.prune Function
julia
prune(
    d::Union{Choose, Parallel, Resolve, Sequential},
    path::Symbol
) -> Union{Choose, Parallel, Resolve, Sequential}

Drop a branch from a composed distribution (a topology edit).

prune(d, path) removes the node addressed by path from its parent, changing the tree shape. A Resolve arm is removed and the remaining branch probabilities are renormalised to sum to one; a Choose alternative or a Sequential/Parallel step is removed. The parent must keep at least the minimum number of children (two for Resolve/Choose, one for Sequential/Parallel). The result is a valid composed distribution that scores and rands. path accepts the same forms as event: varargs Symbols, a dotted Symbol, or a tuple of edge names.

prune and splice are the two topology edits (they change the tree shape); update keeps the same shape and replaces contents.

Arguments

  • d: the composed distribution to edit.

  • path: the branch to drop, as varargs Symbols, a dotted Symbol, or a tuple of edge names.

Examples

julia
using ComposedDistributions, Distributions

node = resolve(:death => (Gamma(1.5, 1.0), 0.3),
    :disch => (Gamma(2.0, 1.5), 0.5),
    :transfer => (Gamma(1.0, 1.0), 0.2))
tree = compose((resolution = node, onset = Gamma(1.0, 1.0)))
tree2 = prune(tree, :resolution, :transfer)
event_names(event(tree2, :resolution))

See also

  • splice: insert a before/after step at a node (the other topology edit)

  • update: replace a node or its values (keeps the shape)

source
ComposedDistributions.reconstruct Function
julia
reconstruct(
    d::ComposedDistributions.AbstractComposedDistribution,
    x::AbstractVector
) -> Any

Rebuild a composed distribution straight from its estimated flat vector.

reconstruct(d, x) collapses d at the estimated parameters in x, holding each fixed parameter at its template value. It is update(d,unflatten(d, x)) named as one verb, and is the flat-vector primary a per-gradient hot path routes through (DistributionsInference.jl's distribution_to_logdensity and distribution_to_turing).

Being that composition rather than a generated function of its own, reconstruct is not independently shown @inferred-concrete; the guarantee is unflatten's, and update's inferrability is inherited from it.

Arguments

  • d: the composed distribution to rebuild.

  • x: a flat vector of length flat_dimension(d).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = uncertain(Gamma(2.0, 1.0);
    shape = LogNormal(log(2.0), 0.2)),
    admit_death = LogNormal(0.5, 0.4)))
ComposedDistributions.reconstruct(tree, [3.0])

See also

source
ComposedDistributions.required_covariates Function
julia
required_covariates(d) -> Dict{Symbol, Vector{Symbol}}

The covariate names a composed distribution's Varying leaves and data-selected Choose disjunctions will read, keyed to the node paths that read them.

Returns a Dict{Symbol, Vector{Symbol}}: each key is a covariate name a Varying leaf's covariate field or a Choose's selector names, and each value is the dotted edge path (the same edge namespace params_table uses) of every node that reads it. A stationary tree (no Varying leaf, no data-selected Choose) returns an empty Dict.

Pair with missing_covariates to check a Context up front, reporting every covariate a fitting loop still needs instead of discovering each one reactively, one at a time, mid-instantiate.

Arguments

  • d: the composed distribution, node, or leaf to inspect.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset = varying(t -> Gamma(2.0, 1.0 + 0.1t)),
    admit = LogNormal(0.5, 0.4)))
required_covariates(tree)

See also

source
ComposedDistributions.required_parameters Function
julia
required_parameters(
    d
) -> Vector{@NamedTuple{edge::Symbol, param::Symbol}}

The unpinned (estimated) parameters a composed distribution's params_table still needs, as (edge, param) pairs.

The symmetric sibling of required_covariates: where that lists the covariate columns a tree's Varying/Choose leaves still need from a Context, this lists the parameters an uncertain leaf still needs a value for (every params_table row whose prior is not nothing). A fully concrete (pinned) tree returns an empty vector.

Arguments

  • d: the composed distribution, node, or leaf to inspect.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset = uncertain(Gamma(2.0, 1.0); shape = LogNormal(0.0, 0.3)),
    admit = LogNormal(0.5, 0.4)))
required_parameters(tree)

See also

source
ComposedDistributions.reserved_record_fields Function
julia
reserved_record_fields() -> NTuple{6, Symbol}

The reserved per-record field names in a scoring row.

A row passed to logpdf(tree, row) (or a table of such rows) is matched to the tree's events by name; these reserved names are read for their own meaning instead of being treated as events, and every other field must be an event of the tree. Returned as a Tuple of Symbols. Each reserved field, with its owner, type, default and when it is read:

  • weight / count (the scorer; Real; default 1, unweighted): read when a row carries a multiplicity.

  • obs_time (the censored scorer; Real; default none, uncensored): read when a per-record right-truncation horizon D is given.

  • obs_window (the censored scorer; Real; default none): read when a δ-bounded window [obs_time - δ, obs_time] is given.

  • branch_probs / branch_prob (a nested Resolve; probabilities; default the node's own): read when a row overrides a Resolve's branch split.

The covariate context read by a Varying leaf is a separate namespace: a single flat bag keyed by bare covariate name (Context(time = 4.0)), shared across every leaf and merged last-writer-wins by with_covariates, so an observed covariate and a sampler-threaded latent parameter share one channel. Covariate names are open (any Symbol), so they are not validated against a fixed registry the way these reserved row fields are.

Examples

julia
using ComposedDistributions

reserved_record_fields()

See also

  • event_names: the event namespace a row is otherwise matched against.

  • params_table: the parameter namespace (dotted node paths).

source
ComposedDistributions.resolve Function
julia
resolve(
    outcomes::Pair...
) -> Resolve{_A, D, P, Nothing} where {_A, D<:Tuple, P<:Tuple}

Build a fixed-probability Resolve node from name => (delay, branch_prob) outcomes: exactly one outcome resolves, with cause independent of timing.

Each outcome is name => (delay, branch_prob); the branch probabilities must each lie in and sum to one, and at least two outcomes are required.

The last outcome's probability may be omitted (a bare name => delay): it then takes the residual 1 - sum(of the others), so a probability that is fully determined by the rest need not be written out (and cannot disagree with them). The leading probabilities must sum to at most one. Omitting any outcome but the last, or more than one, is rejected. To omit every probability (a racing-hazard node where the winning probability is derived from the hazards) use compete instead.

Arguments

  • outcomes: two or more name => (delay, branch_prob) pairs, each giving the outcome name (a Symbol), its delay distribution, and the probability that the outcome occurs. The last pair's probability may be omitted (a bare name => delay), taking the residual 1 - sum(of the others). A single named tuple (name = (delay, branch_prob), …) is the equivalent positional spelling for hand-written outcomes; use Pairs for data-driven or computed names.

Examples

julia
using ComposedDistributions, Distributions

cfr = 0.3
node = resolve(:death => (Gamma(1.5, 1.0), cfr),
    :disch => (Gamma(2.0, 1.5), 1 - cfr))
mean(node)
julia
using ComposedDistributions, Distributions

# The equivalent named tuple spelling for hand-written outcomes.
cfr = 0.3
node = resolve((death = (Gamma(1.5, 1.0), cfr),
    disch = (Gamma(2.0, 1.5), 1 - cfr)))
mean(node)
julia
using ComposedDistributions, Distributions

# The discharge probability is the residual `1 - cfr`, so it is omitted.
cfr = 0.3
node = resolve(:death => (Gamma(1.5, 1.0), cfr),
    :disch => Gamma(2.0, 1.5))
mean(node)

See also

source
ComposedDistributions.rewrap_leaf Function
julia
rewrap_leaf(leaf, inner) -> Any

Rebuild the same wrapper around a new inner delay inner.

The inverse of free_leaf: the base identity returns inner, and a Truncated re-applies its bounds around the rebuilt inner delay. A wrapper type adds its own method on its own type, so rewrap_leaf(leaf, free_leaf_of_new) carries the fixed structure across a parameter update.

Arguments

  • leaf: the wrapped leaf whose fixed structure is re-applied.

  • inner: the new inner delay to wrap.

Examples

julia
using ComposedDistributions, Distributions

rewrap_leaf(truncated(Gamma(2.0, 1.0); upper = 10.0), Gamma(3.0, 1.5))

See also

source
ComposedDistributions.sequential Function
julia
sequential(steps::Pair...) -> Sequential

Compose univariate distributions into a Sequential chain.

Lowercase verb mirroring parallel / resolve: the public constructor for a Sequential chain. Pass step distributions positionally (default names :step_1, :step_2, ...) or name => dist pairs to name the steps; a step may itself be a Parallel, Resolve, Compete, Choose or nested chain. Prefer this verb over the bare struct constructor.

Arguments

  • steps: the step distributions, either as positional distributions or as name => dist pairs naming each step. A single named tuple (name = dist, …) is the equivalent positional spelling for hand-written steps; use Pairs for data-driven or computed names.

Examples

julia
using ComposedDistributions, Distributions

d = sequential(:onset_admit => Gamma(2.0, 1.0),
    :admit_death => LogNormal(0.5, 0.4))
event_names(d)
julia
using ComposedDistributions, Distributions

# The equivalent named tuple spelling.
d = sequential((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))
event_names(d)

See also

source
ComposedDistributions.set_extra_leaf_params Function
julia
set_extra_leaf_params(
    leaf,
    _::NamedTuple{()}
) -> Distributions.Truncated

Set a leaf's extra, modifier-owned parameters by name and rebuild the leaf.

The setter dual of extra_leaf_params: vals is a NamedTuple mapping each extra name to a new value (the support is fixed structure, not passed), and the leaf is rebuilt carrying the updated values. The default no-extras method is the identity on the empty NamedTuple, and a Truncated re-applies its bounds around the rebuilt inner delay. A modifier layer that owns an extra parameter defines this on its own wrapper type, rebuilding around the new value.

Arguments

  • leaf: the leaf whose extra parameters are set.

  • vals: a NamedTuple of extra name to new value.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.set_extra_leaf_params(Gamma(2.0, 1.0), (;))

See also

source
ComposedDistributions.shared Function
julia
shared(
    name::Symbol,
    dist::Distributions.UnivariateDistribution
) -> Shared

Tag a leaf distribution as a shared parameter group named name.

shared(name, dist) marks dist as a tied parameter so multiple occurrences of the same name in a composed distribution are handled once by the prior/params interface (inventoried, sampled and updated as a single free parameter), with the shared value placed in every occurrence. The result is transparent to scoring and sampling.

shared(name, dist) is the leaf-local spelling of the tie, applied where the leaf is built. tie(d, paths...; name) is the tree-level spelling of the same tie: it walks a composed d to the named leaves and wraps each in the exact shared(name, leaf) artefact this produces. Use whichever is convenient; the tagged occurrences are one free parameter either way.

Arguments

  • name: the shared-parameter group name (Symbol).

  • dist: the leaf distribution to tag.

Examples

julia
using ComposedDistributions, Distributions

# The same incubation `inc` tied across two branches of a `choose`.
inc = shared(:inc, Gamma(2.0, 1.0))
d = choose(:index => inc,
    :sourced => compose((src = LogNormal(0.5, 0.4), inc = inc)))
event_names(d)

See also

  • Shared: the tagged-leaf type.

  • tie: the tree-level, path-based spelling of the same tie.

source
ComposedDistributions.shared_tag Function
julia
shared_tag(leaf) -> Any

The shared tag of a (possibly wrapped) leaf, or nothing when untagged.

A shared(:tag, dist) leaf carries a tag that ties every occurrence to one set of parameters. The tag survives wrapper leaves (a Truncated, and the censoring / modifier wrappers whose own methods live in their owning package or extension), so a shared(:inc, ...) leaf and a bare shared(:inc, Gamma(...)) both report :inc. An untagged leaf reports nothing. params_table uses the tag as a leaf's edge and inventories a tied parameter once.

Arguments

  • leaf: the (possibly wrapped) leaf distribution whose tag is read.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.shared_tag(shared(:inc, Gamma(2.0, 1.0)))

See also

  • shared: constructs a tagged leaf.

  • tie: the tree-level, path-based spelling of the same tie.

source
ComposedDistributions.splice Function
julia
splice(
    d::Union{Choose, Parallel, Resolve, Sequential},
    path::Symbol;
    before,
    after
)

Splice before/after steps around a node in a composed distribution (a topology edit).

splice(d, path; before, after) replaces the node at path with a Sequential chain of before, the original node, then after (any of which may be omitted). This inserts a change-point step around the addressed node without rebuilding the rest of the tree, e.g. an extra delay before a branch or a follow-up step after it, changing the tree shape. The result is a valid composed distribution that scores and rands. path accepts the same forms as event: varargs Symbols, a dotted Symbol, or a tuple of edge names.

splice and prune are the two topology edits (they change the tree shape); update keeps the same shape and replaces contents.

Arguments

  • d: the composed distribution to edit.

  • path: the node to wrap, as varargs Symbols, a dotted Symbol, or a tuple of edge names.

Keyword Arguments

  • before: a name => dist step inserted before the node (default: none).

  • after: a name => dist step inserted after the node (default: none).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))
tree2 = splice(tree, :admit_death; after = :death_report => Gamma(1.0, 2.0))
event_names(event(tree2, :admit_death))

See also

  • prune: drop a branch (the other topology edit)

  • update: replace a node or its values (keeps the shape)

source
ComposedDistributions.tie Function
julia
tie(
    d::Union{ComposedDistributions.AbstractOneOf, Choose, Parallel, Sequential},
    paths...;
    name
)

Tie leaves at named paths of a composed distribution into one shared group.

tie(d, paths...; name) walks the composed distribution d to each leaf named by paths and wraps it in a Shared group tagged name, returning the rebuilt composed distribution. This is the tree-level, path-based spelling of shared: tie(d, p1, p2; name = :inc) produces the exact same artefact as building d with shared(:inc, leaf) at each of those leaves, so every tag consumer (params_table, build_priors, update, a downstream composed_parameters_model) inventories, samples and updates the tied leaves as a single free parameter.

Each path takes the same forms event and update accept: a bare Symbol direct child, a dotted-path Symbol (:"sourced.inc", as in params_table's edge column), or a tuple of edge names from the root. Every path must resolve to a leaf (not a composer subtree), and the tied leaves must be parameter-compatible (same inner family and parameter structure), since they become one group.

Arguments

  • d: the composed distribution to tie leaves in.

  • paths: one or more leaf paths to tie together.

Keyword Arguments

  • name: the shared-parameter group name (Symbol, required).

Examples

julia
using ComposedDistributions, Distributions

d = choose(:index => compose((inc = Gamma(2.0, 1.0),)),
    :sourced => compose((src = LogNormal(0.5, 0.4), inc = Gamma(2.0, 1.0))))
tied = tie(d, (:index, :inc), (:sourced, :inc); name = :inc)
params_table(tied)

See also

  • shared: the leaf-local spelling of the same tie.

  • event, update: share the path forms tie accepts.

source
ComposedDistributions.uncertain Function
julia
uncertain(
    template::Distributions.UnivariateDistribution;
    kwargs...
) -> Any

Attach parameter uncertainty to a distribution: parameters that are themselves distributions, nestable to any depth.

uncertain has three forms:

  • uncertain(template; kwargs...) wraps a concrete template leaf so the named parameters are drawn from the given distributions rather than fixed. Each keyword is a parameter name of the template's free delay (as in params_table's param column); a distribution value makes that parameter uncertain, a Real value re-pins it to a new fixed value.

  • uncertain(Family, args...) (a type, e.g. Gamma) takes one positional argument per parameter, in the family's constructor order: a UnivariateDistribution makes that parameter uncertain, a Real fixes it. The template is the family's default instance with the Real slots pinned; the uncertain slots are driven by their specs.

  • uncertain(Family; kwargs...) is the keyword form on the family's default-constructed template; every parameter must then be given explicitly.

A spec may itself be an uncertain distribution, so hyper-uncertainty nests. The template may be a wrapped leaf (truncated(...), a censoring wrapper): the wrapper is fixed structure re-applied to every draw. Apply such wrappers inside the template. truncated is the exception: applied outside it pushes itself into the template automatically.

The result is a Distributions.UnivariateDistribution and composes as a leaf everywhere (sequential, parallel, resolve, compete, choose, shared): rand draws the marginal, and update(tree, params) collapses an uncertain leaf to its concrete template. In params_table an uncertain parameter's spec rides the row's prior column, so build_priors picks it up without an explicit override.

Only rand is marginal

Every other query on the result — logpdf/cdf/quantile/... and the moments mean/var/std — silently reports the template's central values, not the marginal. Guard a scoring/fitting loop with has_uncertain before assuming a tree is fully concrete.

A template whose parameters are themselves composite (e.g. a Convolved/ Difference node from the ConvolvedDistributions interop, whose parameters are its components' own parameter tuples rather than scalars) is refused with an informative ArgumentError: attach uncertainty to an individual component instead, either by building the composite from uncertain components or by targeting one through update at its component_i path (that interop sees through a composite leaf to its component parameters).

Arguments

  • template: the concrete (possibly wrapped) leaf distribution, or a distribution type (e.g. Gamma).

  • args...: for the positional family form, one value per parameter (a distribution for an uncertain parameter, a Real for a fixed one).

Keyword Arguments

  • kwargs...: parameter name = spec pairs. A distribution spec makes the parameter uncertain; a Real re-pins the template's fixed value.

Examples

julia
using ComposedDistributions, Distributions

# A literature-reported Gamma delay with an uncertain shape.
u = uncertain(Gamma(2.0, 1.0); shape = LogNormal(log(2.0), 0.2))
rand(u)

# The positional family form: shape uncertain, scale fixed at 1.0.
uncertain(Gamma, LogNormal(log(2.0), 0.2), 1.0)

# Nested: the shape's prior location is itself uncertain.
uncertain(Gamma(2.0, 1.0);
    shape = uncertain(LogNormal(log(2.0), 0.2); mu = Normal(log(2.0), 0.1)))

See also

  • Uncertain: the wrapper type.

  • update: collapse an uncertain leaf to a concrete distribution.

source
julia
uncertain(
    d::ComposedDistributions.AbstractComposedDistribution,
    params::NamedTuple
) -> Any

Promote one or more of an existing tree's free parameters to uncertain.

uncertain(tree, params) / uncertain(tree; kwargs...) apply params (or the keywords, packed the same way) through the same dotted/nested targeting update uses — a Shared tag routes through its tag, a nested edge targets a descendant node — but restricted to calls that introduce or extend at least one prior. A distribution value promotes that parameter (replacing any spec already there — promoting an already-uncertain parameter replaces its spec; it does not nest a hyperprior); a Real value alongside it re-pins a sibling parameter in the same call without itself becoming uncertain. A call with no distribution anywhere is refused: use update for a purely concrete edit.

uncertain(tree) (no params) promotes every free parameter with its default, support-derived prior — one call in place of update(tree, param_priors(tree)).

This is the preferred way to write a promotion: update(tree, nt) still accepts the identical distribution-valued nt directly (unchanged), for a call site that assembles nt programmatically without knowing in advance whether it promotes anything.

Arguments

  • tree: the composed distribution to promote parameters of.

  • params: a nested NamedTuple, dotted/nested like update's params, with at least one distribution-valued entry.

Keyword Arguments

  • kwargs...: the same targeting, as keywords (onset_admit = (shape = ...,)).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))

# Targeted promotion: only onset_admit.shape becomes uncertain.
u = uncertain(tree; onset_admit = (shape = LogNormal(log(2.0), 0.2),))
has_uncertain(u)

# Promote every free parameter with a default prior.
everything = uncertain(tree)
has_uncertain(everything)

See also

  • update: the underlying mechanism (merge mode) this promotes through; also the concrete-set / node-replacement / flat-vector / table / chain verb.

  • Uncertain: the leaf type a promoted parameter's spec builds.

  • param_priors: the default priors uncertain(tree) (bare) applies.

  • has_uncertain: check whether any promotion remains unresolved.

source
ComposedDistributions.uncertain_specs Function
julia
uncertain_specs(leaf) -> Any

Leaf-level distribution-valued parameter specs, or nothing for a fixed leaf.

The uncertain-spec protocol: a NamedTuple of a leaf's distribution-valued parameters (its attached priors), keyed by parameter name, or nothing when the leaf carries no attached prior. The base identity returns nothing, and a Truncated peels to its untruncated inner delay's specs (the truncation bounds are fixed structure, not free parameters). Uncertain reports its own specs (see Uncertain.jl), and a leaf-wrapper type (censoring in CensoredDistributions, the modifiers in ModifiedDistributions) adds its own method dispatching on its own type and forwarding to its inner delay's specs, so an uncertain prior attached under a wrapper still reaches params_table's prior column and build_priors. Without a forwarding method the attached prior is silently dropped and the parameter is treated as fixed.

Arguments

  • leaf: the (possibly wrapped) leaf distribution to inspect.

Examples

julia
using ComposedDistributions, Distributions

ComposedDistributions.uncertain_specs(Gamma(2.0, 1.0)) === nothing

See also

source
ComposedDistributions.unflatten Function
julia
unflatten(
    d::ComposedDistributions.AbstractComposedDistribution,
    x::AbstractVector
) -> Any

Rebuild the full nested parameter NamedTuple from an estimated flat vector.

unflatten(d, x) maps the estimated flat vector x (the spec'd parameters, e.g. a draw from a sampler) back to the full nested NamedTuple update consumes: each estimated parameter takes its value from x, each fixed parameter its template value. It is the inverse of flatten, so update(d, unflatten(d, x)) collapses every uncertain leaf at the draw while holding the fixed parameters at the template.

Generated once per distinct tree type from a compile-time layout walk (no Dict, no intermediate Any-typed accumulation), so the result is @inferred-concrete and the reverse-mode AD backends (including Enzyme) differentiate through it.

Arguments

  • d: the composed distribution whose table fixes the layout.

  • x: an estimated flat vector of length flat_dimension(d).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((
    onset_admit = uncertain(Gamma(2.0, 1.0); shape = LogNormal(log(2.0), 0.2)),
    admit_death = LogNormal(0.5, 0.4)))
# One estimated parameter (onset_admit.shape); the rest stay at the template.
# Public but not exported; reach it by the qualified name.
update(tree, ComposedDistributions.unflatten(tree, [3.0]))

See also

  • flatten: the inverse, nested NamedTuple -> flat vector.

  • reconstruct: flat vector straight to a rebuilt distribution.

  • update: rebuild the distribution from the result.

source
ComposedDistributions.update Function

Update a composed distribution's parameters or replace named nodes — the single verb for every shape-preserving edit, dispatching on the second argument.

  • a nested NamedTuple of parameter values/specs (below): fixes or re-specs free parameters, the fine-grained value edit;

  • one or more path => new_node pairs (update(d, edits::Pair...) in structural_edits.jl): replaces whole nodes, coarser than a value edit but still same-shape.

For topology edits that change the tree shape, use prune or splice instead.

update(d, params::NamedTuple) — set free parameters

update(d, params) returns a new distribution of the same structure as d with its parameters set from params, a nested NamedTuple mirroring the tree: a Sequential/Parallel is keyed by its edge names, a leaf by its parameter names (as in params_table's param column), and a Resolve by its outcome names plus an optional branch_probs entry. A censored leaf is transparent: supply only the inner delay's parameters and the censoring is carried through.

The value type at a leaf parameter decides what happens (the object-level spelling of "distribution in the slot = estimate, value = fix"):

  • a Real pins the parameter to that fixed value, collapsing any uncertain spec on it. A NamedTuple of all-Real values replaces every free parameter (each key required), the plain concrete update;

  • a distribution makes the parameter uncertain with that spec. Passing distributions switches to a partial update: only the named parameters change (an absent parameter keeps its current spec or fixed value), so update(tree, (onset = (shape = LogNormal(log(2), 0.2),),)) makes just onset's shape uncertain. Promote a whole tree to uncertainty over its free parameters with default priors via update(tree, param_priors(tree)) — the explicit estimate-everything path;

  • a pool spec makes the parameter partially pooled across the leaves that name the same group (also a partial update), e.g. update(tree, (onset = (shape = pool(:district),),)).

A Resolve node's branch_probs are a node-level parameter, not a leaf: attach a simplex-valued Distributions.Dirichlet at the branch_probs slot to make them uncertain, update(node, (branch_probs = Dirichlet(ones(K)),)). The Dirichlet is the prior you write; the codec estimates the node through the Dirichlet's K-1 stick-breaking coordinates (labelled :stick_1 … :stick_{K-1} in params_table and a fitted chain), each a Beta in (0, 1), so every draw lands on the probability simplex and the gradient is well-defined. The probabilities are recovered from any draw: a strict update from the stick coordinates (as read back from a chain) collapses the node to concrete probabilities summing to one (read them with Distributions.probs). Promote attaches a flat Dirichlet(ones(K)) per Resolve.

Read a fitted chain back onto a template with DistributionsInference.point_estimate (or readback_draws for every draw) — this package stays fit-protocol-agnostic, so chain readback lives in DistributionsInference.jl rather than here; the NamedTuple it returns pairs directly with update.

Arguments

  • d: the composed distribution (or bare leaf) to update.

  • params: a nested NamedTuple keyed like d, each leaf value a Real (fix) or a UnivariateDistribution (make uncertain).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))
# Concrete values pin the parameters.
tree2 = update(tree, (onset_admit = (shape = 3.0, scale = 1.5),
    admit_death = (mu = 0.7, sigma = 0.5)))
event(tree2, :onset_admit)
# A distribution makes just that parameter uncertain (a partial update).
est = update(tree, (onset_admit = (shape = LogNormal(log(2.0), 0.2),),))
has_uncertain(est)

update(d, path => new_node, ...) — replace nodes

update(d, path => new_node, ...) returns a new composed distribution of the same outer structure as d with the node addressed by each path replaced by new_node. A path is a Symbol (a top-level child), a dotted Symbol (:admit_path.admit_resolution.death, as in event / params_table), or a tuple of edge names from the root (e.g. (:admit_path, :admit_resolution, :death)); the same address event reads is the one this writes. new_node may be a leaf distribution or a nested composer. This shares the recursive reconstruction with the value-update form above, so the result scores and rands. It preserves the tree shape; for shape changes use prune or splice.

Arguments

  • d: the composed distribution to edit.

  • edits: one or more path => new_node pairs.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))
tree2 = update(tree, :admit_death => Gamma(3.0, 1.5))
event(tree2, :admit_death)

update(d, x::AbstractVector) — set from flat vector

update(d, x) is a shorthand for update(d, unflatten(d, x)): rebuild the distribution with parameters read from the flat estimated vector x. Each estimated parameter (an uncertain spec in params_table) takes its value from the vector, each fixed parameter its template value. This collapses the tree at the draw and is commonly used to rebuild a distribution from a sampler output after reading it into the flat coordinate system.

Arguments

  • d: the composed distribution to update.

  • x: a flat vector of estimated parameters, of length flat_dimension (d).

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = uncertain(Gamma(2.0, 1.0);
    shape = LogNormal(log(2.0), 0.2)),
    admit_death = LogNormal(0.5, 0.4)))
# The one estimated parameter is onset_admit.shape; the vector is length 1.
# This is equivalent to
# update(tree, ComposedDistributions.unflatten(tree, [3.0])).
result = update(tree, [3.0])
event(result, :onset_admit)

update(d, table) — bulk-set from a Tables.jl table

update(d, table) reads a params_table-shaped Tables.jl table (any Tables.istable source with edge/param columns) and folds every row into the tree in one call: a row's prior (when present and not nothing) promotes that parameter to uncertain, otherwise its value sets it — the spreadsheet-style bulk-edit route, an input format for update rather than a separate verb.

Arguments

  • d: the composed distribution to update.

  • table: a Tables.jl table with edge/param columns and a value and/or prior column.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((onset_admit = Gamma(2.0, 1.0),
    admit_death = LogNormal(0.5, 0.4)))
update(tree, params_table(tree))   # a no-op round-trip here

See also

  • uncertain(tree, ...): the promotion-only entry point built on the same merge-mode pipeline as this docstring's distribution-valued forms

  • params_table: the flat inventory whose param names key the leaves

  • param_priors: default priors for the promote path

  • flatten, unflatten: the flat <-> nested codec

  • prune, splice: topology edits that change the shape

source
ComposedDistributions.validate_pool_groups Function
julia
validate_pool_groups(d) -> Any

Check that every leaf of each pool group declares the same population distribution and parameterisation, throwing an ArgumentError on a mismatch.

Called once at params_table construction time (typically at distribution_to_logdensity construction), not per gradient evaluation. Reached by qualified name from outside this package — DistributionsInference.jl's fit-protocol extension calls this directly to gate a tree before fitting (#212).

Arguments

  • d: the composed tree whose pool groups are checked.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((north = uncertain(Gamma(2.0, 1.0);
        shape = pool(:region, Beta(2.0, 3.0))),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:region, Beta(2.0, 3.0)))))
ComposedDistributions.validate_pool_groups(tree)

See also

source
ComposedDistributions.validate_tree_names Function
julia
validate_tree_names(d)

Check that no pool group, shared tag, or top-level edge name in the tree collides with one from another of those three roles, throwing an ArgumentError on a collision.

The root-lifted codec merge (unflatten's _root_merge_expr) puts pool groups, shared tags, and root edge names into the same flat namespace; a name crossing roles would silently clobber another entry there. Reusing the same tag for a deliberate tie (shared/tie, or a pool group with several members) is the intended feature and is not flagged; only a name crossing roles is an error. Called once at params_table construction time (typically at distribution_to_logdensity construction), not per gradient evaluation. Reached by qualified name from outside this package — DistributionsInference.jl's fit-protocol extension calls this directly to gate a tree before fitting (#212).

Arguments

  • d: the composed tree whose pool/shared/root names are checked.

Examples

julia
using ComposedDistributions, Distributions

tree = compose((north = uncertain(Gamma(2.0, 1.0);
        shape = pool(:region, Beta(2.0, 3.0))),
    south = uncertain(Gamma(2.0, 1.0); shape = pool(:region, Beta(2.0, 3.0)))))
ComposedDistributions.validate_tree_names(tree)

See also

source
ComposedDistributions.varying Function
julia
varying(f; covariate, reference) -> Varying

Build a Varying context-indexed leaf.

varying(f; covariate = :time, reference = f(0.0)) wraps a map f (a covariate value to a UnivariateDistribution) as a leaf that varies with the named covariate. The reference distribution is used when the leaf is queried without a context; it defaults to f(0.0) (the map at the origin), which suits a :time covariate — pass an explicit reference for a categorical covariate where f(0.0) is not meaningful.

Arguments

  • f: map from a covariate value to a UnivariateDistribution.

Keyword Arguments

  • covariate: the Context field name to read (default :time).

  • reference: the distribution used without a context (default f(0.0)).

Examples

julia
using ComposedDistributions, Distributions

# An onset->admit delay whose mean shortens over calendar time.
d = varying(t -> Gamma(2.0, 1.0 + 0.1 * t); covariate = :time)
mean(d)                                   # the reference (t = 0)
mean(instantiate(d, Context(time = 5.0))) # the delay at t = 5

See also

source
ComposedDistributions.with_covariates Function
julia
with_covariates(ctx::Context; covariates...) -> Context

Add or override covariates on a Context, returning a new context.

with_covariates(ctx; kwargs...) is how the sampling layer of the uncertain distributions work threads its latent index into the same covariate channel an observed covariate uses: starting from a per-record observed context (calendar time, region), it adds the parameter values it has sampled (with_covariates(ctx; inc_shape = θ)), and a Varying leaf keyed on that name resolves against them exactly as a time-varying leaf resolves against time. Observed and latent indices are the same covariate channel; the only difference is who fills the slot — the data, or the sampler. Later keys win over earlier ones.

Arguments

Keyword Arguments

  • covariates...: covariate name = value pairs to add or override.

Examples

julia
using ComposedDistributions

base = Context(time = 4.0)                       # observed covariates
drawn = with_covariates(base; inc_shape = 2.3)   # sampler adds a latent param
(drawn.covariates.time, drawn.covariates.inc_shape)

See also

source