Skip to content

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.

julia
using ComposedDistributions
using Distributions

A 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.

julia
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.

julia
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.

julia
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).

julia
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.

julia
(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.

julia
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 Sequential chain composes per-step delays into one distribution over the whole gap.

  • The overall mean and var are additive over the steps.

  • observed_distribution collapses the chain to its convolved total.

  • A chain of k equal exponential steps is a Gamma(k, theta), the linear chain trick, and the chain nests inside a larger tree.

Where next