FAILURE MAP
← Case archive

FA-65131 / Epidemic compartment models / Open access

Windowed renewal-equation Rt estimate: window feasibility · case 01

A window touching day 0 is estimated using a day with no infection history.

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

ROOT CAUSE

The feasibility guard allows the window to include day 0.

VERIFIED REPAIR

Restore the window feasibility rule: `t - window + 1 < 1`.

Unsuccessful approach: Requiring the window to start after day 1 rejects valid windows that begin on day 1.

Case contract

weights[s-1] is the serial-interval weight at lag s>=1 (unnormalised); daily infection pressure Lambda_k = sum_s w_s*I[k-s]/sum of available weights (lags reaching before day 0 dropped); Rt = sum_{k=t-window+1..t} I_k / sum Lambda_k; None if the window reaches day 0, t is out of range or the denominator is zero; result rounded 6.

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(incidence, weights, t, window):
    if window < 1 or t - window + 1 < 0 or t >= len(incidence):
        return None
    num = 0.0
    den = 0.0
    for k in range(t - window + 1, t + 1):
        pressure = 0.0
        mass = 0.0
        for s in range(1, len(weights) + 1):
            if k - s < 0:
                break
            pressure += weights[s - 1] * incidence[k - s]
            mass += weights[s - 1]
        if mass > 0:
            pressure /= mass
        num += incidence[k]
        den += pressure
    if den <= 0:
        return None
    return round(num / den, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('control: unnormalised weights', ([5, 6, 8, 9, 11, 14, 16], [2, 3, 1], 6, 2), 1.417323),
  ('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667)],
 [('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667),
  ('control: t out of range', ([4, 6, 9], [0.5, 0.5], 3, 1), None),
  ('control: zero history', ([0, 0, 0, 5, 7], [0.6, 0.4], 3, 2), None),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451)],
 [('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667),
  ('control: t out of range', ([4, 6, 9], [0.5, 0.5], 3, 1), None),
  ('control: zero history', ([0, 0, 0, 5, 7], [0.6, 0.4], 3, 2), None),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451),
  ('control: long serial interval',
   ([2, 3, 5, 8, 11, 15, 19, 24, 30, 36], [0.1, 0.15, 0.2, 0.25, 0.3], 9, 4),
   2.289916),
  ('control: flat incidence', ([10, 10, 10, 10, 10, 10], [0.5, 0.3, 0.2], 5, 3), 1.0)],
 [('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451),
  ('control: long serial interval',
   ([2, 3, 5, 8, 11, 15, 19, 24, 30, 36], [0.1, 0.15, 0.2, 0.25, 0.3], 9, 4),
   2.289916),
  ('control: flat incidence', ([10, 10, 10, 10, 10, 10], [0.5, 0.3, 0.2], 5, 3), 1.0)],
 [('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('control: unnormalised weights', ([5, 6, 8, 9, 11, 14, 16], [2, 3, 1], 6, 2), 1.417323),
  ('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667)]]
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
control: growing outbreak2.5154322.515432Passed
control: declining outbreak0.3838770.383877Passed
control: unnormalised weights1.4173231.417323Passed
control: early window truncation2.762.76Passed
regression: boundary window reaching day 02.111111NoneFailed
regression: window starting on day 11.6666671.666667Passed
regression: single day window at day 11.6666671.666667Passed

SHA-256 / dcae64a8ba38132aaf9cf6d3b4a8de8dc62e7d1fb001318f7b187fdd272ed812

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(incidence, weights, t, window):
    if window < 1 or t - window + 1 < 2 or t >= len(incidence):
        return None
    num = 0.0
    den = 0.0
    for k in range(t - window + 1, t + 1):
        pressure = 0.0
        mass = 0.0
        for s in range(1, len(weights) + 1):
            if k - s < 0:
                break
            pressure += weights[s - 1] * incidence[k - s]
            mass += weights[s - 1]
        if mass > 0:
            pressure /= mass
        num += incidence[k]
        den += pressure
    if den <= 0:
        return None
    return round(num / den, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('control: unnormalised weights', ([5, 6, 8, 9, 11, 14, 16], [2, 3, 1], 6, 2), 1.417323),
  ('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667)],
 [('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667),
  ('control: t out of range', ([4, 6, 9], [0.5, 0.5], 3, 1), None),
  ('control: zero history', ([0, 0, 0, 5, 7], [0.6, 0.4], 3, 2), None),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451)],
 [('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667),
  ('control: t out of range', ([4, 6, 9], [0.5, 0.5], 3, 1), None),
  ('control: zero history', ([0, 0, 0, 5, 7], [0.6, 0.4], 3, 2), None),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451),
  ('control: long serial interval',
   ([2, 3, 5, 8, 11, 15, 19, 24, 30, 36], [0.1, 0.15, 0.2, 0.25, 0.3], 9, 4),
   2.289916),
  ('control: flat incidence', ([10, 10, 10, 10, 10, 10], [0.5, 0.3, 0.2], 5, 3), 1.0)],
 [('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451),
  ('control: long serial interval',
   ([2, 3, 5, 8, 11, 15, 19, 24, 30, 36], [0.1, 0.15, 0.2, 0.25, 0.3], 9, 4),
   2.289916),
  ('control: flat incidence', ([10, 10, 10, 10, 10, 10], [0.5, 0.3, 0.2], 5, 3), 1.0)],
 [('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('control: unnormalised weights', ([5, 6, 8, 9, 11, 14, 16], [2, 3, 1], 6, 2), 1.417323),
  ('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667)]]
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
control: growing outbreak2.5154322.515432Passed
control: declining outbreak0.3838770.383877Passed
control: unnormalised weights1.4173231.417323Passed
control: early window truncation2.762.76Passed
regression: boundary window reaching day 0NoneNonePassed
regression: window starting on day 1None1.666667Failed
regression: single day window at day 1None1.666667Failed

SHA-256 / feb8c57534c0dffa5f33341257b3faa8ed8e3511950546e864971299a00b3df9

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(incidence, weights, t, window):
    if window < 1 or t - window + 1 < 1 or t >= len(incidence):
        return None
    num = 0.0
    den = 0.0
    for k in range(t - window + 1, t + 1):
        pressure = 0.0
        mass = 0.0
        for s in range(1, len(weights) + 1):
            if k - s < 0:
                break
            pressure += weights[s - 1] * incidence[k - s]
            mass += weights[s - 1]
        if mass > 0:
            pressure /= mass
        num += incidence[k]
        den += pressure
    if den <= 0:
        return None
    return round(num / den, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('control: unnormalised weights', ([5, 6, 8, 9, 11, 14, 16], [2, 3, 1], 6, 2), 1.417323),
  ('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667)],
 [('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667),
  ('control: t out of range', ([4, 6, 9], [0.5, 0.5], 3, 1), None),
  ('control: zero history', ([0, 0, 0, 5, 7], [0.6, 0.4], 3, 2), None),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451)],
 [('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667),
  ('control: t out of range', ([4, 6, 9], [0.5, 0.5], 3, 1), None),
  ('control: zero history', ([0, 0, 0, 5, 7], [0.6, 0.4], 3, 2), None),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451),
  ('control: long serial interval',
   ([2, 3, 5, 8, 11, 15, 19, 24, 30, 36], [0.1, 0.15, 0.2, 0.25, 0.3], 9, 4),
   2.289916),
  ('control: flat incidence', ([10, 10, 10, 10, 10, 10], [0.5, 0.3, 0.2], 5, 3), 1.0)],
 [('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('control: single day window', ([10, 12, 15, 17, 22, 25], [0.25, 0.5, 0.25], 5, 1), 1.408451),
  ('control: long serial interval',
   ([2, 3, 5, 8, 11, 15, 19, 24, 30, 36], [0.1, 0.15, 0.2, 0.25, 0.3], 9, 4),
   2.289916),
  ('control: flat incidence', ([10, 10, 10, 10, 10, 10], [0.5, 0.3, 0.2], 5, 3), 1.0)],
 [('control: growing outbreak', ([1, 2, 4, 7, 12, 20, 33, 50, 80], [0.2, 0.5, 0.3], 8, 3), 2.515432),
  ('control: declining outbreak', ([90, 80, 60, 45, 30, 20, 12, 8], [0.3, 0.4, 0.3], 7, 2), 0.383877),
  ('control: unnormalised weights', ([5, 6, 8, 9, 11, 14, 16], [2, 3, 1], 6, 2), 1.417323),
  ('control: early window truncation', ([3, 5, 9, 14, 20], [0.1, 0.2, 0.3, 0.4], 3, 2), 2.76),
  ('regression: boundary window reaching day 0', ([4, 6, 9, 12], [0.5, 0.5], 2, 3), None),
  ('regression: window starting on day 1', ([4, 6, 9, 12], [0.5, 0.5], 2, 2), 1.666667),
  ('regression: single day window at day 1', ([3, 5, 8], [1.0], 1, 1), 1.666667)]]
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
control: growing outbreak2.5154322.515432Passed
control: declining outbreak0.3838770.383877Passed
control: unnormalised weights1.4173231.417323Passed
control: early window truncation2.762.76Passed
regression: boundary window reaching day 0NoneNonePassed
regression: window starting on day 11.6666671.666667Passed
regression: single day window at day 11.6666671.666667Passed

SHA-256 / 11528a4b25095706543ac0927d695ce6c266b7a24cc41b37c2b05a6e93685985

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

Case digest / 44e2a8b080579ae8d212f264240947f0fff4a0065c44dcf31d50c8f2520596d0