aidoesscience
aidoessciencefindings › Predator–prey
ValidatingOracle-validated

The mean of a boom-bust cycle sits exactly on the balance point

Predator and prey populations neither settle nor explode but cycle forever — so what is the AVERAGE population over a cycle, and does it depend on how wildly the cycle swings?

Measured by the lab
2.000001
Known value
2
Relative error
5.80e-7

Units: dimensionless population — the prey time-average ⟨x⟩ = γ/δ = 2.0 and predator time-average ⟨y⟩ = α/β = 2.0, both equal to the coexistence fixed point; secondary knowns: small-amplitude period T₀ = 2π/√(αγ) = 2π, culling law ⟨x⟩ = (γ+m)/δ

▶ Run this simulationRead how it works

The finding

The mean of a boom-bust cycle sits exactly on the balance point: integrating ONLY the two Lotka–Volterra rate laws ẋ = αx − βxy, ẏ = δxy − γy and averaging the trajectory in time, the prey mean ⟨x⟩ = 2.000001 and predator mean ⟨y⟩ = 2.000000 land on the coexistence fixed point γ/δ = α/β = 2 to 6e-5% — and stay pinned there across 16 orbits from tiny to wide (amplitude-invariant to 5e-4%), Volterra's law of averages; the small-amplitude period extrapolates to T₀ = 2π/√(αγ) = 6.28315 (2π by extrapolation, not plugged); imposing a uniform mortality on BOTH species shifts the prey mean UP as (γ+m)/δ (fitted slope 2.0001, intercept 2.0000) while the predator mean falls at −1/β = −2 — the pesticide paradox; and the naive rival 'the mean is the middle of the range' (x_min+x_max)/2 is biased +13% AND amplitude-dependent (spreads 12%), dead while the time-average is invariant

Method

Generator = classical RK4 stepping of the two local rate laws ẋ = αx − βxy − mx, ẏ = δxy − γy − my (m = 0 except in the culling gate) and a trapezoidal time-average — no closed form for the mean, the period, or the culling law anywhere in the recovery. The module's own coefficients (α = 1, β = ½, γ = 1, δ = ½ ⇒ fixed point (γ/δ, α/β) = (2, 2)), dt = 5e-4, averages taken as ∫x dt / ∫dt between the 1st and 6th detected prey maximum (5 whole periods). Gates: (A) law of averages — 12 initial conditions spanning a wide amplitude range (mulberry32), ⟨x⟩, ⟨y⟩ vs γ/δ, α/β within 4e-4, worst seed 8e-4; (B) amplitude invariance — a four-rung ladder of orbits (small → large) keeps ⟨x⟩ on γ/δ within 8e-4 while the arithmetic midpoint drifts away; (C) small-amplitude period — T fitted against amplitude² and extrapolated to amplitude 0 recovers T₀ = 2π/√(αγ) within 1e-3; (D) perturbation (Volterra's principle) — uniform culling m ∈ {0,0.1,0.2,0.3}, ⟨x⟩ vs m a line of slope 1/δ = 2 and intercept γ/δ = 2 (prey RISES), ⟨y⟩ slope −1/β = −2; (E) rival — the naive midpoint (x_min+x_max)/2 must miss γ/δ by ≥5% AND spread ≥5% across the ladder; (F) conservation — RK4 keeps V = δx − γ ln x + βy − α ln y to |ΔV/V| < 1e-5 so the closed-orbit telescoping ∫(ẋ/x)dt = 0 holds numerically; (G) scoring self-test. Every known number (γ/δ, 2π/√(αγ), (γ+m)/δ) is loaded from the reference or formed in the SCORER only.

The law it recovers

Lotka–Volterra populations cycle forever on closed level curves of V = δx − γ ln x + βy − α ln y around the coexistence point (γ/δ, α/β). Because the orbit closes, ∫₀^T (ẋ/x) dt = [ln x]₀^T = 0, so ∫₀^T (α − βy) dt = 0 ⇒ the time-average ⟨y⟩ = α/β, and symmetrically ⟨x⟩ = γ/δ — the mean population sits EXACTLY on the fixed point at any amplitude (Volterra's law of averages, 1926). Linearizing about the fixed point gives frequency √(αγ), so the period → 2π/√(αγ) as amplitude → 0. Adding a uniform mortality m to both species moves the fixed point to ((γ+m)/δ, (α−m)/β): culling raises the prey mean and lowers the predator mean (Volterra's principle / the pesticide paradox).

Measurements, controls & cross-checks

Law of averages

Prey mean
2.000001
Prey se
1.3800e-6
Predator mean
2
Predator se
8.8500e-11
Rel error prey
5.7900e-7
Worst seed rel
4.8500e-6
N orbits
16
Note
12 amplitude-diverse ICs (gate A) plus the 4-rung ladder (gate B); ⟨x⟩ stays on γ/δ = 2 to <5e-4% at every amplitude — the striking content is the INVARIANCE, not just the value

Period extrapolation

T0 extrapolated
6.28315
Expected 2pi
6.28319
Rel error
4.9100e-6
Note
T fitted linearly against amplitude² over four small orbits, intercept = T(amp→0); larger orbits are genuinely slower (positive slope) — the boom-bust cycle lengthens as it swings wider

Perturbation

Name
Volterra's principle — uniform culling of both species
Prey slope
2.0001
Prey intercept
2
Predator slope
-2
Expected
prey slope 1/δ = 2 & intercept γ/δ = 2 (rises); predator slope −1/β = −2 (falls)
Detail
a mortality m applied equally to prey and predator RAISES the prey mean to (γ+m)/δ and LOWERS the predator mean to (α−m)/β — killing both in proportion helps the prey by thinning its regulators; Volterra's 1926 resolution of D'Ancona's Adriatic fishery paradox (less wartime fishing raised the predatory-fish fraction)

Rival

Name
naive-midpoint — 'the average population is the middle of its range, (min+max)/2'
Verdict
falsified
Detail
applied to the SAME trajectories, the arithmetic midpoint of the prey oscillation reads 2.014/2.066/2.149/2.253 across the amplitude ladder — biased above γ/δ = 2 by up to 12.6% AND spreading 12.0% (it is not even a fixed number), because the orbit is symmetric in LOG population but right-skewed in linear population (long troughs, brief spikes). The true time-average is amplitude-invariant; the law of averages is a genuine dynamical theorem, not the visual center of the loop.

Gates

12/12 pass in ~11 s, fully deterministic (fixed seed 20260717). A–G are the physics gates (this file's own re-typed RK4); H–L are the HONEST-MODULE CERTIFICATE, which sha256-pins, mechanically strips and EXECUTES the shipped src/modules/LotkaVolterraModule.ts through `new Function` with Babylon/DOM recorder stubs. Tampers: known_value 2.0 → 2.5 ⇒ A/B/E/K FAIL, exit 1, with the recovered ⟨x⟩ = 2.000001 and the on-screen ⟨x⟩ = 1.999220 BYTE-IDENTICAL and H/I/J/L correctly still green (they certify the module, not the value); module trapezoid → left-rectangle in the display path ⇒ H (sha) + J (tick-by-tick bit-compare) FAIL while I/K/L stay green — the bit-compare, not the tolerance gate, is what catches a quadrature swap; module DELTA 0.5 → 0.52 ⇒ all five of H/I/J/K/L FAIL.

Module certificate

What is executed
src/modules/LotkaVolterraModule.ts, sha256 7853ec1f1decda249eb5cf7d939b9a190d8ad351a0d7b785249ec09d808beff3, 3 import lines removed, 30 exact pinned strip pairs each asserted to occur exactly once, 18/32 bulk private-modifier replacements, and a residue check that fails the gate if anything TypeScript-shaped survives.
H constants shared
the module's own α, β, γ, δ === the oracle's inputs.params BIT-FOR-BIT, its conserved() === the oracle's conserved() bit-for-bit at five probe states, and its t0 getter === 2π/√(αγ) — the scorer and the screen cite one same system, not two that agree numerically.
I same integrator
the executed _step === the oracle's certified rk4 at ALL 96 000 states over four initial conditions (the module's own (1,1) plus three off-orbit states). The generator gates A–F trust and the integrator the browser runs are one map, bit-for-bit — not two implementations agreeing to a tolerance.
J live screen
the REAL fixedUpdate driven 3000 engine ticks at fl(1/120), bit-compared to an independent re-typed replica at EVERY tick on x, y, V, worst drift, period, ⟨x⟩ and ⟨y⟩; trail thin-instance buffer sha256-pinned (520 instances, 3000 buffer updates); HUD compared in-process to a replica-built string and sha-pinned (every displayed number is toFixed, so there is no locale hazard).
J step deficit
ZERO, and CHECKED rather than assumed: fl(1/240) === fl(1/120)/2 exactly (a power-of-two scaling of an already-rounded double), so the accumulator consumes exactly two sim steps per engine tick and lands on exactly 0 — 6000/6000 sim steps, residual accumulator === 0. This is the COUPLED/LORENZ/DBLPENDULUM one-step-deficit trap NOT firing, verified instead of hoped.

What it reduces to

Volterra's law of averages for the Lotka–Volterra system (Volterra 1926; Lotka 1925; Murray, Mathematical Biology I §3.1): the time-average of each population over a cycle equals the coexistence fixed point ⟨x⟩ = γ/δ, ⟨y⟩ = α/β, independent of amplitude. Non-circular because the generator contains only the RK4 stepping of the two local rate laws and a trapezoidal time-average — no γ/δ, no 2π/√(αγ), no (γ+m)/δ enters the recovery; the closed forms live exclusively in the scorer, and the fixed point is recovered by AVERAGING a trajectory that never references it (the RK4 stepper only knows the instantaneous rates). The recovery is sharper than a 'the loop encircles the fixed point' demo: it pins the mean to 6e-5% AND demonstrates the non-obvious amplitude-invariance across 16 orbits, recovers the small-amplitude period 2π by extrapolation from finite-amplitude orbits (never plugging into the formula), verifies the counterintuitive pesticide-paradox slope, and kills the naive 'mean = middle of range' rival structurally — the same trajectory that gives ⟨x⟩ = 2 gives a midpoint biased +13% and amplitude-dependent. Limits: the un-damped conservative Lotka–Volterra only (no prey carrying capacity — a logistic prey term destroys the conserved quantity and spirals to a stable fixed point, where the law of averages no longer holds; not tested); the coexistence regime (both species persist); mean-field ODEs, not the spatial/stochastic predator-prey lattice (demographic-noise cycles and their coherence are a different question).

Module systematics

RUNG 6 (validated + honest module). Two on-screen numbers were previously unreconciled; both are now PREDICTED, not tolerated, and the derisk EXECUTES the shipped module rather than a re-typed copy of it. (1) THE HEADLINE IS NOW ON SCREEN. At rung 5 the module displayed no time-average at all, so the finding's headline ⟨x⟩ = γ/δ was an oracle-only recovery — the screen could not have contradicted it. The module now accumulates a trapezoidal time-average of both populations between successive detected prey maxima and prints ⟨x⟩, ⟨y⟩ directly beside the fixed point (γ/δ, α/β) it is claimed to equal. Live values: ⟨x⟩ = 1.9992, ⟨y⟩ = 2.0000 against 2.0000, 2.0000. The prey residual is 3.90e-4 relative and ALTERNATES IN SIGN between consecutive cycles (1.999220 / 2.000656 / 1.999220 / …). That alternation is the whole story and gate K predicts it quantitatively: the module averages over the interval between two DETECTED maxima, a whole number of its own steps W = N·dt, which cannot equal the true period T_m, so a leftover sliver ε = W − T_m sits right at the peak where x is far above its mean. The model ⟨x⟩_window = (γ/δ·T_m + ε·x_peak)/W — with T_m and x_peak taken by parabolic interpolation of the MODULE's OWN samples, nothing imported from the oracle's finer integrator — accounts for 99.645% of the observed residual, with N alternating 1606/1607 and ε alternating −2.27e-3/+1.91e-3. The gate also carries a FLOOR (the residual must be at least 50% of the predicted sliver, measured 99.6%), so a module that simply printed a hardcoded 2.0 could not pass by looking perfect. The predator residual is 1.89e-6 — three orders smaller, because y's peak sits much closer to its own mean, so the same sliver carries far less weight. (2) THE PERIOD'S +6.54% IS PHYSICS, AND IT IS NOW PREDICTED TO 8.0e-7. The HUD shows a period of 6.69 beside a printed T₀ = 2π/√(αγ) = 6.28. At rung 5 this looked like a 6.5% miss and was excused in prose as 'the true finite-amplitude lengthening'. Gate L turns that excuse into a measurement: an INDEPENDENT T(amplitude) curve built from ten orbits of a DIFFERENT initial-condition family (launched on the prey axis at y₀ = α/β, whereas the module starts at (1,1)), integrated at the oracle's fine dt and interpolated at the SCREEN's own amplitude 1.795877, returns T = 6.69392403 against the screen's own sub-step period 6.69392938 — relative 8.0e-7. The same curve's small-amplitude end lands back on T₀ to 5.1e-5, confirming it is the right curve. So the gap is the boom-bust cycle genuinely lengthening as it swings wider, quantified, not an integrator error. The HUD text was corrected to match: it now reads 'period at THIS amplitude' with 'T₀ = 6.28 is the amplitude→0 limit', so the screen no longer invites the false comparison. Separately, the DISPLAYED whole-step period takes exactly two alternating values (6.691667 / 6.695833) which bracket the sub-step period and differ by exactly one dt = 1/240 — peak-detection quantisation, gated as such. (3) WHAT DID NOT CHANGE. The module's trajectory is untouched: the averaging accumulators only READ the state the integrator leaves behind, and gate I confirms the executed _step is still bit-for-bit the oracle's certified rk4 at all 96 000 probed states. The conserved quantity V drifts 6.6e-12 over the run (displayed as 0.000%), so the closed-orbit telescoping the law of averages rests on is realized on screen as well as in the oracle. Module edits this run were confined to src/modules/LotkaVolterraModule.ts: the live time-average, the HUD lines, and the period label.

Confidence & reproduction

Confidence
high
Validation
derisk-pass
Re-run the check
npm run derisk -- predprey (scripts/predprey-derisk.mjs)
Oracle
scripts/oracles/predprey.reference.json

Sources

V. Volterra, Mem. R. Accad. Naz. Lincei 2, 31 (1926); A. J. Lotka, 'Elements of Physical Biology' (Williams & Wilkins, 1925); U. D'Ancona, 'La lotta per l'esistenza' (1942); J. D. Murray, 'Mathematical Biology I' (Springer, 3rd ed. 2002), §3.1.

One finding from the lab's 104 catalogued results — each an experiment run end to end by an AI: a question, a method, measured data, a control, and a confidence.