FA-64956 / Epidemic compartment models / Open access
SIRS waning-immunity simulation: permanent immunity sentinel · case 01
Declaring permanent immunity (0) makes every recovered person susceptible again the next day.
ROOT CAUSE
The 0 sentinel falls through to instantaneous waning of the whole R class.
VERIFIED REPAIR
Restore the permanent immunity sentinel rule: `if immunity_days > 0 else 0.0`.
Unsuccessful approach: Substituting a one-year duration still wanes immunity that should be permanent.
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 r
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 = [[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: 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 |
|---|---|---|---|
| control: seasonal coronavirus | [543.377, 58.225, 398.398, 1339.789] | [543.377, 58.225, 398.398, 1339.789] | Passed |
| regression: permanent immunity sentinel | [200.02, 249.985, 49.995, 1528.612] | [48.675, 7.802, 443.523, 446.325] | Failed |
| control: 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 |
| control: no transmission waning only | [98.35, 0.04, 1.61, 0.0] | [98.35, 0.04, 1.61, 0.0] | Passed |
| control: long immunity single wave | [376.102, 3.345, 1620.554, 2084.424] | [376.102, 3.345, 1620.554, 2084.424] | Passed |
| control: one day immunity | [188.175, 86.35, 25.475, 420.744] | [188.175, 86.35, 25.475, 420.744] | Passed |
SHA-256 / f3e858b9d0498694abfd2f3c70fd688ecc557eec4ccbeeb6b204e4a6799a5935
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 r / 365.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 = [[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: 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 |
|---|---|---|---|
| control: seasonal coronavirus | [543.377, 58.225, 398.398, 1339.789] | [543.377, 58.225, 398.398, 1339.789] | Passed |
| regression: permanent immunity sentinel | [63.594, 9.219, 427.187, 454.955] | [48.675, 7.802, 443.523, 446.325] | Failed |
| control: 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 |
| control: no transmission waning only | [98.35, 0.04, 1.61, 0.0] | [98.35, 0.04, 1.61, 0.0] | Passed |
| control: long immunity single wave | [376.102, 3.345, 1620.554, 2084.424] | [376.102, 3.345, 1620.554, 2084.424] | Passed |
| control: one day immunity | [188.175, 86.35, 25.475, 420.744] | [188.175, 86.35, 25.475, 420.744] | Passed |
SHA-256 / d35db3fec2220d8c4a7f3b1e477d9bb6ee5b10d9ef6e7df642cabc14b372c029
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 = [[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: 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]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: two week run', (0.9, 0.3, 20, 400, 4, 14), [58.25, 94.786, 246.964, 386.248])],
[('control: seasonal coronavirus', (0.4, 0.2, 30, 1000, 5, 80), [543.377, 58.225, 398.398, 1339.789]),
('regression: permanent immunity sentinel', (0.5, 0.2, 0, 500, 5, 40), [48.675, 7.802, 443.523, 446.325]),
('control: short immunity endemic', (0.6, 0.25, 10, 800, 8, 100), [333.272, 133.331, 333.396, 3385.33]),
('control: no transmission waning only', (0.0, 0.3, 5, 100, 50, 20), [98.35, 0.04, 1.61, 0.0]),
('control: long immunity single wave',
(0.5, 0.1, 365, 2000, 10, 120),
[376.102, 3.345, 1620.554, 2084.424]),
('control: one day immunity', (0.5, 0.3, 1, 300, 3, 30), [188.175, 86.35, 25.475, 420.744]),
('control: 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 |
|---|---|---|---|
| control: seasonal coronavirus | [543.377, 58.225, 398.398, 1339.789] | [543.377, 58.225, 398.398, 1339.789] | Passed |
| regression: permanent immunity sentinel | [48.675, 7.802, 443.523, 446.325] | [48.675, 7.802, 443.523, 446.325] | Passed |
| control: 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 |
| control: no transmission waning only | [98.35, 0.04, 1.61, 0.0] | [98.35, 0.04, 1.61, 0.0] | Passed |
| control: long immunity single wave | [376.102, 3.345, 1620.554, 2084.424] | [376.102, 3.345, 1620.554, 2084.424] | Passed |
| control: one day immunity | [188.175, 86.35, 25.475, 420.744] | [188.175, 86.35, 25.475, 420.744] | Passed |
SHA-256 / 79733020054082ce39dc0324b71a156ec4ed0b8268dd397c9928a080d3eef2a9
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.495207+00:00.
Case digest / d112dce795b672b9a4c2720c1ef1863059e88181d1ba74a059526feca7db7524