FA-64926 / Epidemic compartment models / Open access
Reed-Frost expected chain-binomial generations: escape probability · case 01
Expected cases exceed the susceptible pool when several infectives are present.
ROOT CAUSE
The probability of infection uses the linear p*C approximation instead of 1-(1-p)**C.
VERIFIED REPAIR
Restore the escape probability rule: `(1 - q ** c)`.
Unsuccessful approach: Clamping p*C at one still overstates infection risk for moderate C.
Case contract
Expected Reed-Frost: C[t+1] = S[t]*(1-(1-p)**C[t]), S[t+1]=S[t]-C[t+1]; cases are infectious for one generation only; stop early once expected cases fall below 1e-9; return [cases per generation including the index generation rounded to 4, final size including index cases rounded to 4]; None for invalid p or negative counts.
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(p, s0, i0, generations):
if not 0 <= p <= 1 or s0 < 0 or i0 < 0:
return None
q = 1 - p
s = float(s0)
c = float(i0)
cases = [round(c, 4)]
for _ in range(generations):
nxt = s * (p * c)
s -= nxt
c = nxt
cases.append(round(c, 4))
if c < 1e-9:
break
return [cases, round(s0 + i0 - s, 4)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None)],
[('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0]),
('regression: fading chain',
(0.02, 30, 1, 10),
[[1.0, 0.6, 0.3542, 0.2071, 0.1204, 0.0698, 0.0404, 0.0233, 0.0135, 0.0078, 0.0045], 2.4409]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465])],
[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0]),
('regression: fading chain',
(0.02, 30, 1, 10),
[[1.0, 0.6, 0.3542, 0.2071, 0.1204, 0.0698, 0.0404, 0.0233, 0.0135, 0.0078, 0.0045], 2.4409]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465]),
('regression: two generations only', (0.15, 40, 4, 2), [[4.0, 19.1198, 19.9465], 43.0662])],
[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465]),
('regression: two generations only', (0.15, 40, 4, 2), [[4.0, 19.1198, 19.9465], 43.0662])],
[('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0])]]
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: household of 10 | [[1.0, 0.9, 0.729, 0.5373, 0.3672, 0.2375, 0.1479], 3.9189] | [[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798] | Failed |
| regression: school class | [[1.0, 1.16, 1.2918, 1.3718, 1.3815, 1.3149, 1.1823, 1.0072, 0.8175], 10.527] | [[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969] | Failed |
| regression: high contact probability | [[2.0, 20.0, 0.0], 22.0] | [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0] | Failed |
| control: p zero no spread | [[1.0, 0.0], 1.0] | [[1.0, 0.0], 1.0] | Passed |
| control: p one immediate saturation | [[1.0, 10.0, 0.0], 11.0] | [[1.0, 10.0, 0.0], 11.0] | Passed |
| control: no index cases | [[0.0, 0.0], 0.0] | [[0.0, 0.0], 0.0] | Passed |
| control: invalid p | None | None | Passed |
SHA-256 / d3ff209f7be69f1ba7890859117f4e040604ae258224d6f0e85e970b2360a712
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(p, s0, i0, generations):
if not 0 <= p <= 1 or s0 < 0 or i0 < 0:
return None
q = 1 - p
s = float(s0)
c = float(i0)
cases = [round(c, 4)]
for _ in range(generations):
nxt = s * (min(1.0, p * c))
s -= nxt
c = nxt
cases.append(round(c, 4))
if c < 1e-9:
break
return [cases, round(s0 + i0 - s, 4)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None)],
[('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0]),
('regression: fading chain',
(0.02, 30, 1, 10),
[[1.0, 0.6, 0.3542, 0.2071, 0.1204, 0.0698, 0.0404, 0.0233, 0.0135, 0.0078, 0.0045], 2.4409]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465])],
[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0]),
('regression: fading chain',
(0.02, 30, 1, 10),
[[1.0, 0.6, 0.3542, 0.2071, 0.1204, 0.0698, 0.0404, 0.0233, 0.0135, 0.0078, 0.0045], 2.4409]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465]),
('regression: two generations only', (0.15, 40, 4, 2), [[4.0, 19.1198, 19.9465], 43.0662])],
[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465]),
('regression: two generations only', (0.15, 40, 4, 2), [[4.0, 19.1198, 19.9465], 43.0662])],
[('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0])]]
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: household of 10 | [[1.0, 0.9, 0.729, 0.5373, 0.3672, 0.2375, 0.1479], 3.9189] | [[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798] | Failed |
| regression: school class | [[1.0, 1.16, 1.2918, 1.3718, 1.3815, 1.3149, 1.1823, 1.0072, 0.8175], 10.527] | [[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969] | Failed |
| regression: high contact probability | [[2.0, 20.0, 0.0], 22.0] | [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0] | Failed |
| control: p zero no spread | [[1.0, 0.0], 1.0] | [[1.0, 0.0], 1.0] | Passed |
| control: p one immediate saturation | [[1.0, 10.0, 0.0], 11.0] | [[1.0, 10.0, 0.0], 11.0] | Passed |
| control: no index cases | [[0.0, 0.0], 0.0] | [[0.0, 0.0], 0.0] | Passed |
| control: invalid p | None | None | Passed |
SHA-256 / f5e8181b48645e80776131c9c2cf375cc077ee098cb3e2d5bdb6c5a62d71b30e
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(p, s0, i0, generations):
if not 0 <= p <= 1 or s0 < 0 or i0 < 0:
return None
q = 1 - p
s = float(s0)
c = float(i0)
cases = [round(c, 4)]
for _ in range(generations):
nxt = s * (1 - q ** c)
s -= nxt
c = nxt
cases.append(round(c, 4))
if c < 1e-9:
break
return [cases, round(s0 + i0 - s, 4)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None)],
[('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0]),
('regression: fading chain',
(0.02, 30, 1, 10),
[[1.0, 0.6, 0.3542, 0.2071, 0.1204, 0.0698, 0.0404, 0.0233, 0.0135, 0.0078, 0.0045], 2.4409]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465])],
[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0]),
('regression: fading chain',
(0.02, 30, 1, 10),
[[1.0, 0.6, 0.3542, 0.2071, 0.1204, 0.0698, 0.0404, 0.0233, 0.0135, 0.0078, 0.0045], 2.4409]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465]),
('regression: two generations only', (0.15, 40, 4, 2), [[4.0, 19.1198, 19.9465], 43.0662])],
[('regression: household of 10',
(0.1, 9, 1, 6),
[[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798]),
('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('regression: large ward',
(0.03, 60, 3, 8),
[[3.0, 5.2396, 8.0777, 10.182, 9.733, 6.8673, 3.7561, 1.7453, 0.7455], 49.3465]),
('regression: two generations only', (0.15, 40, 4, 2), [[4.0, 19.1198, 19.9465], 43.0662])],
[('regression: school class',
(0.04, 29, 1, 8),
[[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969]),
('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0]),
('control: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
('control: p one immediate saturation', (1.0, 10, 1, 4), [[1.0, 10.0, 0.0], 11.0]),
('control: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
('control: invalid p', (1.5, 10, 1, 4), None),
('control: zero generations', (0.3, 10, 2, 0), [[2.0], 2.0])]]
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: household of 10 | [[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798] | [[1.0, 0.9, 0.7328, 0.5474, 0.3822, 0.2541, 0.1633], 3.9798] | Passed |
| regression: school class | [[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969] | [[1.0, 1.16, 1.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844, 0.8038], 10.3969] | Passed |
| regression: high contact probability | [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0] | [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0] | Passed |
| control: p zero no spread | [[1.0, 0.0], 1.0] | [[1.0, 0.0], 1.0] | Passed |
| control: p one immediate saturation | [[1.0, 10.0, 0.0], 11.0] | [[1.0, 10.0, 0.0], 11.0] | Passed |
| control: no index cases | [[0.0, 0.0], 0.0] | [[0.0, 0.0], 0.0] | Passed |
| control: invalid p | None | None | Passed |
SHA-256 / e77349a3a3082777def7e43db74124312454f13708af18a21b27983ce450b278
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.358722+00:00.
Case digest / 4e6a06db403aa3eaf3714c2e6c628bd6e9218c53a83cd1446fc4bd5752e5abdb