FAILURE MAP
← Case archive

FA-65506 / Ecological population dynamics / Open access

Two-sample mark-recapture abundance: chapman numerator · case 01

Chapman estimates are biased low, especially with few recaptures.

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

ROOT CAUSE

The +1 corrections are dropped from the numerator but kept in the denominator.

VERIFIED REPAIR

Restore the chapman numerator rule: `est = (marked + 1) * (caught + 1) /`.

Unsuccessful approach: Correcting only the marked count is still inconsistent.

Case contract

Recaptures must be in [0, min(marked, caught)] else None; 'lp' = M*C/R ([None, None] when R=0, no variance); Chapman = (M+1)(C+1)/(R+1)-1 with variance (M+1)(C+1)(M-R)(C-R)/((R+1)^2 (R+2)); return [estimate rounded 2, variance rounded 2 or None].

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(marked, caught, recaptured, method):
    if recaptured < 0 or recaptured > min(marked, caught):
        return None
    if method == 'lp':
        if recaptured == 0:
            return [None, None]
        return [round(marked * caught / recaptured, 2), None]
    est = marked * caught / (recaptured + 1) - 1
    var = (marked + 1) * (caught + 1) * (marked - recaptured) * (caught - recaptured) / ((recaptured + 1) ** 2 * (recaptured + 2))
    return [round(est, 2), round(var, 2)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None)],
 [('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84]),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84]),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84])]]
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: trout chapman[570.43, 10077.1][580.95, 10077.1]Failed
control: trout petersen[600.0, None][600.0, None]Passed
regression: zero recaptures chapman[1999.0, 2091000.0][2090.0, 2091000.0]Failed
control: zero recaptures petersen[None, None][None, None]Passed
regression: all recaptured[28.03, 0.0][30.0, 0.0]Failed
control: recaptures exceed catchNoneNonePassed
control: recaptures exceed markedNoneNonePassed

SHA-256 / 1abf51a95a8d5d88923c717a0f360568049b8b5ed2ae0f233b86c7bba5708ee9

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(marked, caught, recaptured, method):
    if recaptured < 0 or recaptured > min(marked, caught):
        return None
    if method == 'lp':
        if recaptured == 0:
            return [None, None]
        return [round(marked * caught / recaptured, 2), None]
    est = (marked + 1) * caught / (recaptured + 1) - 1
    var = (marked + 1) * (caught + 1) * (marked - recaptured) * (caught - recaptured) / ((recaptured + 1) ** 2 * (recaptured + 2))
    return [round(est, 2), round(var, 2)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None)],
 [('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84]),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84]),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84])]]
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: trout chapman[575.19, 10077.1][580.95, 10077.1]Failed
control: trout petersen[600.0, None][600.0, None]Passed
regression: zero recaptures chapman[2039.0, 2091000.0][2090.0, 2091000.0]Failed
control: zero recaptures petersen[None, None][None, None]Passed
regression: all recaptured[29.0, 0.0][30.0, 0.0]Failed
control: recaptures exceed catchNoneNonePassed
control: recaptures exceed markedNoneNonePassed

SHA-256 / 4395add82f069b6d94f84c020fbc0ae819fde990072c0d0536e37f49b0b9c056

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(marked, caught, recaptured, method):
    if recaptured < 0 or recaptured > min(marked, caught):
        return None
    if method == 'lp':
        if recaptured == 0:
            return [None, None]
        return [round(marked * caught / recaptured, 2), None]
    est = (marked + 1) * (caught + 1) / (recaptured + 1) - 1
    var = (marked + 1) * (caught + 1) * (marked - recaptured) * (caught - recaptured) / ((recaptured + 1) ** 2 * (recaptured + 2))
    return [round(est, 2), round(var, 2)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None)],
 [('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84]),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84]),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: trout chapman', (120, 100, 20, 'chapman'), [580.95, 10077.1]),
  ('control: trout petersen', (120, 100, 20, 'lp'), [600.0, None]),
  ('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: negative recapture', (20, 20, -1, 'lp'), None)],
 [('regression: zero recaptures chapman', (50, 40, 0, 'chapman'), [2090.0, 2091000.0]),
  ('control: zero recaptures petersen', (50, 40, 0, 'lp'), [None, None]),
  ('regression: all recaptured', (30, 30, 30, 'chapman'), [30.0, 0.0]),
  ('control: recaptures exceed catch', (80, 10, 12, 'chapman'), None),
  ('control: recaptures exceed marked', (10, 80, 12, 'lp'), None),
  ('regression: small sample', (15, 12, 3, 'chapman'), [51.0, 280.8]),
  ('regression: butterflies', (200, 150, 45, 'chapman'), [658.8, 4966.84])]]
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: trout chapman[580.95, 10077.1][580.95, 10077.1]Passed
control: trout petersen[600.0, None][600.0, None]Passed
regression: zero recaptures chapman[2090.0, 2091000.0][2090.0, 2091000.0]Passed
control: zero recaptures petersen[None, None][None, None]Passed
regression: all recaptured[30.0, 0.0][30.0, 0.0]Passed
control: recaptures exceed catchNoneNonePassed
control: recaptures exceed markedNoneNonePassed

SHA-256 / eb949029b4e8b92cf268ff70ea80f30fca023ba48366f42df708644d1fa8174c

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

Case digest / caa097eefdd94ce722f2f1c82144da0ef54e17c15870184f075973c9351b39a6