Delay chains, additive moments, and collapsing
Introduction
A multi-step delay is a chain: each step adds an independent delay onto the previous event. Sequential composes the steps, and the chain is one distribution over the whole origin-to-final gap. When every step is an Exponential with the same rate, the total is an Erlang (a Gamma with integer shape), the linear chain trick.
This tutorial builds a chain, reads its structure and moments, and collapses it to its total. It builds on Composing distributions.
using ComposedDistributions
using DistributionsA chain of exponential steps
We build a four-step chain, each step an Exponential with mean theta. sequential names the steps; a bare Vector passed to compose is the same chain with default step names.
theta = 1.5
steps = [Symbol("step_", i) => Exponential(theta) for i in 1:4]
chain = sequential(steps...)Sequential (4 steps)
├─ step_1: Distributions.Exponential{Float64}(θ=1.5)
├─ step_2: Distributions.Exponential{Float64}(θ=1.5)
├─ step_3: Distributions.Exponential{Float64}(θ=1.5)
└─ step_4: Distributions.Exponential{Float64}(θ=1.5)The flat event layout is the origin plus one event per step.
event_names(chain)(:event_1, :event_2, :event_3, :event_4, :event_5)Additive moments
The overall moments of a chain sum over the steps, so a four-step exponential chain has mean 4 * theta and variance 4 * theta^2.
mean(chain), var(chain)(6.0, 9.0)Collapsing to the total
observed_distribution collapses the chain to the single distribution of its origin-to-final gap, integrating the intermediate events out. For the exponential chain this total is an Erlang, so its moments match Gamma(4, theta).
total = observed_distribution(chain)
(chain_mean = mean(total), gamma_mean = mean(Gamma(4, theta)))(chain_mean = 6.0, gamma_mean = 6.0)The variance matches too.
(chain_var = var(total), gamma_var = var(Gamma(4, theta)))(chain_var = 9.0, gamma_var = 9.0)A chain of k exponential steps of mean theta is a Gamma(k, theta) delay, the linear chain trick.
Reusing the chain
A chain is a composer, so it drops into a larger tree as a branch and the same steps can be reused across models.
tree = compose((incubation = chain, reporting = Exponential(2.0)))
event_names(tree)(:event_1, :event_2, :event_3, :event_4, :event_5, :event_6)Summary
A
Sequentialchain composes per-step delays into one distribution over the whole gap.The overall
meanandvarare additive over the steps.observed_distributioncollapses the chain to its convolved total.A chain of
kequal exponential steps is aGamma(k, theta), the linear chain trick, and the chain nests inside a larger tree.
Where next
Competing outcomes shows the disjunctive one_of nodes.
Composing distributions is the full walkthrough of every verb.
LoweredDistributions.jl represents a chain like this as an explicit compartmental system for the CTMC-style algebra.