Skip to content

Composing delays: convolution

The lowering overview and the per-backend pages each lowered a single Distributions.Distribution. The input side composes too. ConvolvedDistributions.convolved builds the delay that is the sum of independent delays, and a sum of delays is again a phase-type, so lower folds the components into one series chain that feeds the same backends.

julia
using LoweredDistributions
using Distributions
using ConvolvedDistributions

delay = convolved(Gamma(2.0, 1.5), Exponential(3.0))
serial = lower(delay)
PhaseType(3 phases)

Gamma(2, ...) is two Erlang phases and the Exponential is one, so the convolution is a three-phase chain, and its mean is the sum of the parts.

julia
length(serial.α), mean(delay)
(3, 6.0)
julia
using LinearAlgebra
-sum(transpose(serial.α) * inv(Matrix(serial.S)))    # the lowered mean
6.0

Backend-ready, and faithful

The composed delay is an AbstractLowering like any other, so every backend from the per-backend pages takes it unchanged. Here the ODE view: both components lower exactly, so the absorbed mass reproduces the convolution's own CDF.

julia
using SciMLBase
using OrdinaryDiffEqTsit5

prob = ode_problem(serial, (0.0, 20.0))
sol = solve(prob, Tsit5())
round(sol(6.0)[end]; digits = 6), round(cdf(delay, 6.0); digits = 6)
(0.586868, 0.586868)

What refuses

Difference and Product are not sums of delays — a difference has signed support, a product is not a convolution — so neither is phase-type representable, and lower refuses them explicitly rather than return a silent moment-matched approximation.