Neipel Heterogeneous SIR Model Example

This example compares the classical SIR model with the Neipel heterogeneous SIR model. The Neipel model accounts for population heterogeneity in individual susceptibility using a gamma distribution shape parameter alpha.

  • Large ``alpha``: Model approaches homogeneous (classical) SIR

  • Small ``alpha``: Strong heterogeneity in susceptibility

Import Libraries

[1]:
import matplotlib.pyplot as plt
import numpy as np
from epimodels.continuous.models import SIR, NeipelHeterogeneousSIR

Define Parameters

[2]:
# Population parameters
N = 100000      # Total population
I0 = 10         # Initial infected
R0_init = 0     # Initial recovered
S0 = N - I0 - R0_init  # Initial susceptible

# Epidemiological parameters
beta = 0.30     # Transmission rate
gamma = 0.10    # Recovery rate

# Time range
t0 = 0
tf = 160

# Heterogeneity parameters to test
alphas = [10.0, 1.0, 0.3, 0.1]  # Large to small alpha

Run Classical SIR Model

[3]:
# Initialize and run classical SIR
sir = SIR()
sir(
    inits=[S0, I0, R0_init],
    trange=[t0, tf],
    totpop=N,
    params={"beta": beta, "gamma": gamma},
)

# Extract results
t_sir = np.array(sir.traces["time"])
S_sir = np.array(sir.traces["S"])
I_sir = np.array(sir.traces["I"])
R_sir = np.array(sir.traces["R"])

print(f"Classical SIR: R0 = {beta/gamma:.2f}")
Classical SIR: R0 = 3.00

Run Neipel Heterogeneous SIR Model

The Neipel model uses state variables [I, tau]. We reconstruct S and R using:

  • S(t) = (N - I0) * (1 + tau/alpha)^(-alpha)

  • R(t) = N - S - I

[4]:
def reconstruct_neipel_states(I, tau, N, I0, alpha):
    """Reconstruct S and R from Neipel model outputs."""
    S = (N - I0) * (1 + tau / alpha) ** (-alpha)
    R = N - S - I
    return S, R

# Store results for different alpha values
neipel_results = []

for alpha in alphas:
    # Initialize and run Neipel model
    neipel = NeipelHeterogeneousSIR()
    neipel(
        inits=[I0, 0],  # Initial I and tau
        trange=[t0, tf],
        totpop=N,
        params={"beta": beta, "gamma": gamma, "alpha": alpha, "I0": I0},
    )

    # Extract and reconstruct results
    t = np.array(neipel.traces["time"])
    I = np.array(neipel.traces["I"])
    tau = np.array(neipel.traces["tau"])
    S, R = reconstruct_neipel_states(I, tau, N, I0, alpha)

    neipel_results.append({"alpha": alpha, "t": t, "S": S, "I": I, "R": R})
    print(f"Neipel alpha={alpha}: Peak I = {np.max(I):.0f}, Final R = {R[-1]:.0f}")
Neipel alpha=10.0: Peak I = 25457, Final R = 91092
Neipel alpha=1.0: Peak I = 17382, Final R = 66425
Neipel alpha=0.3: Peak I = 9089, Final R = 37208
Neipel alpha=0.1: Peak I = 3766, Final R = 16175

Plot Comparison: Infected

[5]:
plt.figure(figsize=(10, 5))

# Classical SIR
plt.plot(t_sir, I_sir, label="Classical SIR", linewidth=2.5, color="black")

# Neipel for different alpha values
for res in neipel_results:
    plt.plot(res["t"], res["I"], label=f"Neipel α={res['alpha']}")

plt.xlabel("Time")
plt.ylabel("Infected Individuals")
plt.title("Infected: Classical SIR vs Neipel Heterogeneous SIR")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
../_images/Examples_neipel_heterogeneous_sir_example_10_0.png

Plot Comparison: Susceptible

[6]:
plt.figure(figsize=(10, 5))

plt.plot(t_sir, S_sir, label="Classical SIR", linewidth=2.5, color="black")

for res in neipel_results:
    plt.plot(res["t"], res["S"], label=f"Neipel α={res['alpha']}")

plt.xlabel("Time")
plt.ylabel("Susceptible Individuals")
plt.title("Susceptible: Classical SIR vs Neipel Heterogeneous SIR")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
../_images/Examples_neipel_heterogeneous_sir_example_12_0.png

Plot Comparison: Recovered

[7]:
plt.figure(figsize=(10, 5))

plt.plot(t_sir, R_sir, label="Classical SIR", linewidth=2.5, color="black")

for res in neipel_results:
    plt.plot(res["t"], res["R"], label=f"Neipel α={res['alpha']}")

plt.xlabel("Time")
plt.ylabel("Recovered Individuals")
plt.title("Recovered: Classical SIR vs Neipel Heterogeneous SIR")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
../_images/Examples_neipel_heterogeneous_sir_example_14_0.png

Summary

Key observations:

  • Smaller ``alpha`` (more heterogeneity) leads to:

    • Lower epidemic peak

    • Smaller final epidemic size

    • Faster depletion of susceptible population

  • Classical SIR overestimates both peak infections and final size when population heterogeneity exists