FA-64861 / Epidemic compartment models / Open access
SIR Euler integrator with daily sampling: initial peak baseline · case 01
A declining subcritical outbreak reports its peak on day 1 instead of at the seed on day 0.
ROOT CAUSE
The running maximum is initialised to zero rather than to the seed prevalence.
VERIFIED REPAIR
Restore the initial peak baseline rule: `peak_i = i / peak_day = 0`.
Unsuccessful approach: Scaling the seed by dt still under-states the day-0 prevalence when substeps > 1.
Case contract
Frequency-dependent SIR integrated with forward Euler at dt=1/substeps; new infections per substep are beta*S*I/pop*dt capped at S; peak prevalence and its day are sampled at day ends (day 0 is the seed); return [S, peak I, peak day, R] rounded to 3 decimals, or None for invalid input.
Why this case matters
Compartmental epidemic calculations drive outbreak forecasts, vaccine targets and hospital planning; a single wrong flow, rate conversion or boundary silently changes every downstream number.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(beta, gamma, pop, i0, days, substeps):
if pop <= 0 or i0 < 0 or i0 > pop or substeps < 1:
return None
dt = 1.0 / substeps
s = float(pop - i0)
i = float(i0)
r = 0.0
peak_i = 0.0
peak_day = 0
for day in range(1, days + 1):
for _ in range(substeps):
new_inf = min(beta * s * i / pop * dt, s)
new_rec = gamma * i * dt
s -= new_inf
i += new_inf - new_rec
r += new_rec
if i > peak_i:
peak_i = i
peak_day = day
return [round(s, 3), round(peak_i, 3), peak_day, round(r, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0])],
[('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0]),
('control: invalid seed above population', (0.5, 0.1, 100, 150, 10, 2), None),
('control: invalid zero substeps', (0.5, 0.1, 100, 1, 10, 0), None),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0]),
('control: invalid seed above population', (0.5, 0.1, 100, 150, 10, 2), None),
('control: invalid zero substeps', (0.5, 0.1, 100, 1, 10, 0), None),
('control: slow recovery long run', (0.25, 0.05, 2000, 2, 90, 3), [19.331, 965.817, 43, 1829.639]),
('control: large city fine steps', (0.4, 0.15, 100000, 20, 50, 8), [13105.196, 25867.476, 37, 75829.968]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: slow recovery long run', (0.25, 0.05, 2000, 2, 90, 3), [19.331, 965.817, 43, 1829.639]),
('control: large city fine steps', (0.4, 0.15, 100000, 20, 50, 8), [13105.196, 25867.476, 37, 75829.968]),
('control: small village', (0.6, 0.25, 50, 1, 30, 2), [5.845, 11.78, 12, 43.206]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control: mild outbreak in 1000 | [85.915, 303.71, 39, 810.961] | [85.915, 303.71, 39, 810.961] | Passed |
| control: fast outbreak daily steps | [2.433, 249.994, 10, 497.014] | [2.433, 249.994, 10, 497.014] | Passed |
| regression: subcritical seed decays | [984.011, 8.828, 1, 15.167] | [984.011, 10.0, 0, 15.167] | Failed |
| control: overshoot-prone large beta single step | [0.0, 75.0, 1, 95.312] | [0.0, 75.0, 1, 95.312] | Passed |
| control: overshoot-prone large beta two steps | [0.0, 156.8, 1, 166.339] | [0.0, 156.8, 1, 166.339] | Passed |
| regression: boundary zero days | [97.0, 0.0, 0, 0.0] | [97.0, 3.0, 0, 0.0] | Failed |
| control: boundary no seed | [100.0, 0.0, 0, 0.0] | [100.0, 0.0, 0, 0.0] | Passed |
SHA-256 / 86e0c0365e89a2a798198cc7c45c453e0bac919abaeac2714c93085baa6eaa44
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(beta, gamma, pop, i0, days, substeps):
if pop <= 0 or i0 < 0 or i0 > pop or substeps < 1:
return None
dt = 1.0 / substeps
s = float(pop - i0)
i = float(i0)
r = 0.0
peak_i = i * dt
peak_day = 0
for day in range(1, days + 1):
for _ in range(substeps):
new_inf = min(beta * s * i / pop * dt, s)
new_rec = gamma * i * dt
s -= new_inf
i += new_inf - new_rec
r += new_rec
if i > peak_i:
peak_i = i
peak_day = day
return [round(s, 3), round(peak_i, 3), peak_day, round(r, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0])],
[('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0]),
('control: invalid seed above population', (0.5, 0.1, 100, 150, 10, 2), None),
('control: invalid zero substeps', (0.5, 0.1, 100, 1, 10, 0), None),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0]),
('control: invalid seed above population', (0.5, 0.1, 100, 150, 10, 2), None),
('control: invalid zero substeps', (0.5, 0.1, 100, 1, 10, 0), None),
('control: slow recovery long run', (0.25, 0.05, 2000, 2, 90, 3), [19.331, 965.817, 43, 1829.639]),
('control: large city fine steps', (0.4, 0.15, 100000, 20, 50, 8), [13105.196, 25867.476, 37, 75829.968]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: slow recovery long run', (0.25, 0.05, 2000, 2, 90, 3), [19.331, 965.817, 43, 1829.639]),
('control: large city fine steps', (0.4, 0.15, 100000, 20, 50, 8), [13105.196, 25867.476, 37, 75829.968]),
('control: small village', (0.6, 0.25, 50, 1, 30, 2), [5.845, 11.78, 12, 43.206]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control: mild outbreak in 1000 | [85.915, 303.71, 39, 810.961] | [85.915, 303.71, 39, 810.961] | Passed |
| control: fast outbreak daily steps | [2.433, 249.994, 10, 497.014] | [2.433, 249.994, 10, 497.014] | Passed |
| regression: subcritical seed decays | [984.011, 8.828, 1, 15.167] | [984.011, 10.0, 0, 15.167] | Failed |
| control: overshoot-prone large beta single step | [0.0, 75.0, 1, 95.312] | [0.0, 75.0, 1, 95.312] | Passed |
| control: overshoot-prone large beta two steps | [0.0, 156.8, 1, 166.339] | [0.0, 156.8, 1, 166.339] | Passed |
| regression: boundary zero days | [97.0, 1.5, 0, 0.0] | [97.0, 3.0, 0, 0.0] | Failed |
| control: boundary no seed | [100.0, 0.0, 0, 0.0] | [100.0, 0.0, 0, 0.0] | Passed |
SHA-256 / d763f9fa995f8c1797fcb03af3f24f5ed8f677425919173a4ac975dedf453dd1
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(beta, gamma, pop, i0, days, substeps):
if pop <= 0 or i0 < 0 or i0 > pop or substeps < 1:
return None
dt = 1.0 / substeps
s = float(pop - i0)
i = float(i0)
r = 0.0
peak_i = i
peak_day = 0
for day in range(1, days + 1):
for _ in range(substeps):
new_inf = min(beta * s * i / pop * dt, s)
new_rec = gamma * i * dt
s -= new_inf
i += new_inf - new_rec
r += new_rec
if i > peak_i:
peak_i = i
peak_day = day
return [round(s, 3), round(peak_i, 3), peak_day, round(r, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0])],
[('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0]),
('control: invalid seed above population', (0.5, 0.1, 100, 150, 10, 2), None),
('control: invalid zero substeps', (0.5, 0.1, 100, 1, 10, 0), None),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: boundary no seed', (0.5, 0.1, 100, 0, 10, 2), [100.0, 0.0, 0, 0.0]),
('control: invalid seed above population', (0.5, 0.1, 100, 150, 10, 2), None),
('control: invalid zero substeps', (0.5, 0.1, 100, 1, 10, 0), None),
('control: slow recovery long run', (0.25, 0.05, 2000, 2, 90, 3), [19.331, 965.817, 43, 1829.639]),
('control: large city fine steps', (0.4, 0.15, 100000, 20, 50, 8), [13105.196, 25867.476, 37, 75829.968]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: slow recovery long run', (0.25, 0.05, 2000, 2, 90, 3), [19.331, 965.817, 43, 1829.639]),
('control: large city fine steps', (0.4, 0.15, 100000, 20, 50, 8), [13105.196, 25867.476, 37, 75829.968]),
('control: small village', (0.6, 0.25, 50, 1, 30, 2), [5.845, 11.78, 12, 43.206]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])],
[('control: mild outbreak in 1000', (0.3, 0.1, 1000, 1, 60, 4), [85.915, 303.71, 39, 810.961]),
('control: fast outbreak daily steps', (0.9, 0.2, 500, 5, 40, 1), [2.433, 249.994, 10, 497.014]),
('regression: subcritical seed decays', (0.08, 0.2, 1000, 10, 20, 2), [984.011, 10.0, 0, 15.167]),
('control: overshoot-prone large beta single step', (3.0, 0.5, 100, 50, 5, 1), [0.0, 75.0, 1, 95.312]),
('control: overshoot-prone large beta two steps', (2.5, 0.3, 200, 80, 6, 2), [0.0, 156.8, 1, 166.339]),
('regression: boundary zero days', (0.5, 0.1, 100, 3, 0, 2), [97.0, 3.0, 0, 0.0]),
('regression: whole population seeded', (0.5, 0.2, 40, 40, 5, 2), [0.0, 40.0, 0, 26.053])]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control: mild outbreak in 1000 | [85.915, 303.71, 39, 810.961] | [85.915, 303.71, 39, 810.961] | Passed |
| control: fast outbreak daily steps | [2.433, 249.994, 10, 497.014] | [2.433, 249.994, 10, 497.014] | Passed |
| regression: subcritical seed decays | [984.011, 10.0, 0, 15.167] | [984.011, 10.0, 0, 15.167] | Passed |
| control: overshoot-prone large beta single step | [0.0, 75.0, 1, 95.312] | [0.0, 75.0, 1, 95.312] | Passed |
| control: overshoot-prone large beta two steps | [0.0, 156.8, 1, 166.339] | [0.0, 156.8, 1, 166.339] | Passed |
| regression: boundary zero days | [97.0, 3.0, 0, 0.0] | [97.0, 3.0, 0, 0.0] | Passed |
| control: boundary no seed | [100.0, 0.0, 0, 0.0] | [100.0, 0.0, 0, 0.0] | Passed |
SHA-256 / 87709d85bff1e1235390c05f94dfd7b7cdaaf4362e205fae79c0abe751547e10
Verification & scope
Deterministic bounded teaching model with a stipulated contract; not a validated scientific or public-health modelling library. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:47:28.799959+00:00.
Case digest / c18775bc7c2015abb52fff0e297816565d832803ba5ff3ce6e7d918678761f89