FA-64981 / Epidemic compartment models / Open access
SIS endemic equilibrium convergence: subthreshold equilibrium · case 01
Subcritical infections report a negative endemic level and never converge.
ROOT CAUSE
The R0<=1 disease-free equilibrium branch is missing.
VERIFIED REPAIR
Restore the subthreshold equilibrium rule: `target = pop * (1 - 1 / r0) if r0 > 1 else 0.0`.
Unsuccessful approach: Assuming the seed level persists below threshold ignores extinction.
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)
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]),
('regression: 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]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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)],
[('regression: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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]),
('control: 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]),
('regression: 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]),
('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]),
('control: 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]),
('regression: 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]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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)],
[('regression: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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]),
('control: 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control: gonorrhoea-like endemic | [666.6667, 42, 657.0888] | [666.6667, 42, 657.0888] | Passed |
| regression: subcritical dies out | [-500.0, 200, 0.0] | [0.0, 21, 4.9755] | Failed |
| 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 |
| control: tight tolerance capped | [400.0, 30, 4.288] | [400.0, 30, 4.288] | Passed |
| control: zero day cap | [66.6667, 0, 1.0] | [66.6667, 0, 1.0] | Passed |
| control: invalid gamma | None | None | Passed |
SHA-256 / 3e2034f5e464e2328988b80927fef8b1c0c679fad83037d66515d68afe7018c5
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 float(i0)
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]),
('regression: 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]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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)],
[('regression: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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]),
('control: 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]),
('regression: 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]),
('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]),
('control: 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]),
('regression: 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]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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)],
[('regression: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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]),
('control: 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control: gonorrhoea-like endemic | [666.6667, 42, 657.0888] | [666.6667, 42, 657.0888] | Passed |
| regression: subcritical dies out | [50.0, 0, 50.0] | [0.0, 21, 4.9755] | Failed |
| 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 |
| control: tight tolerance capped | [400.0, 30, 4.288] | [400.0, 30, 4.288] | Passed |
| control: zero day cap | [66.6667, 0, 1.0] | [66.6667, 0, 1.0] | Passed |
| control: invalid gamma | None | None | Passed |
SHA-256 / 68d3a4abd04cbd39f113a2db53a619cc3e41d652c10d5e27573edcf1bdeb1c5b
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]),
('regression: 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]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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)],
[('regression: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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]),
('control: 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]),
('regression: 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]),
('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]),
('control: 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]),
('regression: 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]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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)],
[('regression: subcritical dies out', (0.1, 0.2, 500, 50, 0.01, 200), [0.0, 21, 4.9755]),
('control: already at equilibrium', (0.4, 0.2, 1000, 500, 0.01, 50), [500.0, 0, 500.0]),
('control: tight tolerance capped', (0.25, 0.2, 2000, 1, 0.0001, 30), [400.0, 30, 4.288]),
('control: 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]),
('control: 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control: gonorrhoea-like endemic | [666.6667, 42, 657.0888] | [666.6667, 42, 657.0888] | Passed |
| regression: 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 |
| control: tight tolerance capped | [400.0, 30, 4.288] | [400.0, 30, 4.288] | Passed |
| control: zero day cap | [66.6667, 0, 1.0] | [66.6667, 0, 1.0] | Passed |
| control: invalid gamma | None | None | Passed |
SHA-256 / 5fe737df6ee7dc8378ded28d5895cb001179853d2d9f3b0c638163d6a9a5d692
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.589822+00:00.
Case digest / 911dbea290738695c3e6fd3c9ccbbc4dbeb4731dce5bf4a821f04a668b2454e9