///|
fn main raise {
// S+I -> 2I, I -> R. Volume N gives propensity beta*S*I/N.
// This is an educational well-mixed process, not a clinical predictor.
let net = @k.Network::new(
["S", "I", "R"],
[
@k.Reaction::new([1, 1, 0], [0, 2, 0], 2.0),
@k.Reaction::new([0, 1, 0], [0, 0, 1], 1.0),
],
volume=100.0,
)
let initial = [95, 5, 0]
let times = @k.regular_grid(8.0, 40)
let path = net.simulate(initial, 8.0, seed=2026, max_events=200)
let ode = net.integrate(net.concentrations(initial), times, max_step=0.002)
let counts = path.count_states()
for k in 0.. 0 {
assert_true(counts[k][0] <= counts[k - 1][0])
assert_true(counts[k][2] >= counts[k - 1][2])
}
}
for row in ode.samples() {
assert_true((row[0] + row[1] + row[2] - 1.0).abs() < 1.0e-12)
}
let events = net.event_counts(path)
let last = path.last_counts()
assert_eq(events[0], 95 - last[0])
assert_eq(events[1], last[2])
let sampled = path.sample(times)
assert_eq(sampled.last(), last.map(n => n.to_double()))
println(
"epidemic: conserved=100 infections=\{events[0]} recoveries=\{events[1]} final_I=\{last[1]} ODE_I=\{100.0*ode.last()[1]}",
)
println(path.to_json().stringify())
}