Skip to main content

Epimodels

PyPI version Python License: MIT Documentation Status GitHub Actions Workflow Status GitHub stars GitHub forks GitHub issues GitHub last commit Code style: ruff pre-commit EpidBot Ready

Sponsored by
Kwar-AI
AI-powered epidemiological intelligence


Epimodels is a Python library for simulating and fitting mathematical epidemic models. It provides deterministic models in both continuous (ODE-based) and discrete (difference equation) time, along with a comprehensive parameter inference framework, symbolic analysis tools, and multiple ODE solver backends.

Features

  • 27 model classes across continuous, discrete and stochastic (CTMC) families (SIR, SIS, SIRS, SEIR, SEQIAHR, multi-strain, vector-borne, and more)
  • Model registry -- get_model("SIR", family="continuous"), string-based lookup across all families
  • Model fitting -- parameter estimation from observed data with 7 loss functions and 4 optimizers, plus a one-call model.fit(...) sugar API
  • Bayesian inference -- DE-MCMC posterior sampling with normal/Poisson/negative-binomial observation models
  • Uncertainty ensembles -- run many simulations with sampled parameters and get quantile bands
  • Intervention scenarios -- time-bounded parameter changes (lockdowns, vaccination) with scenario comparison
  • Rt estimation -- Cori/EpiEstim-style time-varying reproduction number from incidence data
  • SDE models -- stochastic differential equation versions of any continuous model (JAX backend)
  • Network models -- event-driven SIR/SIS on networkx graphs, adjacency dicts or matrices
  • Model serialization -- save/load model specs (and traces) to JSON/YAML
  • Symbolic analysis -- R0 computation, equilibrium finding, stability analysis, sensitivity analysis
  • Multiple solvers -- scipy (CPU) and diffrax/JAX (GPU) backends with a unified interface
  • Phase space tools -- time delay embedding, mutual information, phase portraits
  • Mermaid diagrams -- auto-generated compartment flow diagrams for every model

Installation

pip install epimodels

Optional extras:

pip install epimodels[plot]       # matplotlib plotting
pip install epimodels[dataframe]  # pandas DataFrame support
pip install epimodels[jax]        # diffrax/JAX GPU solvers

Getting Started

Simulation

from epimodels.continuous.models import SIR

model = SIR()
model([1000, 1, 0], [0, 50], 1001, {'beta': 2, 'gamma': .1})
model.plot_traces()
print(f"R0 = {model.R0}")
print(model.summary())

Parameter Fitting

from epimodels.continuous.models import SIR
from epimodels.fitting import fit_model, Dataset

model = SIR()
dataset = Dataset()
dataset.add_series("I", times=[0, 1, 2, 3, 5, 7, 10], values=[1, 3, 8, 20, 50, 80, 60])

result = fit_model(
    model,
    dataset,
    params={"beta": (0.1, 5.0), "gamma": (0.01, 1.0)},
    initial_conditions=[1000, 1, 0],
    time_range=[0, 10],
)
print(result.best_params)
print(result.fitted_model.summary())

Symbolic Analysis

from epimodels.validation import SymbolicModel

sym = SymbolicModel()
sym.add_parameter("beta", positive=True, real=True)
sym.add_parameter("gamma", positive=True, real=True)
sym.add_variable("S", positive=True)
sym.add_variable("I", positive=True)
sym.add_variable("R", positive=True)
sym.set_total_population("N")

sym.define_ode("S", "-beta*S*I/N")
sym.define_ode("I", "beta*S*I/N - gamma*I")
sym.define_ode("R", "gamma*I")

R0 = sym.compute_R0_next_generation()
print(f"R0 = {R0}")

Available Models

Continuous (ODE)

Model Compartments Key Features
SIR S, I, R Classic susceptible-infectious-removed
SIS S, I No immunity, reinfection
SIRS S, I, R Waning immunity
SEIR S, E, I, R Latent period
SEQIAHR S, E, I, A, H, R, C, D COVID-like with quarantine, hospitalization
Dengue4Strain 49 compartments 4-strain dengue with cross-immunity
SIRSEI 7 compartments Malaria vector-host with climate forcing
SIRSEIData 7 compartments Malaria with real climate data
SEIRS_SEI 7 compartments Vector-borne with deforestation/fire effects
SIR2Strain 10 compartments Two-strain SIR with cross-immunity
SIR1D S, I 1D reduced SIR (beta/gamma tracking)
SISLogistic S, I SIS with logistic population growth
SIRSNonAutonomous S, I, R Time-dependent parameters (callables)
NeipelHeterogeneousSIR I, tau Heterogeneous susceptibility

Discrete (Difference Equations)

Model Compartments Key Features
SIR S, I, R Classic discrete SIR
SIS S, I No immunity
SIRS S, I, R Waning immunity
SEIR S, E, I, R Latent period
SEIS S, E, I Exposed, no immunity
SIpRpS S, I, R Partial immunity
SIpR S, I, R Secondary infections from recovered
SEIpRpS S, E, I, R Exposed + partial immunity
SEIpR S, E, I, R Exposed + secondary infections from R
Influenza 20 compartments Age-structured (4 groups)
SEQIAHR S, E, I, A, H, R, C, D COVID-like discrete version

Solvers

Epimodels supports multiple ODE solvers through a unified interface. You can choose between scipy (CPU-only) and diffrax (JAX-accelerated with GPU support) backends.

Available Solvers

Backend Class Methods Best For
scipy ScipySolver RK45, RK23, DOP853, Radau, BDF, LSODA General use, CPU-bound
diffrax DiffraxSolver Tsit5, Dopri5, Dopri8, Euler, Heun, Midpoint, Ralston GPU acceleration, batch simulations

Usage Examples

from epimodels.continuous import SIR
from epimodels.solvers import ScipySolver, DiffraxSolver

# Default scipy solver (RK45)
model = SIR()
model([999, 1, 0], [0, 100], 1000, {'beta': 0.3, 'gamma': 0.1})

# Explicit scipy solver with specific method
solver = ScipySolver(method='LSODA')
model = SIR()
model([999, 1, 0], [0, 100], 1000, {'beta': 0.3, 'gamma': 0.1}, solver=solver)

# JAX-accelerated solver (requires: pip install diffrax jax)
solver = DiffraxSolver(solver='Tsit5', rtol=1e-6, atol=1e-9)
model = SIR()
model([999, 1, 0], [0, 100], 1000, {'beta': 0.3, 'gamma': 0.1}, solver=solver)

When to Use Each Solver

Scenario Recommended Solver Reason
General use ScipySolver('LSODA') Fast, handles stiffness automatically
High accuracy needed ScipySolver('DOP853') 8th order method
Stiff systems ScipySolver('BDF') or ScipySolver('Radau') Implicit methods
Batch simulations DiffraxSolver('Tsit5') GPU parallelization
Parameter sweeps DiffraxSolver JAX JIT compilation
Quick prototyping Default (RK45) Robust and reliable

Installing Diffrax

For GPU acceleration, install the JAX backend:

# CPU only
pip install diffrax jax

# GPU (CUDA 12)
pip install diffrax "jax[cuda12]"

Model Fitting

The epimodels.fitting module provides parameter estimation from observed epidemiological data.

Loss Functions

Loss Function Best For
SumOfSquaredErrors General purpose
WeightedSSE Variable importance weighting
PoissonLikelihood Count data
NegativeBinomialLikelihood Overdispersed count data
NormalLikelihood Continuous data with noise
HuberLoss Robust to outliers
CustomLoss User-defined objectives

Optimizers

Optimizer Methods Notes
ScipyOptimizer L-BFGS-B, BFGS, Nelder-Mead, Powell, CG, differential_evolution CPU, most methods
JAXOptimizer Adam, SGD, RMSprop GPU-accelerated
NevergradOptimizer Derivative-free No gradients needed
MultiStartOptimizer Multi-start wrapper Avoids local minima

For stochastic epidemic models check EpiStochModels.

Model Registry

Look up models by name across the continuous, discrete and stochastic families:

from epimodels import get_model, list_models

SIR = get_model("SIR", family="continuous")
model = SIR()
print(list_models())

Custom models can be registered with the @register_model decorator from epimodels.registry.

Intervention Scenarios

from epimodels.continuous import SIR
from epimodels.interventions import Intervention, Scenario, ScenarioComparison

model = SIR()
base = Scenario("baseline", model, params={"beta": 2.0, "gamma": 0.5},
                initial_conditions=[999, 1, 0], trange=[0, 100], totpop=1000)
lockdown = Scenario("lockdown", model, params={"beta": 2.0, "gamma": 0.5},
                    initial_conditions=[999, 1, 0], trange=[0, 100], totpop=1000,
                    interventions=[Intervention("beta", start=10, end=40, factor=0.4)])

cmp = ScenarioComparison(base, [lockdown]).run()
print(cmp.peak("I"))
cmp.plot("I")

Uncertainty Ensembles

from epimodels.continuous import SIR
from epimodels.ensembles import simulate_ensemble

model = SIR()
rng = np.random.default_rng(0)
ensemble = simulate_ensemble(
    model, n_sims=200,
    param_sampler=lambda: {"beta": rng.uniform(1.5, 2.5), "gamma": 0.5},
    initial_conditions=[999, 1, 0], trange=[0, 100], totpop=1000,
)
ensemble.quantiles([0.025, 0.5, 0.975])
ensemble.plot_band("I")

Bayesian Inference

# ... or simply, using the sugar API:
result = model.fit(
    {"I": observed_I}, times=times,
    params_to_fit={"beta": (0.1, 5.0), "gamma": (0.01, 1.0)},
    total_population=10000,
    method="bayes", likelihood="poisson",
)
print(result.summary())

Rt Estimation

Model-free, real-time reproduction number estimation from incidence data (Cori et al. 2013, the EpiEstim method):

from epimodels.rt import estimate_rt

result = estimate_rt(incidence, window=7, si_mean=4.0, si_sd=2.0)
print(result.rt_mean)   # posterior mean Rt per window
result.plot()           # Rt with 95% credible band

Stochastic Differential Equations

Add demographic (square-root) noise to any continuous model via diffrax/JAX (pip install epimodels[jax]):

from epimodels.sde import SDEModel

sde = SDEModel(SIR())
sde([999, 1, 0], [0, 100], 1000, {"beta": 2.0, "gamma": 0.5},
    n_sims=50, seed=42)
sde.get_quantiles(0.95)
sde.plot_traces("I")

Network Models

Event-driven SIR/SIS on contact networks (pip install epimodels[network]). Accepts networkx graphs, adjacency dicts or adjacency matrices:

import networkx as nx
from epimodels.network import NetworkSIR

G = nx.barabasi_albert_graph(1000, 3, seed=0)
model = NetworkSIR(G)
model(5, [0, 50], {"beta": 0.3, "gamma": 0.1}, n_sims=20, seed=0)
print(model.final_size().mean())   # mean attack rate
model.plot_traces("I")

Saving and Loading Models

from epimodels.io import save_model, load_model

model.simulate([1000, 1, 0], [0, 50], 1001, {"beta": 2, "gamma": 0.1})
save_model(model, "sir_run.json", include_traces=True)
clone = load_model("sir_run.json")   # registry-based reconstruction

Documentation

Full documentation is available at epimodels.readthedocs.io.

Key Methodological References

The models and algorithms implemented in this library are described in the following works (full bibliography in the docs/references.rst documentation page):

License

MIT License - see LICENSE.txt for details.

Metadata

Release files for epimodels 1.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for epimodels 1.5.0
File Size Uploaded Provenance
epimodels-1.5.0.tar.gz 170.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for epimodels 1.5.0
File Interpreter ABI Platform
epimodels-1.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 315.3 kB

Release files / epimodels-1.5.0.tar.gz

Download URL epimodels-1.5.0.tar.gz
Size 170.4 kB
Tags Source
SHA-256 checksum
How to use checksums
838d7c8672b17bf1086aa10bf6e5f47febc5abc6fc40578235f1cf99e0c8c32f
BLAKE2b-256 checksum
How to use checksums
9b76cc6d596e30f53e3cee53aa841cc935f8101bb9379a548d582876a5e87068
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / epimodels-1.5.0-py3-none-any.whl

Download URL epimodels-1.5.0-py3-none-any.whl
Size 144.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
560d29f1230edd6a340d9a9e88e753c2f6b9a43c9997dd62be97c22401343688
BLAKE2b-256 checksum
How to use checksums
0e7df9da2899cc47df056151b33a89fa6f2c8a82610efe3dab846a96c674c05f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Security for epimodels 1.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for epimodels-1.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page