FAILURE MAP
← Case archive

FA-64946 / Epidemic compartment models / Open access

Reed-Frost expected chain-binomial generations: final size accounting · case 01

The final size misses the index cases.

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

ROOT CAUSE

The attack total counts only susceptibles depleted, excluding the index generation.

VERIFIED REPAIR

Restore the final size accounting rule: `round(s0 + i0 - s, 4)`.

Unsuccessful approach: Subtracting the last generation drops cases that were already infected.

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 < 1e-9:
            break
    return [cases, round(s0 - 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]),
  ('regression: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: 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: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
  ('control: invalid p', (1.5, 10, 1, 4), None),
  ('regression: 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]),
  ('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.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: 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]),
  ('regression: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: 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], 2.9798][[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], 9.3969][[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, 0.0], 20.0][[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0]Failed
regression: p zero no spread[[1.0, 0.0], 0.0][[1.0, 0.0], 1.0]Failed
regression: p one immediate saturation[[1.0, 10.0, 0.0], 10.0][[1.0, 10.0, 0.0], 11.0]Failed
control: no index cases[[0.0, 0.0], 0.0][[0.0, 0.0], 0.0]Passed
control: invalid pNoneNonePassed

SHA-256 / 6dbeeba15a6e6c90bce1a96f73a9495a82938ede76dca152a3db29359f01b966

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 < 1e-9:
            break
    return [cases, round(s0 + i0 - s - cases[-1], 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]),
  ('regression: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: 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: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
  ('control: invalid p', (1.5, 10, 1, 4), None),
  ('regression: 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]),
  ('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.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: 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]),
  ('regression: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: 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.8165][[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], 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, 0.0], 22.0][[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.0]Passed
regression: p zero no spread[[1.0, 0.0], 1.0][[1.0, 0.0], 1.0]Passed
regression: 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 / 8ff34e0977ad525d1004c10ce66afa85864f6c5d4dbb216b16d5b21849095342

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]),
  ('regression: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: 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: no index cases', (0.2, 10, 0, 4), [[0.0, 0.0], 0.0]),
  ('control: invalid p', (1.5, 10, 1, 4), None),
  ('regression: 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]),
  ('regression: high contact probability', (0.5, 20, 2, 5), [[2.0, 15.0, 4.9998, 0.0001, 0.0], 22.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: 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]),
  ('regression: p zero no spread', (0.0, 10, 1, 4), [[1.0, 0.0], 1.0]),
  ('regression: 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: 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
regression: p zero no spread[[1.0, 0.0], 1.0][[1.0, 0.0], 1.0]Passed
regression: 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 / b795cbbd2761f816db10733352a12383410380796711167175ca5b957b539a25

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

Case digest / 01c9ec150441684e23822d037bfae697846699e45493a101b4b5cd8a2fb0cf3a