Branching Processes

A branching process models a population in which each individual independently produces a random number of offspring drawn from a fixed offspring distribution. The classic version is the Galton–Watson process, and it answers a sharp question: starting from a single ancestor, does the lineage die out or grow without bound?

The Galton–Watson process

Label the generations 0,1,2,0, 1, 2, \dots, starting with one individual in generation 00. Each individual, independently, leaves a random number of offspring XX with probability mass function pk=P(X=k)p_k = P(X=k). Let ZnZ_n be the population size in generation nn, so Z0=1Z_0 = 1 and each individual in generation nn founds the next generation.

The offspring distribution has a mean, the expected number of children per individual, m=E[X]=k0kpk.m = \mathbb{E}[X] = \sum_{k\ge 0} k\,p_k. This single number controls the qualitative fate of the process.

Expected growth

Because individuals reproduce independently, expectations multiply across generations. Conditioning on the previous generation gives E[ZnZn1]=mZn1\mathbb{E}[Z_{n}\mid Z_{n-1}] = m\,Z_{n-1}, and taking expectations again yields E[Zn]=mn.\mathbb{E}[Z_n] = m^{\,n}. The expected population size grows or shrinks geometrically at rate mm.

In the epidemic reading, one individual is one infection and its “offspring” are the people it infects, so mm is exactly the basic reproduction number R0R_0. The mean chain then grows like R0nR_0^{\,n}, the same quantity that the next-generation matrix computes for structured populations and that drives the early exponential phase of an SIR outbreak.

The probability generating function

The natural bookkeeping tool for offspring counts is the probability generating function (PGF) G(s)=E[sX]=k0pksk,0s1.G(s) = \mathbb{E}[s^{X}] = \sum_{k\ge 0} p_k s^{k}, \qquad 0\le s\le 1. It packages the whole offspring distribution into one function, much as the moment generating function does for continuous variables. Its derivative at s=1s=1 recovers the mean, G(1)=mG'(1) = m, and composing GG with itself tracks successive generations.

Extinction

Let qq be the probability of ultimate extinction — that the lineage eventually reaches size 00. Extinction of the whole process happens exactly when each of the first generation’s sub-lineages goes extinct, and those are independent copies of the original. This self-similarity gives the fixed-point equation q=G(q).q = G(q). The extinction probability is the smallest solution of s=G(s)s = G(s) in the interval [0,1][0,1].

Classifying by the mean mm:

The gap between the critical case and genuine growth is why small populations can vanish by chance even when conditions favor increase — the same stochastic fragility seen in genetic drift.

Outbreaks: will an introduction take off?

Branching processes describe the early, stochastic phase of an outbreak, when the susceptible pool is still effectively unlimited. A single imported case starts one lineage of infections; either it fizzles out or it seeds a major outbreak.

A common and tractable choice is a Poisson offspring distribution with mean R0R_0 (see the Poisson distribution), whose PGF is G(s)=eR0(s1)G(s) = e^{R_0(s-1)}. The extinction probability solves s=eR0(s1),s = e^{R_0(s-1)}, and the probability of a major outbreak is 1q1-q. When R01R_0 \le 1 every introduction eventually dies out; only R0>1R_0>1 gives a genuine chance of takeoff.

Worked example

Take a Poisson offspring distribution with R0=2R_0 = 2, so we solve s=e2(s1).s = e^{2(s-1)}. One root is s=1s=1, but we want the smallest root in [0,1][0,1]. Iterating sk+1=e2(sk1)s_{k+1} = e^{2(s_k-1)} from s0=0s_0 = 0 gives s1=e20.135s_1 = e^{-2}\approx 0.135, then 0.1760.176, 0.1940.194, 0.2000.200, converging to q0.203.q \approx 0.203. So the probability of extinction from a single introduction is about 0.2030.203, and the probability of a major outbreak is 1q0.797.1-q \approx 0.797. Even with R0=2R_0=2, roughly one in five introductions fizzles out purely by chance.

In code

R

# Extinction probability by fixed-point iteration: s = G(s)
G <- function(s, R0 = 2) exp(R0 * (s - 1))   # Poisson offspring PGF
q <- 0
for (i in 1:100) q <- G(q)
q            # ~ 0.2032
1 - q        # ~ 0.7968  (major-outbreak probability)

# Simulate branching trees and estimate extinction empirically
set.seed(1)
sim_extinct <- function(R0 = 2, gens = 40) {
  z <- 1
  for (g in 1:gens) {
    z <- sum(rpois(z, R0))   # each individual has Poisson(R0) offspring
    if (z == 0) return(TRUE) # extinct
  }
  FALSE                      # still alive -> treat as survival
}
mean(replicate(10000, sim_extinct()))  # ~ 0.20, matches q

Python

import numpy as np

def G(s, R0=2.0):        # Poisson offspring PGF
    return np.exp(R0 * (s - 1))

q = 0.0
for _ in range(100):
    q = G(q)
print(q, 1 - q)          # ~ 0.2032  0.7968

rng = np.random.default_rng(1)
def sim_extinct(R0=2.0, gens=40):
    z = 1
    for _ in range(gens):
        z = rng.poisson(R0, size=z).sum()  # offspring of current generation
        if z == 0:
            return True
    return False
print(np.mean([sim_extinct() for _ in range(10000)]))  # ~ 0.20

Julia

using Distributions, Statistics, Random

G(s; R0=2.0) = exp(R0 * (s - 1))     # Poisson offspring PGF
q = 0.0
for _ in 1:100
    q = G(q)
end
println((q, 1 - q))                  # ~ (0.2032, 0.7968)

Random.seed!(1)
function sim_extinct(; R0=2.0, gens=40)
    z = 1
    for _ in 1:gens
        z = sum(rand(Poisson(R0), z))
        z == 0 && return true
    end
    false
end
println(mean(sim_extinct() for _ in 1:10000))  # ~ 0.20

Evolutionary emergence: the Antia–Bergstrom model

A single number R0R_0 hides a subtler question for a newly introduced pathogen: what if the strain that spills over is poorly adapted to the new host (R0<1R_0 < 1) but can evolve as it spreads? Antia, Regoes, Koella & Bergstrom (2003) answered this with a multi-type branching process, and it is a beautiful application of everything above.

Model each case’s secondary infections as a binomial offspring distribution: an infected host contacts nn others and infects each independently with probability pp, so offspring Binomial(n,p)\sim \text{Binomial}(n, p) with mean R=npR = np and PGF G(s)=(1p+ps)nG(s) = (1 - p + p s)^{n}. The introduced wildtype is subcritical, Rw=npw<1R_w = n p_w < 1, so on its own it always dies out. But at each transmission the pathogen mutates with small probability μ\mu to an adapted mutant with Rm=npm>1R_m = n p_m > 1. Emergence is the event that an adapted lineage establishes before the wildtype chain burns out.

Two branching-process facts combine to give the answer. First, a single mutant establishes with probability π=1qm\pi = 1 - q_m, where qmq_m is the extinction probability of the mutant process — the smallest root of q=(1pm+pmq)nq = (1 - p_m + p_m q)^{n}. Second, a subcritical wildtype outbreak seeded by one case produces, in expectation, Rw1Rw\dfrac{R_w}{1 - R_w} secondary infections in total (the geometric sum of RwkR_w^{\,k}). Each of those transmissions throws off a mutant with probability μ\mu, so the expected number of established mutant lineages is μRw1Rwπ\mu\,\dfrac{R_w}{1-R_w}\,\pi, and — treating rare mutant lineages as independent — the probability of emergence is

Pemerge    1exp ⁣(μRw1Rwπ).P_{\text{emerge}} \;\approx\; 1 - \exp\!\left(-\,\mu\,\frac{R_w}{1-R_w}\,\pi\right).

The headline result is in the factor Rw1Rw\dfrac{R_w}{1-R_w}: as Rw1R_w \to 1^{-} it blows up, so a pathogen that almost spreads lingers long enough to give evolution many attempts at rescue. A spillover strain with R0R_0 just below 11 is therefore far more dangerous than its subcritical label suggests — the central public-health message of the paper.

Worked example

Take n=10n = 10 contacts, wildtype pw=0.09p_w = 0.09 (so Rw=0.9R_w = 0.9), mutant pm=0.15p_m = 0.15 (so Rm=1.5R_m = 1.5), and mutation probability μ=103\mu = 10^{-3}. Solving q=(0.85+0.15q)10q = (0.85 + 0.15\,q)^{10} gives qm0.37q_m \approx 0.37, so a mutant establishes with probability π0.63\pi \approx 0.63. The wildtype throws off Rw/(1Rw)=0.9/0.1=9R_w/(1-R_w) = 0.9/0.1 = 9 secondary infections on average, so Pemerge1e10390.630.0057P_{\text{emerge}} \approx 1 - e^{-10^{-3}\cdot 9 \cdot 0.63} \approx 0.0057. Push the wildtype closer to threshold, Rw=0.99R_w = 0.99: now it throws off 9999 infections and Pemerge1e0.06240.061P_{\text{emerge}} \approx 1 - e^{-0.0624} \approx 0.061 — a tenfold jump in emergence risk from the same mutation rate.

n <- 10; pw <- 0.09; pm <- 0.15; mu <- 1e-3
Rw <- n * pw; Rm <- n * pm                 # 0.9 (subcritical), 1.5 (supercritical)

# Mutant establishment probability: smallest root of q = (1 - pm + pm q)^n
q <- 0; for (i in 1:1000) q <- (1 - pm + pm * q)^n
pi_est <- 1 - q                            # ~0.63

# Rare-mutation emergence probability
P_emerge <- 1 - exp(-mu * Rw / (1 - Rw) * pi_est)   # ~0.0057
c(pi_est = pi_est, P_emerge = P_emerge)

# Monte Carlo check: two-type binomial branching process
set.seed(1)
establishes <- function(p, gens = 60) {        # does one mutant lineage survive?
  z <- 1
  for (g in 1:gens) { z <- sum(rbinom(z, n, p)); if (z == 0) return(FALSE) }
  TRUE
}
emerge_once <- function() {
  z <- 1
  for (g in 1:300) {
    if (z == 0) return(FALSE)                   # wildtype burned out, no emergence
    kids <- sum(rbinom(z, n, pw))               # wildtype secondary infections
    n_mut <- rbinom(1, kids, mu)                # how many mutated
    if (n_mut > 0 && any(replicate(n_mut, establishes(pm)))) return(TRUE)
    z <- kids - n_mut
  }
  FALSE
}
mean(replicate(20000, emerge_once()))           # ~0.006, matches the formula
import numpy as np
n, pw, pm, mu = 10, 0.09, 0.15, 1e-3
Rw = n * pw
q = 0.0
for _ in range(1000):
    q = (1 - pm + pm * q) ** n        # mutant extinction prob
pi_est = 1 - q                        # ~0.63
P_emerge = 1 - np.exp(-mu * Rw / (1 - Rw) * pi_est)   # ~0.0057
print(pi_est, P_emerge)               # the R Monte-Carlo check translates directly
0.6284894704844379 0.005640437894373074
n, pw, pm, mu = 10, 0.09, 0.15, 1e-3
Rw = n * pw
q = 0.0
for _ in 1:1000
    q = (1 - pm + pm * q)^n           # mutant extinction prob
end
pi_est = 1 - q                        # ~0.63
P_emerge = 1 - exp(-mu * Rw / (1 - Rw) * pi_est)   # ~0.0057
println((pi_est, P_emerge))

Why it matters

Branching processes turn a vague worry — “could this spread?” — into a precise probability, separating the deterministic message of R0R_0 from the luck of small numbers. They explain why an outbreak with R0>1R_0>1 can still fail to establish, why small populations wink out despite favorable growth, and how genealogies and family names go extinct. The same machinery underlies nuclear chain reactions, PCR amplification, surname survival, and the founding dynamics of new mutations.