EMULSION
| Language | YAML DSL; engine in Python |
| Paradigms | Compartmental, individual-based (IBM), hybrid, and metapopulation: multi-level and multi-scale |
| Specification style | A single human-readable YAML document. No code required |
| Version reviewed | 1.2.1 |
| License | Apache-2.0 |
| Code | https://forgemia.inra.fr/dynamo/software/emulsion-public |
| Docs | Ships with the repository; quickstart and feature model libraries |
| Paper | Picault et al. (2019), PLOS Comp Biol 15(9): e1007342, 10.1371/journal.pcbi.1007342 |
| Moving to Starsim | Conversion guide |
What it’s for
EMULSION is a modeling framework for veterinary and plant epidemiology in which the model is a document, not a program. Its stated motivation is communication: models should be “explicit, intelligible and revisable”, so that modelers can discuss them with biologists, veterinarians, and economists throughout the work.
For this project, it is the most important framework in the review after camdl and Starsim, for the reason below.
The finding
EMULSION got there first. Compare two of its example models:
# compart_SIR.yaml
levels:
population:
desc: 'level of the population'
aggregation_type: 'compartment'
contains:
- individuals
individuals:
desc: 'level of individuals'# IBM_SIR.yaml
levels:
population:
desc: 'level of the population'
aggregation_type: 'IBM'
contains:
- individuals
individuals:
desc: 'level of the individuals'The rest of both files (the state machine, the transitions, the parameters, the initial conditions, the outputs) is identical. A one-word change switches the model between a stochastic compartmental model and an individual-based model. A third value, hybrid, gives a mixed formulation where some processes are tracked individually and others in aggregate.
This is the goal of paradigm independence, achieved in an Apache-2.0 package published in 2019. Design work here should start from what EMULSION can and cannot express under that switch, because that boundary is the real research question.
How a model is specified
One YAML document with named top-level sections:
model_name: IBM_SIR
time_info:
time_unit: 'days'
delta_t: 1
origin: 'January 1'
total_duration: '100'
levels:
population:
aggregation_type: 'IBM'
contains: [individuals]
individuals:
desc: 'level of the individuals'
processes:
individuals:
- health_state # names a state machine
state_machines:
health_state:
states:
- S: {name: 'Susceptible', fillcolor: 'wheat'}
- I: {name: 'Infectious', fillcolor: 'maroon'}
- R: {name: 'Resistant', fillcolor: 'deepskyblue'}
transitions:
- {from: S, to: I, rate: 'force_of_infection'}
- {from: I, to: R, rate: 'recovery'}
parameters:
transmission_I:
desc: 'transmission rate from infectious individuals (/day)'
value: 0.5
recovery:
desc: 'recovery rate (/day)'
value: 0.1
force_of_infection:
desc: 'infection function'
value: 'transmission_I * total_I / total_population'
source: 'classical function assuming frequency dependence'
prototypes:
individuals:
- healthy: {health_state: S}
- infected: {health_state: I}
initial_conditions:
population:
- {prototype: healthy, amount: 'initial_population_size - initial_infected'}
- {prototype: infected, amount: 'initial_infected'}
outputs:
type: csv
population:
period: 1
extra_vars: [percentage_prevalence, total_population]Every element carries a desc:, and parameters may carry a source: naming the assumption or citation behind the value. Documentation is part of the model, not adjacent to it.
Core abstractions
| Concept | EMULSION name | Notes |
|---|---|---|
| Organization level | levels: with aggregation_type and contains |
compartment, IBM, hybrid, metapopulation; levels nest (individual → herd → metapopulation) |
| Process | processes: per level |
Names the state machines (or grouped processes) that run at that level |
| State machine | state_machines: |
States and transitions: the core model structure |
| Transition | {from:, to:, rate:} or {... proba:} or {... duration:} |
Three ways to specify timing, converted to the time step automatically |
| Parameter | name with value, desc, source |
value may be a number or an expression over other parameters and aggregates |
| Aggregate | total_I, total_population |
Automatically available names for state totals |
| Prototype | prototypes: |
Named bundles of initial attribute values: a template for creating individuals |
| Initial condition | initial_conditions: |
Prototype + amount, where amount is an expression |
| Output | outputs: with period and extra_vars |
Declarative |
| Code add-on | optional Python module | The escape hatch for behavior the YAML cannot express |
Time
time_info declares time_unit, delta_t, origin (a calendar date), and total_duration. Transitions may be specified as a rate, a probability, or a duration, and the engine converts:
Rates are automatically converted into probabilities w.r.t the duration of the time step (delta_t), assuming a classical exponential distribution of durations in the states.
Starsim later encoded the same three-way distinction as per / prob / dur. Here they are three alternative keys on a transition. They are not types (a rate is still a number, and its unit is in the desc: string), but the distinction is in the model, and the engine does the conversion.
Multi-level and multi-scale
levels with contains builds a hierarchy: individuals inside herds inside a metapopulation, each with its own aggregation_type and processes. A herd-level process (culling, movement, testing) acts on herds, an individual-level process (infection, recovery) acts on individuals, and the engine reconciles them.
No other framework in the review does this. Starsim, EpiModel, and epiworld have one population level, and epymorph has nodes and aggregated individuals. EMULSION lets each level choose its own paradigm, which is what makes the hybrid mode possible.
Strengths
- Paradigm as a declared property of a level.
aggregation_type: compartment | IBM | hybridon an otherwise identical model. This proves in practice that the project’s central idea can work. - The model is a document. YAML that people can read, compare between versions, and review without programming. Transitions, the state machine, and initial conditions are all data.
- Several levels, each with its own paradigm. Individual → herd → metapopulation.
- Three ways to give timing on a transition: rate, probability, or duration, converted by the engine.
- Documentation in the model:
desc:on everything, andsource:on parameters recording where a value came from. - Prototypes: named bundles of initial attributes, a neat way to say “create 100 healthy individuals and 1 infected one”.
- Parameters as expressions over other parameters and automatic totals (
total_I,total_population), so the force of infection is visible and not hidden in the engine. - Models can be drawn:
fillcoloron states is there because the engine draws the model. - A stated communication goal, and a design that follows from it.
Limitations
- YAML makes checking hard. Checks stop at the file format, expressions sit in strings (
value: 'transmission_I * total_I / total_population'), there is no editor support or type checking, and errors come from a YAML parser. A dedicated language, like camdl’s, avoids these at the cost of readers having to learn it. - No dimensional types. A rate’s units are in its
desc:string. - The force of infection is written by hand.
transmission_I * total_I / total_populationis correct here, but nothing checks it, and nothing knows that this transition is an infection. camdl catches this kind of missing-/Nerror. - Totals appear without being declared, such as
total_Iandtotal_population. - Code add-ons handle what YAML cannot, and a model using them is no longer only a document.
- Small community, on INRAE’s GitLab, mainly in veterinary and plant epidemiology, so it is less known outside that field.
- The paradigm switch has undocumented limits. The examples show
compartment/IBM/hybridon the same simple SIR. What happens with individual attributes that have no compartmental counterpart is not stated in the material reviewed, and this project needs that answered.
Implications for the lingua franca
- Start by mapping where EMULSION’s paradigm switch stops. Take its feature models, try each under
compartment/IBM/hybrid, and record where the switch stops working and why. That boundary shows how far paradigm independence can go, and EMULSION provides the means of measuring it. - Paradigm belongs to a level, not to a model. The choice is per level and hierarchical: individuals may be aggregated while herds are explicit, or the reverse. That says more than one flag per model, and matches how hybrid models are built. The lingua franca should let each level declare how it is aggregated.
- Rate, probability, and duration are the right three ways to give timing. They match how epidemiologists hold their parameters (a hazard, a per-step probability, a mean duration), and they convert mechanically. Starsim’s types are the rigorous form, and EMULSION’s keys the readable one. The lingua franca wants both:
rate/prob/duras typed alternatives on a transition. - Keep sources in the model.
source:on a parameter (“classical function assuming frequency dependence”) is one line that answers every reviewer’s question. With camdl’s#'comments and@symbolannotations, this suggests a standard place for why this value, as well as what value. - Prototypes are a good way to set initial conditions. Naming a bundle of attributes and saying how many of each to create is neater than a compartment vector (which cannot express correlated attributes) or per-agent code.
- Do not build on YAML. EMULSION shows the value of a model as a document, and also the cost of using a data format as the language. The lingua franca should be as readable as EMULSION’s YAML and as checkable as camdl, which means its own language with a standard data form underneath.