FAILURE MAP
← Case archive

FA-65366 / Ecological population dynamics / Open access

Ricker map trajectory and dynamic regime: period-doubling onset · case 01

The first period-doubling at r=2 is reported as a damped approach.

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

ROOT CAUSE

The damped band includes r=2.

VERIFIED REPAIR

Restore the period-doubling onset rule: `elif r < 2:`.

Unsuccessful approach: Rounding r to one decimal pushes values just below 2 into the cycle band.

Case contract

Regime by r: r<0 decline, r==0 neutral, 0<r<=1 monotone, 1<r<2 damped, 2<=r<2.526 cycle2, else complex; trajectory N[t+1]=N*exp(r*(1-N/K)) rounded 4; None for K<=0 or N0<0.

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(r, k, n0, years):
    if k <= 0 or n0 < 0:
        return None
    if r < 0:
        regime = 'decline'
    elif r == 0:
        regime = 'neutral'
    elif r <= 1:
        regime = 'monotone'
    elif r <= 2:
        regime = 'damped'
    elif r < 2.526:
        regime = 'cycle2'
    else:
        regime = 'complex'
    n = float(n0)
    traj = []
    for _ in range(years):
        n = n * math.exp(r * (1 - n / k))
        traj.append(round(n, 4))
    return [regime, traj]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('control: two cycle',
   (2.2, 100, 30, 6),
   ['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]])],
 [('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]]),
  ('control: neutral empty patch', (0.0, 100, 0, 3), ['neutral', [0.0, 0.0, 0.0]]),
  ('control: declining', (-0.3, 100, 80, 4), ['decline', [75.3412, 69.9688, 63.9407, 57.3848]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None)],
 [('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]]),
  ('control: neutral empty patch', (0.0, 100, 0, 3), ['neutral', [0.0, 0.0, 0.0]]),
  ('control: declining', (-0.3, 100, 80, 4), ['decline', [75.3412, 69.9688, 63.9407, 57.3848]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None),
  ('control: weak growth above K',
   (0.5, 200, 350, 5),
   ['monotone', [240.5512, 217.36, 208.1284, 203.9417, 201.9419]])],
 [('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None),
  ('control: weak growth above K',
   (0.5, 200, 350, 5),
   ['monotone', [240.5512, 217.36, 208.1284, 203.9417, 201.9419]]),
  ('control: weak growth below K',
   (0.7, 200, 20, 5),
   ['monotone', [37.5522, 66.3073, 105.8713, 147.1827, 177.0682]])],
 [('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('control: two cycle',
   (2.2, 100, 30, 6),
   ['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]])]]
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
control: salmon stock damped['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]Passed
control: superstable r one['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]Passed
control: two cycle['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]Passed
regression: period doubling onset exactly 2['damped', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]Failed
regression: near two['damped', [129.6553, 72.5035, 124.2836, 77.2161]]['damped', [129.6553, 72.5035, 124.2836, 77.2161]]Passed
control: chaotic['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]Passed
control: neutral['neutral', [60.0, 60.0, 60.0]]['neutral', [60.0, 60.0, 60.0]]Passed

SHA-256 / 5a25b665eb0b8d3225ed2a905853de132f0b31b2ae4a156d0f419e8430aa27d8

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(r, k, n0, years):
    if k <= 0 or n0 < 0:
        return None
    if r < 0:
        regime = 'decline'
    elif r == 0:
        regime = 'neutral'
    elif r <= 1:
        regime = 'monotone'
    elif round(r, 1) < 2:
        regime = 'damped'
    elif r < 2.526:
        regime = 'cycle2'
    else:
        regime = 'complex'
    n = float(n0)
    traj = []
    for _ in range(years):
        n = n * math.exp(r * (1 - n / k))
        traj.append(round(n, 4))
    return [regime, traj]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('control: two cycle',
   (2.2, 100, 30, 6),
   ['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]])],
 [('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]]),
  ('control: neutral empty patch', (0.0, 100, 0, 3), ['neutral', [0.0, 0.0, 0.0]]),
  ('control: declining', (-0.3, 100, 80, 4), ['decline', [75.3412, 69.9688, 63.9407, 57.3848]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None)],
 [('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]]),
  ('control: neutral empty patch', (0.0, 100, 0, 3), ['neutral', [0.0, 0.0, 0.0]]),
  ('control: declining', (-0.3, 100, 80, 4), ['decline', [75.3412, 69.9688, 63.9407, 57.3848]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None),
  ('control: weak growth above K',
   (0.5, 200, 350, 5),
   ['monotone', [240.5512, 217.36, 208.1284, 203.9417, 201.9419]])],
 [('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None),
  ('control: weak growth above K',
   (0.5, 200, 350, 5),
   ['monotone', [240.5512, 217.36, 208.1284, 203.9417, 201.9419]]),
  ('control: weak growth below K',
   (0.7, 200, 20, 5),
   ['monotone', [37.5522, 66.3073, 105.8713, 147.1827, 177.0682]])],
 [('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('control: two cycle',
   (2.2, 100, 30, 6),
   ['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]])]]
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
control: salmon stock damped['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]Passed
control: superstable r one['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]Passed
control: two cycle['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]Passed
regression: period doubling onset exactly 2['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]Passed
regression: near two['cycle2', [129.6553, 72.5035, 124.2836, 77.2161]]['damped', [129.6553, 72.5035, 124.2836, 77.2161]]Failed
control: chaotic['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]Passed
control: neutral['neutral', [60.0, 60.0, 60.0]]['neutral', [60.0, 60.0, 60.0]]Passed

SHA-256 / d3d4a9d86fd0581104abd392d61a9962ed460afe10529549e51a24314d60434c

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(r, k, n0, years):
    if k <= 0 or n0 < 0:
        return None
    if r < 0:
        regime = 'decline'
    elif r == 0:
        regime = 'neutral'
    elif r <= 1:
        regime = 'monotone'
    elif r < 2:
        regime = 'damped'
    elif r < 2.526:
        regime = 'cycle2'
    else:
        regime = 'complex'
    n = float(n0)
    traj = []
    for _ in range(years):
        n = n * math.exp(r * (1 - n / k))
        traj.append(round(n, 4))
    return [regime, traj]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('control: two cycle',
   (2.2, 100, 30, 6),
   ['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]])],
 [('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]]),
  ('control: neutral empty patch', (0.0, 100, 0, 3), ['neutral', [0.0, 0.0, 0.0]]),
  ('control: declining', (-0.3, 100, 80, 4), ['decline', [75.3412, 69.9688, 63.9407, 57.3848]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None)],
 [('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]]),
  ('control: neutral empty patch', (0.0, 100, 0, 3), ['neutral', [0.0, 0.0, 0.0]]),
  ('control: declining', (-0.3, 100, 80, 4), ['decline', [75.3412, 69.9688, 63.9407, 57.3848]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None),
  ('control: weak growth above K',
   (0.5, 200, 350, 5),
   ['monotone', [240.5512, 217.36, 208.1284, 203.9417, 201.9419]])],
 [('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: invalid capacity', (1.0, 0, 10, 3), None),
  ('control: weak growth above K',
   (0.5, 200, 350, 5),
   ['monotone', [240.5512, 217.36, 208.1284, 203.9417, 201.9419]]),
  ('control: weak growth below K',
   (0.7, 200, 20, 5),
   ['monotone', [37.5522, 66.3073, 105.8713, 147.1827, 177.0682]])],
 [('control: salmon stock damped',
   (1.5, 1000, 200, 6),
   ['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]),
  ('control: superstable r one',
   (1.0, 500, 100, 5),
   ['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]),
  ('control: two cycle',
   (2.2, 100, 30, 6),
   ['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]),
  ('regression: period doubling onset exactly 2',
   (2.0, 100, 40, 5),
   ['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]),
  ('regression: near two', (1.96, 100, 40, 4), ['damped', [129.6553, 72.5035, 124.2836, 77.2161]]),
  ('control: chaotic', (3.0, 100, 10, 5), ['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]),
  ('control: neutral', (0.0, 100, 60, 3), ['neutral', [60.0, 60.0, 60.0]])]]
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
control: salmon stock damped['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]['damped', [664.0234, 1099.1389, 947.2604, 1025.2415, 987.1493, 1006.3622]]Passed
control: superstable r one['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]['monotone', [222.5541, 387.6342, 485.3128, 499.78, 500.0]]Passed
control: two cycle['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]['cycle2', [139.9377, 58.1234, 146.0361, 53.0407, 149.0324, 50.6759]]Passed
regression: period doubling onset exactly 2['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]['cycle2', [132.8047, 68.9089, 128.3303, 72.8208, 125.4096]]Passed
regression: near two['damped', [129.6553, 72.5035, 124.2836, 77.2161]]['damped', [129.6553, 72.5035, 124.2836, 77.2161]]Passed
control: chaotic['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]['complex', [148.7973, 34.421, 246.1736, 3.0674, 56.1939]]Passed
control: neutral['neutral', [60.0, 60.0, 60.0]]['neutral', [60.0, 60.0, 60.0]]Passed

SHA-256 / 07b01479e51dcead1f86078996a16d5c8aa089d5f5edf2c89480fbd942c1c48f

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

Case digest / c2d2401a016243d20572468ad6617c98cb089d6e5794c6b3d6630425f4661f68