Skip to main content
🧠0% 🤖100%

Compartmental (deterministic ODE)

Recommendation

  1. A purely compartmental model runs as an ODE by default. solver='auto' picks the simplest, fastest representation that keeps the model’s mixing and its individual-level structure as written, and prints what it picked and why.
  2. The ODE solution is exact, not approximated with homogeneous agents. This is the largest single change from Starsim 3.
  3. The model text is the same in every paradigm. For a simple model the solver is a free choice: ss.Sim(sir, solver='ode'), ss.Sim(sir, solver='abm').
  4. The textbook model needs no mixing object. With no network given, mixing is homogeneous and beta is the classical transmission rate.
  5. Non-exponential dwell times become Erlang sub-compartments, generated by the library, under auto as well as when an ODE is asked for, with a note when the stages are an approximation.
  6. Print the equations. sir.equations() shows the ODE system with parameter names, so the user can check it against the one in their head.
  7. Build the model as a graph of states and transitions before running it. The library generates the right-hand side from the graph; the user never writes a derivative function.

Why

Starsim 3’s compartmental limit was approximate and one-directional

From the Starsim review, of Starsim 3:

The compartmental limit is approximate, not exact. Starsim can be run in an ODE-like configuration, but it gets there by simulating agents homogeneously, not by solving equations. It cannot produce the deterministic solution, and paradigm conversion is therefore one-directional.

population-and-mixing.md is how this was fixed: homogeneous mixing (the default when no network is given), ss.MixingPool(n_contacts=c), and age-structured mixing pools all have exact ODE forms.

One specification, several solvers, already demonstrated

epipack shows it can be done: the same list of process tuples goes to a numeric ODE integrator, a Gillespie simulator, or a network simulator. Its review: “Whatever else is uncertain about this project, ‘the same model text can be deterministic, stochastic, and network-simulated’ is not.”

camdl does ODE, chain-binomial, and Gillespie from one source. odin does ODE and discrete-time stochastic, with initial() shared and deriv() versus update() selecting the paradigm. EpiModel offers three paradigms with one input vocabulary but three separately written models, which its review calls “the honest baseline this project is trying to beat: same vocabulary and same model text.”

The alternative is several separately written implementations

MEmilio has ode_sir, ode_seir, ode_secir, ode_secirvvs, ode_secirts, ode_seair, ode_seirdb, ode_seirv, ode_mseirs4: nine ODE model classes, plus IDE, LCT, GLCT, SDE, metapopulation, and ABM families. From its review: “MEmilio is the cost estimate for not having a lingua franca… this library is a large, well-funded, multi-year effort, and the paradigm coverage was bought by writing each one separately.”

What the frameworks make you write

camdl needs a transitions block with hand-written total propensities including /N. odin needs deriv() and initial() per compartment. summer2 needs add_infection_frequency_flow versus add_infection_density_flow. EpiModel needs a deSolve derivative function whose returned vector has to match the order of init.dcm(): “an unchecked coupling between two separate function calls, and a good argument for named transitions.” None of this is needed once the model is states and transitions and the mixing supplies the /N.

The model is a graph first

odin sorts the user’s equations by dependency; SimInf resolves a dependency graph; summer2 builds a networkx graph and hands it to JAX; camdl turns the model text into a flat intermediate form. From the summer2 review: “summer2’s networkx DAG, odin’s topological sort, and camdl’s IR are three routes to the same requirement. Whatever the lingua franca’s surface syntax, the model must become a graph before it becomes a computation.” A disease made of ss.State and ss.Transition objects already is that graph, so the right-hand side is generated from it with no separate step.

Dwell times are assumed exponential

An ODE means exponential dwell times, and, per finding 7 of the landscape review, nearly every framework assumes this without saying so. camdl’s via erlang and MEmilio’s entire LCT model family are the explicit alternative, Erlang stages; here they are one keyword (stages=). Staging a dwell time that is neither exponential nor Erlang is what a compartmental modeler would do by hand, and the alternative is that one Weibull turns a two-line model into an agent-based one, so auto does it, with a note.

Equations, not symbolic analysis

epipack gets the ODE system in LaTeX, the Jacobian, the fixed points, and the eigenvalues at the disease-free state because the model is symbolic data. The part every user benefits from is the system itself, printed with parameter names, as a check that the model written is the model meant. A numeric R0 is useful for teaching and few real models need it. Jacobians and fixed points are, as epipack’s own review notes, “tractable for a few compartments and intractable for a stratified model”.

The specification

The disease declaration is in 03-states-and-transitions.md, with dwell times, stages=, equations(), R0(), the transition table, and results that are fractions (I.frac, or a ratio declared on the disease, 09-results.md). What solver='auto' does, the ODE and its integrators, Erlang stages for other dwell times, and the approximations an explicit solver='ode' makes are in 12-backends.md. Homogeneous mixing by default and mixing pools are in 06-routes.md. The docs show how to choose and run a solver in Solvers and backends.

Trade-offs

  • Individual-level networks have no exact ODE form. Under auto a model with ss.RandomNet or ss.SexualNet runs with agents. Asked for explicitly, solver='ode' uses the standard approximation and says so (ss.RandomNet becomes homogeneous mixing at its mean number of contacts, and ss.SexualNet a partner-change model, which needs the model stratified by sex); the result is a coarser model, not the same one. See paradigm-conversion.md. For the exact network result, use agents.
  • Erlang expansion multiplies compartments, and stacked with stratification it multiplies fast. A fitted dwell time gets at most 50 stages, so a very narrow distribution keeps its mean but not its variance; the note gives both.
  • Per-agent continuous heterogeneity has no exact ODE form. rel_sus = ss.lognormal(mean=1, std=0.3) makes the model agent-based under auto. Under an explicit solver='ode' the mean is used, with a one-line note. To keep the heterogeneity in a compartmental run, bin it into a dimension (sir.stratify(rel_sus=[...])); then it is a stratification and nothing is approximated.
  • Defaulting to ODE means a user who wanted stochastic agents has to say so. The printed solver: ode (...) line is what makes this visible, and solver='abm' or n_agents= is the one-keyword fix.
  • Staging a dwell time under auto is an approximation the user did not ask for. It keeps the mean and comes close on the variance, and the note gives both. A result that depends on the tail of the distribution should be run with agents.
  • JAX is a heavy dependency for what is, in the small case, a few lines of RK4. So every ODE runs in kernels in Rust or Numba (ss.solvers.ode(integrator='rk4'), the default, or 'euler'), and JAX, which is optional, runs the same steps only for gradients, the log-density, gradient fits, and many runs at once.

Rejected

  • A separate compartmental model class (MEmilio, EpiModel’s dcm). The paradigm is a choice of solver.
  • Making the user write the derivative function (EpiModel’s new.mod, MetaCast’s subpopulation function). Positional, unchecked, unstratifiable.
  • Requiring /N in the rate expression (camdl, SimInf, flepiMoP, EMULSION). It follows from the mixing.
  • Requiring a mixing object for a well-mixed model. Homogeneous mixing is the default.
  • Agents as the default for every model. Starsim began as agent-based, but a purely compartmental model run with homogeneous agents is slower and noisier than the ODE it stands for. The default is the simplest representation that works.
  • Symbolic analysis as a headline feature (epipack’s Jacobians and fixed points). Useful for textbook models and rarely needed for real ones. Equations are printed; the rest is left out of v1.
  • Fixed model structures (epidemics). A sound choice for its audience, made in the knowledge that “users cannot create new links between compartments”. This project needs models to be open to change.

Open questions

The open questions are collected in 19-open-questions.md.