FAILURE MAP
← Case archive

FA-64941 / Epidemic compartment models / Open access

Reed-Frost expected chain-binomial generations: extinction stop · case 01

Chains are truncated while fractional expected cases would still infect others.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

Expected (fractional) case counts below one are treated as extinction.

VERIFIED REPAIR

Restore the extinction stop rule: `if c < 1e-9:`.

Unsuccessful approach: A 0.5 threshold is still an arbitrary integer-case heuristic, not the 1e-9 contract.

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 * (1 - q ** c)
        s -= nxt
        c = nxt
        cases.append(round(c, 4))
        if c < 1:
            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)],
 [('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: 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: 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: 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]),
  ('control: 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]),
  ('control: 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]),
  ('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: 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]),
  ('control: 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 fixtureActualExpectedOutcome
regression: household of 10[[1.0, 0.9], 1.9][[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.2876, 1.3596, 1.3602, 1.2872, 1.1541, 0.9844], 9.5931][[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, 15.0, 4.9998, 0.0001], 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 pNoneNonePassed

SHA-256 / a0e53d074a1749abed5ac5193072cf354ed721d3b36d0e5bfbc15a3b5ce56808

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 * (1 - q ** c)
        s -= nxt
        c = nxt
        cases.append(round(c, 4))
        if c < 0.5:
            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)],
 [('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: 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: 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: 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]),
  ('control: 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]),
  ('control: 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]),
  ('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: 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]),
  ('control: 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 fixtureActualExpectedOutcome
regression: household of 10[[1.0, 0.9, 0.7328, 0.5474, 0.3822], 3.5624][[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.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], 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 pNoneNonePassed

SHA-256 / 98002716afe081a840f2baf54fa74ee35573ea47631a07e667fd4b476bf48039

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)],
 [('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: 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: 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: 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]),
  ('control: 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]),
  ('control: 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]),
  ('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: 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]),
  ('control: 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 fixtureActualExpectedOutcome
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 pNoneNonePassed

SHA-256 / 6462cec6885ce4702b7c860cbd6f29894afc43445e4d8d318efa152d0a6b83cd

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.452206+00:00.

Case digest / 20e85b8290de1cad895e1c8dd6f24dd13dc44f47860ceeb79c2e43f2635adb8d