ODEModel
models.ODEModel(
states,
interventions,
dynamics_and_rewards,
horizon,
discount_rate=0.03,
method='RK45',
rtol=1e-08,
atol=1e-08,
max_step=None,
effect='qaly',
)Ordinary differential equation (compartmental) model engine.
discount_rate is an annual rate discounted continuously, exp(-rate * t), the convention for continuous-time accrual. The horizon is in the same time units as the derivatives (years, by convention).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| states | Sequence[str] |
Compartment labels; their order fixes every array’s axis order. | required |
| interventions | InterventionSpec |
A sequence of intervention names or heormodel.models.Intervention objects, in the order they appear in Outcomes. An Intervention may carry parameter decision levers merged into params for that intervention. |
required |
| dynamics_and_rewards | Callable[[pd.Series, str], ODESpec] |
fn(params, intervention) -> ODESpec returning the system and reward arrays for one intervention under one parameter set. params is a draw-matrix row (a pandas.Series); intervention is the intervention name. |
required |
| horizon | float |
Length of the analytic time horizon, in years. | required |
| discount_rate | float |
Annual discount rate for costs and effects (0.03 by default), applied continuously. | 0.03 |
| method | str |
Integration method passed to scipy.integrate.solve_ivp ("RK45" by default; "LSODA" switches automatically to a stiff solver when the dynamics stiffen). |
'RK45' |
| rtol | float |
Relative error tolerance for the integrator. | 1e-08 |
| atol | float |
Absolute error tolerance for the integrator. | 1e-08 |
| max_step | float | None |
Largest step the integrator may take, in years; None (the default) lets the solver choose. Set it to force the solver through a short-lived feature, such as a narrow epidemic peak, it might otherwise step over. |
None |
| effect | str |
Name of the primary effect column (quality-adjusted life-years by default). | 'qaly' |
Example
import numpy as np, pandas as pd from heormodel.models.ode import ODEModel, ODESpec def dynamics_and_rewards(params, intervention): … k = params[“decay”] … return ODESpec( … derivatives=lambda t, y: np.array([-k * y[0], k * y[0]]), … initial=np.array([1.0, 0.0]), … state_cost=np.array([params[“cost”], 0.0]), … state_effect=np.array([1.0, 0.0])) engine = ODEModel( … states=(“alive”, “dead”), interventions=(“care”,), … dynamics_and_rewards=dynamics_and_rewards, horizon=10.0) draws = pd.DataFrame({“decay”: [0.1], “cost”: [1000.0]}, … index=pd.RangeIndex(1, name=“iteration”)) engine.evaluate(draws).interventions [‘care’]
Methods
| Name | Description |
|---|---|
| evaluate | Evaluate every intervention on every draw and return Outcomes. |
| trajectory | Compartment occupancy over the horizon for one parameter set. |
evaluate
models.ODEModel.evaluate(draws)Evaluate every intervention on every draw and return Outcomes.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| draws | pd.DataFrame | Parameter draw matrix (rows = iterations). Its index becomes the outcome iteration index. | required |
Returns
| Name | Type | Description |
|---|---|---|
| Outcomes | Outcomes indexed by (intervention, draws.index). |
trajectory
models.ODEModel.trajectory(params, intervention, *, n_points=200)Compartment occupancy over the horizon for one parameter set.
A convenience for inspection and plotting (an epidemic curve, a vaccination-coverage path); it is not part of the engine contract and does not accrue costs or effects. evaluate is what produces Outcomes.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| params | pd.Series | One draw-matrix row (a pandas.Series) of parameter values. |
required |
| intervention | str |
The intervention name passed to dynamics_and_rewards. |
required |
| n_points | int |
Number of evenly spaced time points returned over [0, horizon]. |
200 |
Returns
| Name | Type | Description |
|---|---|---|
| pd.DataFrame | A DataFrame with a time column and one column per compartment. |
Example
import numpy as np, pandas as pd from heormodel.models.ode import ODEModel, ODESpec def dynamics_and_rewards(params, intervention): … return ODESpec( … derivatives=lambda t, y: np.array([-0.1 * y[0], 0.1 * y[0]]), … initial=np.array([1.0, 0.0]), … state_cost=np.zeros(2), state_effect=np.array([1.0, 0.0])) engine = ODEModel(states=(“a”, “b”), interventions=(“s”,), … dynamics_and_rewards=dynamics_and_rewards, horizon=10.0) traj = engine.trajectory(pd.Series(dtype=float), “s”, n_points=3) list(traj.columns) [‘time’, ‘a’, ‘b’]