FA-65581 / Ecological population dynamics / Open access
Levins metapopulation with habitat destruction: equilibrium destruction term · case 01
Equilibrium occupancy ignores habitat destruction.
ROOT CAUSE
The destroyed fraction is not subtracted from the equilibrium.
VERIFIED REPAIR
Restore the equilibrium destruction term rule: `eq = max(0.0, 1 - destroyed - e / c)`.
Unsuccessful approach: Multiplying by 1-D understates the extinction threshold effect.
Case contract
Occupancy p: p += c*p*(1-D-p) - e*p each year, clamped to [0, 1-D]; equilibrium max(0, 1-D-e/c); return [equilibrium rounded 6, trajectory rounded 6]; None for c<=0, e<0 or D outside [0,1).
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(c, e, destroyed, p0, years):
if c <= 0 or e < 0 or not 0 <= destroyed < 1:
return None
eq = max(0.0, 1 - e / c)
p = p0
traj = []
for _ in range(years):
p = p + c * p * (1 - destroyed - p) - e * p
p = min(max(p, 0.0), 1 - destroyed)
traj.append(round(p, 6))
return [round(eq, 6), traj]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])]]
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: butterfly network | [0.8, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]] | [0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]] | Failed |
| control: intact habitat | [0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]] | [0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]] | Passed |
| regression: heavy destruction extinction | [0.333333, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]] | [0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]] | Failed |
| regression: overshoot clamp | [0.98, [0.7, 0.665, 0.689937]] | [0.68, [0.7, 0.665, 0.689937]] | Failed |
| regression: no extinction | [1.0, [0.284, 0.388966, 0.508231, 0.627697]] | [0.9, [0.284, 0.388966, 0.508231, 0.627697]] | Failed |
| control: invalid destruction | None | None | Passed |
| regression: empty network | [0.8, [0.0, 0.0, 0.0]] | [0.6, [0.0, 0.0, 0.0]] | Failed |
SHA-256 / a320225317005294ffd22c20de1af9ba17ee620b45d54c23f3cd477a939ba427
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(c, e, destroyed, p0, years):
if c <= 0 or e < 0 or not 0 <= destroyed < 1:
return None
eq = max(0.0, (1 - destroyed) * (1 - e / c))
p = p0
traj = []
for _ in range(years):
p = p + c * p * (1 - destroyed - p) - e * p
p = min(max(p, 0.0), 1 - destroyed)
traj.append(round(p, 6))
return [round(eq, 6), traj]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])]]
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: butterfly network | [0.64, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]] | [0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]] | Failed |
| control: intact habitat | [0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]] | [0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]] | Passed |
| regression: heavy destruction extinction | [0.166667, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]] | [0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]] | Failed |
| regression: overshoot clamp | [0.686, [0.7, 0.665, 0.689937]] | [0.68, [0.7, 0.665, 0.689937]] | Failed |
| regression: no extinction | [0.9, [0.284, 0.388966, 0.508231, 0.627697]] | [0.9, [0.284, 0.388966, 0.508231, 0.627697]] | Passed |
| control: invalid destruction | None | None | Passed |
| regression: empty network | [0.64, [0.0, 0.0, 0.0]] | [0.6, [0.0, 0.0, 0.0]] | Failed |
SHA-256 / 1826fab9194487c6b635208b6bbb6371c2f9159d4753619fc0feca04a769a088
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(c, e, destroyed, p0, years):
if c <= 0 or e < 0 or not 0 <= destroyed < 1:
return None
eq = max(0.0, 1 - destroyed - e / c)
p = p0
traj = []
for _ in range(years):
p = p + c * p * (1 - destroyed - p) - e * p
p = min(max(p, 0.0), 1 - destroyed)
traj.append(round(p, 6))
return [round(eq, 6), traj]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: overshoot clamp', (2.5, 0.05, 0.3, 0.6, 3), [0.68, [0.7, 0.665, 0.689937]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])],
[('regression: butterfly network',
(0.5, 0.1, 0.2, 0.1, 6),
[0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]]),
('control: intact habitat',
(0.4, 0.1, 0.0, 0.05, 5),
[0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]]),
('regression: heavy destruction extinction',
(0.3, 0.2, 0.5, 0.3, 6),
[0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]]),
('regression: no extinction', (0.6, 0.0, 0.1, 0.2, 4), [0.9, [0.284, 0.388966, 0.508231, 0.627697]]),
('control: invalid destruction', (0.5, 0.1, 1.0, 0.1, 3), None),
('regression: empty network', (0.5, 0.1, 0.2, 0.0, 3), [0.6, [0.0, 0.0, 0.0]]),
('regression: high turnover',
(1.2, 0.8, 0.25, 0.4, 5),
[0.083333, [0.248, 0.198995, 0.171376, 0.15327, 0.140407]])]]
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: butterfly network | [0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]] | [0.6, [0.125, 0.154687, 0.18913, 0.227984, 0.27039, 0.314952]] | Passed |
| control: intact habitat | [0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]] | [0.75, [0.064, 0.081562, 0.103369, 0.130106, 0.162367]] | Passed |
| regression: heavy destruction extinction | [0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]] | [0.0, [0.258, 0.225131, 0.198669, 0.176895, 0.158663, 0.143177]] | Passed |
| regression: overshoot clamp | [0.68, [0.7, 0.665, 0.689937]] | [0.68, [0.7, 0.665, 0.689937]] | Passed |
| regression: no extinction | [0.9, [0.284, 0.388966, 0.508231, 0.627697]] | [0.9, [0.284, 0.388966, 0.508231, 0.627697]] | Passed |
| control: invalid destruction | None | None | Passed |
| regression: empty network | [0.6, [0.0, 0.0, 0.0]] | [0.6, [0.0, 0.0, 0.0]] | Passed |
SHA-256 / 9cb7b199534e8b6d0ddeb16b8d0c045d5d184078049026de3264d9077d42b4db
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:35.313395+00:00.
Case digest / e7c36c5af323f24a407f5b9e74a146e5cc8b962855d20a0ef2369db5ae01f5bd