Building a composable Julia ecosystem for infectious disease modelling

A roadmap, challenges, and questions

Sam Abbott

London School of Hygiene & Tropical Medicine

14 August 2026

Talk plan

  • Bundibugyo virus disease in the Democratic Republic of the Congo
  • The ways we are trying to build it
  • Six years of trying this in R
  • Before and after agents
  • Infrastructure, autodiff, docs, community, governance
  • Please send help!

samabbott.co.uk/JuliaCon2026/roadmap

JuliaCon 2026, Muschel — N3, Friday 14 August 2026, 14:30.

The 2026 Bundibugyo virus disease outbreak in the Democratic Republic of the Congo

A situation report page from the DRC national public health institute, in French, with cumulative confirmed cases, deaths, isolation occupancy, recoveries and suspected cases in boxes.

Posterior densities for cumulative infections, one curve per data stream and one for the joint fit, which sits inside the spread of the single-stream fits.

Posterior reproduction number trajectories over the established outbreak, with most of the mass between 1.5 and 2.

  • A replication of the McCabe et al. report of 18 May, in a few hours the next day
  • Updated up to daily since, and the model has evolved with the outbreak
  • From replicating their model to a renewal process fitted to the suspected, confirmed, death, export and bed-occupancy counts
  • The situation reports are French PDFs. Agents ingest them directly
  • Aiming to follow our infectious disease modelling workflow, and to run in free CI so anyone could use it

BVDOutbreakSize, first commit 2026-05-19 with 33 that day, read 2026-08-13 from the clone at 90df658d · figures from the WHO collaboratory talk of 10 June 2026, fitted to data as of 7 June · situation reports from insp.cd · McCabe et al., Estimation of the size of the outbreak of Ebola disease caused by Bundibugyo virus in DRC, Imperial College London, 18 May 2026, doi:10.25560/130007.

Where does Julia come in?

  • All of it in Julia, with Turing.jl and Mooncake as the autodiff backend
  • Our R and Stan tools were not expressive enough
  • Nothing in Julia for infectious disease modelling that made sense to use, so all of it from scratch. About 35,000 lines
  • The good. Heavy use of submodels makes it easy to follow the workflow
  • The bad. Really quite slow. I cannot get Enzyme to work, and Mooncake compile times are very slow
  • Some of it took a lot of work to run under autodiff at all, the integrals especially

The Julia language logo

The Mooncake.jl logo

About 35,000 lines of Julia across src, test, scripts, ext and docs, counted 2026-08-13 · the Enzyme comparison is BVDOutbreakSize#445, open at 2026-08-12.

epiaware.org

  • Composable tools for infectious disease modelling in Julia
  • Models built from interchangeable parts, not one system rebuilt for each outbreak
  • A specialist contributes the part they know, without holding the whole model in their head
  • Uncertainty carried from the priors through infection dynamics to observation
  • Nowcasting, forecasting and scenarios from one model definition

The EpiAware logo

epiaware.org, read 2026-08-13.

Approach one, ComposableTuringIDModels.jl

  • A library of parts over Turing.jl. Transmission, latent process and observation are separate objects that nest into one model
  • Joint inference and the rest of the Turing toolchain come with it
  • Registered, and the proof of concept the 16:45 talk demonstrates
using ComposableTuringIDModels, Distributions, Turing, Mooncake
using ADTypes: AutoMooncake

renewal = Renewal(;
    generation_time = Gamma(1.4, 1 / 0.38),
    rt = AR(; ϵ_t = HierarchicalNormal(std = HalfNormal(0.1))),
    initialisation = Normal(log(1.0), 1.0))

obs = LatentDelay(                    # delay → ascertainment → noise
    Ascertainment(NegativeBinomialError(),
        Intercept(Normal(log(0.4), 0.1))),
    truncated(Normal(5.0, 2.0), 0.0, Inf))

model = IDModel(renewal, obs)

chain = sample(as_turing_model(model, cases, length(cases)),
    NUTS(0.95; adtype = AutoMooncake(; config = nothing)),
    MCMCThreads(), 100, 2)

Approach two, ComposedDistributions.jl

  • Model the relationships between events with Distributions.jl
  • Those relationships form an event tree
  • Modify the tree, and the distributions in it, for an intervention. Split the tree, compete distributions against each other
  • convolve_series pushes a time series through the tree, and it marginalises across the tree where that is cheaper
  • lower turns a delay into a compartmental generator, over Catalyst, SciMLBase, JumpProcesses and AlgebraicPetri

epiaware.org/approaches/composed-distributions, read 2026-08-13, comments trimmed to fit · ComposedDistributions.jl · LoweredDistributions.jl README, read 2026-08-10.

using ComposedDistributions, Distributions

tree = @uncertain compose((          # parallel pathways, nested
    clinical = sequential(
        :onset_admit => Gamma(Normal(1.2, 0.2), 3.0),
        :admit_resolve => resolve(
            :death => (Gamma(2.0, 3.5), 0.3),
            :discharge => (Gamma(1.0, 8.0), 0.7))),
    surveillance = sequential(
        :onset_notif => Gamma(0.7, 20.0),
        :notif_compete => compete(
            :confirmed => Gamma(2.0, 1.0),
            :lost => Gamma(5.0, 0.5))),))

event_tree(tree)              # the tree,
params_table(tree)            # its parameters,
priors = param_priors(tree)   # and its default priors

case = rand(tree)
logpdf(tree, case)

Some inspiration

Six years of trying this in R and Stan

Package Since Stars CRAN
EpiNow2 2020 141 58,073
epinowcast 2021 67 not on CRAN
epidist 2023 16 not on CRAN
primarycensored 2024 9 11,294
baselinenowcast 2025 10 1,678

Stars and creation dates from the GitHub API, downloads are CRAN totals to 2026-08-11 from cranlogs, retrieved 2026-08-12, and baselinenowcast 2026-08-13.

  • All of them are used by public health departments, including CDC, UKHSA, RKI, ECDC and WHO
  • epinowcast was the attempt at community developed tools
  • In many cases the package is too complex for anyone else to use
  • We see fragmentation, and a lot of small mistakes in outbreak modelling that add up to real biases

Trying, but not in software