FAILURE MAP
← Case archive

FA-64996 / Epidemic compartment models / Open access

SIS endemic equilibrium convergence: day cap · case 01

Non-converging runs take one more step than allowed.

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

ROOT CAUSE

The day-cap comparison is inclusive.

VERIFIED REPAIR

Restore the day cap rule: `day < max_days`.

Unsuccessful approach: Stopping at max_days-1 takes one step too few.

Case contract

SIS with susceptibles pop-I; endemic target pop*(1-1/R0) if R0=beta/gamma>1 else 0; iterate daily Euler I += beta*I*(pop-I)/pop - gamma*I until |I-target| <= tol*pop or max_days steps; return [target rounded 4, days taken, I rounded 4] or None for non-positive beta, gamma or pop.

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, pop, i0, tol, max_days):
    if beta <= 0 or gamma <= 0 or pop <= 0:
        return None
    r0 = beta / gamma
    target = pop * (1 - 1 / r0) if r0 > 1 else 0.0
    i = float(i0)
    day = 0
    while abs(i - target) > tol * pop and day <= max_days:
        i += beta * i * (pop - i) / pop - gamma * i
        day += 1
    return [round(target, 4), day, round(i, 4)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None)],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None)],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])]]
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: gonorrhoea-like endemic[666.6667, 42, 657.0888][666.6667, 42, 657.0888]Passed
control: subcritical dies out[0.0, 21, 4.9755][0.0, 21, 4.9755]Passed
control: start above equilibrium[500.0, 17, 504.3274][500.0, 17, 504.3274]Passed
control: already at equilibrium[500.0, 0, 500.0][500.0, 0, 500.0]Passed
regression: tight tolerance capped[400.0, 31, 4.5001][400.0, 30, 4.288]Failed
regression: zero day cap[66.6667, 1, 1.197][66.6667, 0, 1.0]Failed
control: invalid gammaNoneNonePassed

SHA-256 / 4ffda6daa5875141457e56cda13364eae407d52fc2f3fab1b5e11ca3a4ffaf7c

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, pop, i0, tol, max_days):
    if beta <= 0 or gamma <= 0 or pop <= 0:
        return None
    r0 = beta / gamma
    target = pop * (1 - 1 / r0) if r0 > 1 else 0.0
    i = float(i0)
    day = 0
    while abs(i - target) > tol * pop and day < max_days - 1:
        i += beta * i * (pop - i) / pop - gamma * i
        day += 1
    return [round(target, 4), day, round(i, 4)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None)],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None)],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])]]
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: gonorrhoea-like endemic[666.6667, 42, 657.0888][666.6667, 42, 657.0888]Passed
control: subcritical dies out[0.0, 21, 4.9755][0.0, 21, 4.9755]Passed
control: start above equilibrium[500.0, 17, 504.3274][500.0, 17, 504.3274]Passed
control: already at equilibrium[500.0, 0, 500.0][500.0, 0, 500.0]Passed
regression: tight tolerance capped[400.0, 29, 4.0858][400.0, 30, 4.288]Failed
regression: zero day cap[66.6667, 0, 1.0][66.6667, 0, 1.0]Passed
control: invalid gammaNoneNonePassed

SHA-256 / a846fbde8f298a1e05fc014366218c4e4b933d63cb9258b437da72977f6adb8e

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, pop, i0, tol, max_days):
    if beta <= 0 or gamma <= 0 or pop <= 0:
        return None
    r0 = beta / gamma
    target = pop * (1 - 1 / r0) if r0 > 1 else 0.0
    i = float(i0)
    day = 0
    while abs(i - target) > tol * pop and day < max_days:
        i += beta * i * (pop - i) / pop - gamma * i
        day += 1
    return [round(target, 4), day, round(i, 4)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None)],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
  ('control: start above equilibrium', (0.4, 0.2, 1000, 900, 0.005, 100), [500.0, 17, 504.3274]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None)],
 [('control: gonorrhoea-like endemic', (0.3, 0.1, 1000, 10, 0.01, 200), [666.6667, 42, 657.0888]),
  ('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
  ('regression: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
  ('regression: zero day cap', (0.3, 0.1, 100, 1, 0.01, 0), [66.6667, 0, 1.0]),
  ('control: invalid gamma', (0.3, 0.0, 100, 1, 0.01, 10), None),
  ('control: fast dynamics', (0.9, 0.3, 300, 3, 0.02, 100), [200.0, 14, 197.4596]),
  ('regression: near threshold', (0.21, 0.2, 10000, 100, 0.001, 60), [476.1905, 60, 155.2383])]]
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: gonorrhoea-like endemic[666.6667, 42, 657.0888][666.6667, 42, 657.0888]Passed
control: subcritical dies out[0.0, 21, 4.9755][0.0, 21, 4.9755]Passed
control: start above equilibrium[500.0, 17, 504.3274][500.0, 17, 504.3274]Passed
control: already at equilibrium[500.0, 0, 500.0][500.0, 0, 500.0]Passed
regression: tight tolerance capped[400.0, 30, 4.288][400.0, 30, 4.288]Passed
regression: zero day cap[66.6667, 0, 1.0][66.6667, 0, 1.0]Passed
control: invalid gammaNoneNonePassed

SHA-256 / 5ddfe75fecaa97576aa3fb2a8b1b72c66e9a50c7d0557c0b3c8437d64a656cbf

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

Case digest / 1ad50235df716102489d6a537069351adfc5bc0541d49fc1c1a8bb23410e4eab