FA-64961 / Epidemic compartment models / Open access
SIRS waning-immunity simulation: waning destination · case 01
Population shrinks steadily as immunity wanes.
ROOT CAUSE
Waned individuals leave R but are never returned to S.
VERIFIED REPAIR
Restore the waning destination rule: `s += wane - inf`.
Unsuccessful approach: Scaling the waning inflow by S/pop still loses part of the waning flow.
Case contract
Daily forward-Euler SIRS; waning flow is R/immunity_days back to S (immunity_days 0 means permanent immunity); cumulative incidence sums every S->I flow including reinfections; return [S, I, R, cumulative incidence] rounded to 3.
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, immunity_days, pop, i0, days):
s, i, r = float(pop - i0), float(i0), 0.0
cumulative = 0.0
for _ in range(days):
inf = beta * s * i / pop
rec = gamma * i
wane = r / immunity_days if immunity_days > 0 else 0.0
s -= inf
i += inf - rec
r += rec - wane
cumulative += inf
return [round(s, 3), round(i, 3), round(r, 3), round(cumulative, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])]]
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 |
|---|---|---|---|
| regression: seasonal coronavirus | [190.011, 0.436, 160.305, 804.989] | [543.377, 58.225, 398.398, 1339.789] | Failed |
| control: permanent immunity sentinel | [48.675, 7.802, 443.523, 446.325] | [48.675, 7.802, 443.523, 446.325] | Passed |
| regression: short immunity endemic | [82.245, 0.0, 0.17, 709.755] | [333.272, 133.331, 333.396, 3385.33] | Failed |
| control: boundary zero days | [95.0, 5.0, 0.0, 0.0] | [95.0, 5.0, 0.0, 0.0] | Passed |
| regression: no transmission waning only | [50.0, 0.04, 1.61, 0.0] | [98.35, 0.04, 1.61, 0.0] | Failed |
| regression: long immunity single wave | [8.652, 0.045, 1536.0, 1981.348] | [376.102, 3.345, 1620.554, 2084.424] | Failed |
| regression: one day immunity | [111.765, 15.863, 5.335, 185.235] | [188.175, 86.35, 25.475, 420.744] | Failed |
SHA-256 / dd333b110978f6328ea9aeec84b23afedbe198c1c7d754024f39997c66d0eaf2
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, immunity_days, pop, i0, days):
s, i, r = float(pop - i0), float(i0), 0.0
cumulative = 0.0
for _ in range(days):
inf = beta * s * i / pop
rec = gamma * i
wane = r / immunity_days if immunity_days > 0 else 0.0
s += wane * s / pop - inf
i += inf - rec
r += rec - wane
cumulative += inf
return [round(s, 3), round(i, 3), round(r, 3), round(cumulative, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])]]
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 |
|---|---|---|---|
| regression: seasonal coronavirus | [311.309, 2.166, 199.791, 929.698] | [543.377, 58.225, 398.398, 1339.789] | Failed |
| control: permanent immunity sentinel | [48.675, 7.802, 443.523, 446.325] | [48.675, 7.802, 443.523, 446.325] | Passed |
| regression: short immunity endemic | [163.727, 0.002, 0.303, 844.559] | [333.272, 133.331, 333.396, 3385.33] | Failed |
| control: boundary zero days | [95.0, 5.0, 0.0, 0.0] | [95.0, 5.0, 0.0, 0.0] | Passed |
| regression: no transmission waning only | [80.406, 0.04, 1.61, 0.0] | [98.35, 0.04, 1.61, 0.0] | Failed |
| regression: long immunity single wave | [10.792, 0.047, 1539.902, 1986.297] | [376.102, 3.345, 1620.554, 2084.424] | Failed |
| regression: one day immunity | [143.903, 53.642, 16.965, 345.859] | [188.175, 86.35, 25.475, 420.744] | Failed |
SHA-256 / feb4c8d4d0e571b4544a6848345e53da041271e04eed630d36807ecd7becc6c5
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, immunity_days, pop, i0, days):
s, i, r = float(pop - i0), float(i0), 0.0
cumulative = 0.0
for _ in range(days):
inf = beta * s * i / pop
rec = gamma * i
wane = r / immunity_days if immunity_days > 0 else 0.0
s += wane - inf
i += inf - rec
r += rec - wane
cumulative += inf
return [round(s, 3), round(i, 3), round(r, 3), round(cumulative, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('regression: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('control: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: boundary zero days', (0.4, 0.2, 30, 100, 5, 0), [95.0, 5.0, 0.0, 0.0]),
('regression: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('regression: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('regression: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('regression: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])]]
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 |
|---|---|---|---|
| regression: seasonal coronavirus | [543.377, 58.225, 398.398, 1339.789] | [543.377, 58.225, 398.398, 1339.789] | Passed |
| control: permanent immunity sentinel | [48.675, 7.802, 443.523, 446.325] | [48.675, 7.802, 443.523, 446.325] | Passed |
| regression: short immunity endemic | [333.272, 133.331, 333.396, 3385.33] | [333.272, 133.331, 333.396, 3385.33] | Passed |
| control: boundary zero days | [95.0, 5.0, 0.0, 0.0] | [95.0, 5.0, 0.0, 0.0] | Passed |
| regression: no transmission waning only | [98.35, 0.04, 1.61, 0.0] | [98.35, 0.04, 1.61, 0.0] | Passed |
| regression: long immunity single wave | [376.102, 3.345, 1620.554, 2084.424] | [376.102, 3.345, 1620.554, 2084.424] | Passed |
| regression: one day immunity | [188.175, 86.35, 25.475, 420.744] | [188.175, 86.35, 25.475, 420.744] | Passed |
SHA-256 / df8c15c73a7837b2c59733bfecc9f738043adaa0df71d35c354f83b563e05452
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:29.500793+00:00.
Case digest / 77ddd64e4ed17de8039a5aa006bda216a17e2d873ef427dc3be3aff16fb250ae