Delay chains and the linear chain trick
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). This is the linear chain trick, the identity behind representing a Gamma-distributed delay as a series of exponential compartments.
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)The linear chain trick is exactly this identity: a chain of k exponential steps of mean theta is a Gamma(k, theta) delay, so a smooth delay can be represented as a series of memoryless compartments and back again.
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.