FA-65816 / Ecological population dynamics / Open access
Count-based population viability (diffusion approximation): sample variance · case 01
Environmental variance is underestimated, understating extinction risk.
ROOT CAUSE
The population variance divisor q is used instead of q-1.
VERIFIED REPAIR
Restore the sample variance rule: `var = sum((x - mu) ** 2 for x in logs) / (q - 1)`.
Unsuccessful approach: Dividing by q+1 biases the variance further downward.
Case contract
Log growth increments ln(N[t+1]/N[t]); mu = mean, sigma^2 = sample variance (q-1); d = ln(N_last/threshold); d<=0 gives probability 1.0; zero variance gives 1.0 if mu*T <= -d else 0.0; otherwise P = Phi((-d-muT)/(s sqrtT)) + exp(-2 mu d/s^2) Phi((-d+muT)/(s sqrtT)) capped at 1; return [mu, var, P] rounded 6; None with fewer than three counts, non-positive counts or threshold.
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(counts, threshold, horizon):
if len(counts) < 3 or min(counts) <= 0 or threshold <= 0:
return None
logs = [math.log(counts[t + 1] / counts[t]) for t in range(len(counts) - 1)]
q = len(logs)
mu = sum(logs) / q
var = sum((x - mu) ** 2 for x in logs) / q
d = math.log(counts[-1] / threshold)
if d <= 0:
return [round(mu, 6), round(var, 6), 1.0]
if var == 0:
return [round(mu, 6), 0.0, 1.0 if mu * horizon <= -d else 0.0]
sd = math.sqrt(var * horizon)
phi = lambda z: 0.5 * (1 + math.erf(z / math.sqrt(2)))
prob = phi((-d - mu * horizon) / sd) + math.exp(-2 * mu * d / var) * phi((-d + mu * horizon) / sd)
return [round(mu, 6), round(var, 6), round(min(prob, 1.0), 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])]]
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 |
|---|---|---|---|
| regression: grizzly census decline | [-0.062113, 0.003952, 0.653307] | [-0.062113, 0.004743, 0.688257] | Failed |
| regression: fluctuating stable | [0.009758, 0.037461, 0.141144] | [0.009758, 0.046826, 0.191687] | Failed |
| regression: growing | [0.157691, 0.002425, 0.0] | [0.157691, 0.003032, 0.0] | Failed |
| regression: already below threshold | [-0.660878, 0.065236, 1.0] | [-0.660878, 0.130471, 1.0] | Failed |
| control: constant decline | [-0.693147, 0.0, 1.0] | [-0.693147, 0.0, 1.0] | Passed |
| control: too short | None | None | Passed |
| regression: high variance | [-0.102165, 0.951171, 0.876849] | [-0.102165, 1.188964, 0.882919] | Failed |
SHA-256 / 04d456d9596dae3aa0e7fbf2cb0db60ef99d8550bb5e988d3f58e7d52e8bc6c9
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(counts, threshold, horizon):
if len(counts) < 3 or min(counts) <= 0 or threshold <= 0:
return None
logs = [math.log(counts[t + 1] / counts[t]) for t in range(len(counts) - 1)]
q = len(logs)
mu = sum(logs) / q
var = sum((x - mu) ** 2 for x in logs) / (q + 1)
d = math.log(counts[-1] / threshold)
if d <= 0:
return [round(mu, 6), round(var, 6), 1.0]
if var == 0:
return [round(mu, 6), 0.0, 1.0 if mu * horizon <= -d else 0.0]
sd = math.sqrt(var * horizon)
phi = lambda z: 0.5 * (1 + math.erf(z / math.sqrt(2)))
prob = phi((-d - mu * horizon) / sd) + math.exp(-2 * mu * d / var) * phi((-d + mu * horizon) / sd)
return [round(mu, 6), round(var, 6), round(min(prob, 1.0), 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])]]
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 |
|---|---|---|---|
| regression: grizzly census decline | [-0.062113, 0.003388, 0.664893] | [-0.062113, 0.004743, 0.688257] | Failed |
| regression: fluctuating stable | [0.009758, 0.031217, 0.10478] | [0.009758, 0.046826, 0.191687] | Failed |
| regression: growing | [0.157691, 0.002021, 0.0] | [0.157691, 0.003032, 0.0] | Failed |
| regression: already below threshold | [-0.660878, 0.04349, 1.0] | [-0.660878, 0.130471, 1.0] | Failed |
| control: constant decline | [-0.693147, 0.0, 1.0] | [-0.693147, 0.0, 1.0] | Passed |
| control: too short | None | None | Passed |
| regression: high variance | [-0.102165, 0.792643, 0.872269] | [-0.102165, 1.188964, 0.882919] | Failed |
SHA-256 / 46ce6bdab59c7bf30ea775b74e9dfa53f220bd5c2a8683a24f6f560783899dc6
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(counts, threshold, horizon):
if len(counts) < 3 or min(counts) <= 0 or threshold <= 0:
return None
logs = [math.log(counts[t + 1] / counts[t]) for t in range(len(counts) - 1)]
q = len(logs)
mu = sum(logs) / q
var = sum((x - mu) ** 2 for x in logs) / (q - 1)
d = math.log(counts[-1] / threshold)
if d <= 0:
return [round(mu, 6), round(var, 6), 1.0]
if var == 0:
return [round(mu, 6), 0.0, 1.0 if mu * horizon <= -d else 0.0]
sd = math.sqrt(var * horizon)
phi = lambda z: 0.5 * (1 + math.erf(z / math.sqrt(2)))
prob = phi((-d - mu * horizon) / sd) + math.exp(-2 * mu * d / var) * phi((-d + mu * horizon) / sd)
return [round(mu, 6), round(var, 6), round(min(prob, 1.0), 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('regression: already below threshold', ([30, 20, 8], 10, 10), [-0.660878, 0.130471, 1.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])],
[('regression: grizzly census decline',
([45, 42, 44, 38, 36, 35, 31], 10, 20),
[-0.062113, 0.004743, 0.688257]),
('regression: fluctuating stable', ([100, 120, 90, 110, 95, 105], 20, 50), [0.009758, 0.046826, 0.191687]),
('regression: growing', ([30, 36, 40, 50, 55, 66], 10, 30), [0.157691, 0.003032, 0.0]),
('control: constant decline', ([80, 40, 20], 5, 5), [-0.693147, 0.0, 1.0]),
('control: too short', ([10, 9], 5, 5), None),
('regression: high variance', ([200, 100, 260, 90, 300, 120], 30, 25), [-0.102165, 1.188964, 0.882919]),
('regression: peak then decline', ([60, 120, 80, 70, 65], 15, 40), [0.020011, 0.222192, 0.539603])]]
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 |
|---|---|---|---|
| regression: grizzly census decline | [-0.062113, 0.004743, 0.688257] | [-0.062113, 0.004743, 0.688257] | Passed |
| regression: fluctuating stable | [0.009758, 0.046826, 0.191687] | [0.009758, 0.046826, 0.191687] | Passed |
| regression: growing | [0.157691, 0.003032, 0.0] | [0.157691, 0.003032, 0.0] | Passed |
| regression: already below threshold | [-0.660878, 0.130471, 1.0] | [-0.660878, 0.130471, 1.0] | Passed |
| control: constant decline | [-0.693147, 0.0, 1.0] | [-0.693147, 0.0, 1.0] | Passed |
| control: too short | None | None | Passed |
| regression: high variance | [-0.102165, 1.188964, 0.882919] | [-0.102165, 1.188964, 0.882919] | Passed |
SHA-256 / 6441c4866d9b6c03e3d87eaf7240db1ef75582cc308ea2af6a2bd82f722fd329
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:37.391409+00:00.
Case digest / b15e5d94e6e8ab4122c4e0ed4bf4917aa4dd97768980433cf1cc32f343440ac0