FAILURE MAP
← Case archive

FA-65396 / Ecological population dynamics / Open access

Beverton-Holt recruitment convergence: band scale · case 01

Convergence is almost never detected for large stocks.

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

ROOT CAUSE

The 1% band is treated as an absolute number of individuals.

VERIFIED REPAIR

Restore the band scale rule: `band = 0.01 * k`.

Unsuccessful approach: Scaling the band by the initial stock makes convergence depend on the starting point.

Case contract

N[t+1] = r0*N/(1+(r0-1)*N/K); equilibrium K if r0>1 else 0; band = 0.01*K; hit = first year (0 = start) with |N-eq|<=band, else None; return [trajectory rounded 4, equilibrium, hit]; None for invalid inputs.

Why this case matters

Population projections set harvest quotas, conservation status and pest-control timing; a wrong update order, boundary or rate conversion silently changes management advice.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(n0, r0, k, years):
    if k <= 0 or r0 <= 0 or n0 < 0:
        return None
    eq = float(k) if r0 > 1 else 0.0
    band = 0.01
    n = float(n0)
    traj = []
    hit = 0 if abs(n - eq) <= band else None
    for year in range(1, years + 1):
        n = r0 * n / (1 + (r0 - 1) * n / k)
        traj.append(round(n, 4))
        if hit is None and abs(n - eq) <= band:
            hit = year
    return [traj, eq, hit]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5])],
 [('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None),
  ('control: never reaches band', (1, 1.2, 10000, 5), [[1.2, 1.4399, 1.7279, 2.0734, 2.4879], 10000.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1])],
 [('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None),
  ('control: never reaches band', (1, 1.2, 10000, 5), [[1.2, 1.4399, 1.7279, 2.0734, 2.4879], 10000.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1]),
  ('regression: small capacity',
   (2, 4.0, 20, 6),
   [[6.1538, 12.8, 17.5342, 19.3208, 19.8258, 19.9562], 20.0, 5])],
 [('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1]),
  ('regression: small capacity',
   (2, 4.0, 20, 6),
   [[6.1538, 12.8, 17.5342, 19.3208, 19.8258, 19.9562], 20.0, 5])],
 [('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None)]]
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: herring recruitment[[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783], 1000.0, None][[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783], 1000.0, 7]Failed
control: slow approach[[14.8515, 21.9512, 32.2196, 46.8208, 67.09, 94.3079, 129.2706, 171.709, 219.8186, 270.309, 319.1849, 362.9343], 500.0, None][[14.8515, 21.9512, 32.2196, 46.8208, 67.09, 94.3079, 129.2706, 171.709, 219.8186, 270.309, 319.1849, 362.9343], 500.0, None]Passed
control: start at capacity[[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0][[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]Passed
regression: start within band[[997.4937, 998.7453, 999.3723], 1000.0, None][[997.4937, 998.7453, 999.3723], 1000.0, 0]Failed
control: replacement r0 one[[200.0, 200.0, 200.0, 200.0], 0.0, None][[200.0, 200.0, 200.0, 200.0], 0.0, None]Passed
regression: declining r0 half[[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, None][[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]Failed
regression: above capacity[[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, None][[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]Failed

SHA-256 / cf2262f82da3f1e6b5bfa9785997fd1031f978e292716713de2847d3edad70e1

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(n0, r0, k, years):
    if k <= 0 or r0 <= 0 or n0 < 0:
        return None
    eq = float(k) if r0 > 1 else 0.0
    band = 0.01 * n0
    n = float(n0)
    traj = []
    hit = 0 if abs(n - eq) <= band else None
    for year in range(1, years + 1):
        n = r0 * n / (1 + (r0 - 1) * n / k)
        traj.append(round(n, 4))
        if hit is None and abs(n - eq) <= band:
            hit = year
    return [traj, eq, hit]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5])],
 [('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None),
  ('control: never reaches band', (1, 1.2, 10000, 5), [[1.2, 1.4399, 1.7279, 2.0734, 2.4879], 10000.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1])],
 [('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None),
  ('control: never reaches band', (1, 1.2, 10000, 5), [[1.2, 1.4399, 1.7279, 2.0734, 2.4879], 10000.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1]),
  ('regression: small capacity',
   (2, 4.0, 20, 6),
   [[6.1538, 12.8, 17.5342, 19.3208, 19.8258, 19.9562], 20.0, 5])],
 [('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1]),
  ('regression: small capacity',
   (2, 4.0, 20, 6),
   [[6.1538, 12.8, 17.5342, 19.3208, 19.8258, 19.9562], 20.0, 5])],
 [('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None)]]
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: herring recruitment[[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783], 1000.0, 10][[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783], 1000.0, 7]Failed
control: slow approach[[14.8515, 21.9512, 32.2196, 46.8208, 67.09, 94.3079, 129.2706, 171.709, 219.8186, 270.309, 319.1849, 362.9343], 500.0, None][[14.8515, 21.9512, 32.2196, 46.8208, 67.09, 94.3079, 129.2706, 171.709, 219.8186, 270.309, 319.1849, 362.9343], 500.0, None]Passed
control: start at capacity[[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0][[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]Passed
regression: start within band[[997.4937, 998.7453, 999.3723], 1000.0, 0][[997.4937, 998.7453, 999.3723], 1000.0, 0]Passed
control: replacement r0 one[[200.0, 200.0, 200.0, 200.0], 0.0, None][[200.0, 200.0, 200.0, 200.0], 0.0, None]Passed
regression: declining r0 half[[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 8][[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]Failed
regression: above capacity[[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 4][[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]Failed

SHA-256 / d76a5c134cd34a8f7510fc6c4b1e5ab3997cdfce27b76a221856f12f4a5d5610

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(n0, r0, k, years):
    if k <= 0 or r0 <= 0 or n0 < 0:
        return None
    eq = float(k) if r0 > 1 else 0.0
    band = 0.01 * k
    n = float(n0)
    traj = []
    hit = 0 if abs(n - eq) <= band else None
    for year in range(1, years + 1):
        n = r0 * n / (1 + (r0 - 1) * n / k)
        traj.append(round(n, 4))
        if hit is None and abs(n - eq) <= band:
            hit = year
    return [traj, eq, hit]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5])],
 [('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None),
  ('control: never reaches band', (1, 1.2, 10000, 5), [[1.2, 1.4399, 1.7279, 2.0734, 2.4879], 10000.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1])],
 [('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None),
  ('control: never reaches band', (1, 1.2, 10000, 5), [[1.2, 1.4399, 1.7279, 2.0734, 2.4879], 10000.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1]),
  ('regression: small capacity',
   (2, 4.0, 20, 6),
   [[6.1538, 12.8, 17.5342, 19.3208, 19.8258, 19.9562], 20.0, 5])],
 [('regression: herring recruitment',
   (50, 3.0, 1000, 10),
   [[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783],
    1000.0,
    7]),
  ('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: one year from band',
   (985, 2.0, 1000, 4),
   [[992.4433, 996.2073, 998.1001, 999.0491], 1000.0, 1]),
  ('regression: small capacity',
   (2, 4.0, 20, 6),
   [[6.1538, 12.8, 17.5342, 19.3208, 19.8258, 19.9562], 20.0, 5])],
 [('control: slow approach',
   (10, 1.5, 500, 12),
   [[14.8515,
     21.9512,
     32.2196,
     46.8208,
     67.09,
     94.3079,
     129.2706,
     171.709,
     219.8186,
     270.309,
     319.1849,
     362.9343],
    500.0,
    None]),
  ('control: start at capacity', (1000, 2.0, 1000, 4), [[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]),
  ('regression: start within band', (995, 2.0, 1000, 3), [[997.4937, 998.7453, 999.3723], 1000.0, 0]),
  ('control: replacement r0 one', (200, 1.0, 1000, 4), [[200.0, 200.0, 200.0, 200.0], 0.0, None]),
  ('regression: declining r0 half',
   (400, 0.5, 1000, 8),
   [[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]),
  ('regression: above capacity',
   (3000, 2.5, 1000, 6),
   [[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]),
  ('control: invalid r0', (100, 0.0, 1000, 5), None)]]
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: herring recruitment[[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783], 1000.0, 7][[136.3636, 321.4286, 586.9565, 810.0, 927.4809, 974.5989, 991.3871, 997.1125, 999.0356, 999.6783], 1000.0, 7]Passed
control: slow approach[[14.8515, 21.9512, 32.2196, 46.8208, 67.09, 94.3079, 129.2706, 171.709, 219.8186, 270.309, 319.1849, 362.9343], 500.0, None][[14.8515, 21.9512, 32.2196, 46.8208, 67.09, 94.3079, 129.2706, 171.709, 219.8186, 270.309, 319.1849, 362.9343], 500.0, None]Passed
control: start at capacity[[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0][[1000.0, 1000.0, 1000.0, 1000.0], 1000.0, 0]Passed
regression: start within band[[997.4937, 998.7453, 999.3723], 1000.0, 0][[997.4937, 998.7453, 999.3723], 1000.0, 0]Passed
control: replacement r0 one[[200.0, 200.0, 200.0, 200.0], 0.0, None][[200.0, 200.0, 200.0, 200.0], 0.0, None]Passed
regression: declining r0 half[[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7][[250.0, 142.8571, 76.9231, 40.0, 20.4082, 10.3093, 5.1813, 2.5974], 0.0, 7]Passed
regression: above capacity[[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5][[1363.6364, 1119.403, 1044.5682, 1017.363, 1006.8736, 1002.7381], 1000.0, 5]Passed

SHA-256 / 382166950ec8014be1c74d30b7166c3712227f09745b6e40435d7e46a4b895e2

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

Case digest / fffc49f3d78984467200cd7c8e603168b93017bb1d36e9be1b4851b91520482a