ConfigurableEpi¶
ConfigurableEpi does configurable inference and forecasting for compartmental models over data streams. For ConfigurableEpi, a model is a vectorfield for some dynamical model combined with a set of stochastic latent drivers and a link between observable components and data. The inference "engine" is defined as a combination of two choices:
- A filtering approach. This defines how the distribution over the latent state of the model updates conditional on the new data arriving in the stream and the current static hyperparameter estimate.
- A static hyperparameter estimation approach. This defines how a filtering pass, or filtering passes, are used to infer hyperparameters.
Connection to AlgebraicEpiMech¶
At the moment, ConfigurableEpi is designed to work with AlgebraicEpiMech insofar as a petri net representation of the dynamics is the entry point to constructing a valid model. In principle, other petri net or vectorfield functions could be used as an entry point, however, AlgebraicEpiMech comes with an enforced low level naming convention for rate parameters which simplifies model construction.
Engines¶
Three engines are currently supported:
| Filter | Hyperparameters | Engine |
|---|---|---|
UKF() |
Optimise() |
Unscented Kalman filter; hyperparameters by maximising the differentiable marginal log-posterior. |
PF(n_particles) |
LiuWest() |
Bootstrap particle filter; hyperparameters learned online in the particle cloud by the Liu-West kernel. |
EnKF(n_ensemble) |
EKP(...) |
Augmented ensemble Kalman filter; hyperparameters by outer ensemble Kalman inversion. |
Other pairings are currently rejected.
Only the particle filter handles jump drivers such as ArrivalProcess.
The model contract¶
An EpiModel bundles everything an engine needs:
using ConfigurableEpi, AlgebraicEpiMech, Catlab
pn = attach_observation(dom(create_model(OnePopulationTyping(), SEIR())), AtEvent(:transmission); n_stages = 2)
rates(latent, hyper, t) = (transmission_S_I = hyper.gamma * hyper.R0 * latent.Rt / hyper.N,)
vf! = build_petri_vf(pn, rates; defaults = (E_to_I = 1 / 2, I_to_R = 1 / 1.5, O_transmission_1_to_O_transmission_2 = 1 / 6.6))
drivers = (AR1ParamSpec(:Rt; init = positive_gaussian(:Rt, 1.0, 0.05), mu = 1.0, tau = 30.0, sigma = 0.05),)
layout = StateLayout(pn, drivers; signal_names = (:reports,))
stochastic = build_stochastic_update(layout, drivers)
N = 1.0e7
seed = (S = N - 200.0, E = 100.0, I = 100.0, R = 0.0, O_transmission_1 = 0.0, O_transmission_2 = 0.0)
x0 = vcat([seed[n] for n in ode_names(layout)], collect(stochastic.to_unconstrained((Rt = 1.0,))))
model = EpiModel(;
vectorfield! = vf!, layout, stochastic,
observation = (SignalObservationSpec(1, NegBinomialNoise(phi = (l, h, t) -> h.phi); mean_modifier = 0.005),),
hyperparams = (gamma = 1 / 1.5, R0 = 2.0, N = N, phi = 140.0),
priors = (R0 = positive_gaussian(:R0, 2.0, 0.75),),
initial_state = x0, # a vector, or hyperparams -> vector
)
engine = build_inference(UKF(), Optimise(), model; dt = 7.0, n_ahead = 4)
result = fit_forecast!(engine, weekly_counts, 1)
result.quantiles # [horizon, quantile] at DEFAULT_QS
result.fitted_means # fitted observation means over the series
result.summary # (parameter, statistic, value) estimates and diagnostics
- Rates.
build_petri_vf(pn, rates; defaults)gives the vector field.rates(latent, hyperparams, t)returns the time-varying transition rates by name;defaultsfixes the rest. - Drivers.
AR1ParamSpec,RWParamSpecandIntegratedParamSpecare continuous latents, andArrivalProcessis a marked jump process.StateLayoutplaces compartments, observation accumulators and latents in one state vector, andbuild_stochastic_updatebuilds their noise update. - Observation.
SignalObservationSpecreads a signal's accumulator, scales it bymean_modifierand adds noise (NegBinomialNoise,PoissonNoiseand others). Ascertainment trends, day-of-week effects and seasonal forcing plug in here. - Hyperparameters.
hyperparamsholds every fixed value and the starting value of each learned one;priorsnames the learned subset.
Inference engines runs all three engines on simulated data, and Low-level filtering wires the same building blocks into LowLevelParticleFilters directly.
Run configuration¶
A run can be configured from TOML: build_inference(validate_run_config(from_toml(RunConfig, "run.toml")), model) reads the engine pairing and cadence from the file.
n_ahead = 4
step_days = 7
burnin_observations = 12
[io]
data = "triangle.csv"
model_id = "seir"
forecast_df = "forecast.csv"
loc = "ny"
[input.counts]
[filter.pf]
n_particles = 2000
[hyper.liu_west]
discount = 0.97
[epi.basic_seir] # parsed by the model's own @option type
The [filter.<name>] and [hyper.<name>] tables select the engine (ukf, pf, enkf; optimise, liu_west, ekp) and set its options.
[input.counts] or [input.percent] sets the observation scale, and [priors] overrides a model's default priors by name.
Conventions¶
- Latent coefficients live in the state vector in the unconstrained chart of their
initprior and are read back constrained throughstochastic.extract; every observation spec sees them constrained. - Model time
tis days since the first observation. Seasonality and ascertainment are anchored to the calendar once, at build time. - Observations are given on the regular
step_daysgrid, withmissingwhere there is no observation. The filter predicts through a missing slot without correcting, so a reporting gap does not compress time. - A rate function receives
(latent, hyperparams, t); a noise or mean parameter is aRealor a function of the same three arguments.
Source layout¶
src/
ConfigurableEpi.jl module, imports, exports
config.jl TOML-backed RunConfig and the @option types for both axes
model/ everything that defines a model: priors, state layout, latent processes,
dynamics, observation, seasonality, ascertainment, day-of-week effects,
initialisation, radiation mixing, and EpiModel itself
inference/ the shared engine machinery (build_inference / fit_forecast!) and the
UKF, particle filter and ensemble engines
output/ forecast rolls and quantiles, backtest tables, sample output, data linkage
The full list of exports is in the API reference.