Learning Objectives
By the end of this section you will be able to:
- Derive the Lotka–Volterra equations and from one ecological idea — coupling through encounter terms .
- Find all equilibria of the system and identify the coexistence fixed point .
- Read a phase portrait — closed orbits, the direction of motion, the location of the center — without doing any algebra.
- Prove that is conserved, and use it to explain WHY the orbits are closed loops.
- Linearize about the coexistence point, compute the Jacobian, get purely imaginary eigenvalues , and conclude the period of small oscillations .
- Implement the system in plain Python with RK4 and verify the conserved quantity with PyTorch's autograd to machine precision.
- Explain Volterra's paradox — why predators always lag prey by a quarter-period — and connect it to data from Hudson's Bay fur returns and the Adriatic fishery.
The Story: Fish, Foxes and Vito Volterra
"Why, during the war years when fishing stopped in the Adriatic, did the proportion of predatory fish — sharks, skates, rays — actually INCREASE?" — the question Umberto D'Ancona put to his father-in-law Vito Volterra in 1925.
Volterra was a mathematician, not an ecologist. But he noticed that the question had nothing to do with the biology of any particular species — it was purely about the coupling between two populations. He wrote down two equations, solved them on the back of an envelope, and produced a prediction so counter-intuitive it became one of the founding results of mathematical biology: if you stop fishing, the predators benefit MORE than the prey, because the prey were suppressing them by attracting other predators.
At almost the same moment, Alfred Lotka — a Polish-American chemist working at Johns Hopkins — wrote down the identical equations while thinking about autocatalytic chemical reactions. Two people, two continents, two fields, one system. The equations have been called Lotka–Volterra ever since.
Why this section is the heart of the chapter
Every other system in this chapter is linear (we just have to find eigenvalues). Lotka–Volterra is the first nonlinear system you can't solve with eigenvalues alone — and yet it has a complete qualitative theory, a conserved quantity, and an analytic period formula. It is the proof-of-concept that nonlinear systems can be understood without closed-form solutions.
Building the Model from One Idea
Imagine just prey, no predators. Rabbits in a meadow with infinite grass and no foxes. Birth rate proportional to the existing population, so
Pure exponential growth. Rabbits everywhere.
Now imagine just predators, no prey. Foxes starving in an empty meadow. Death rate proportional to existing predators:
Pure exponential decay. Foxes vanish.
The interesting physics is in the coupling. Suppose prey and predators wander randomly. Then the rate of encounters between them is proportional to the product of densities: . This is the same mass-action law that governs chemical reactions (rate of scales with ) — which is exactly why Lotka came across the same equations.
Each encounter removes some prey (predation) and feeds the predators. Add those two coupling terms to the previous equations and the closed system pops out:
Read it in plain English
- Prey: "I grow at rate α per individual, but predators eat me at rate β per predator-prey pair."
- Predator: "I starve at rate γ per individual, but every prey I eat converts (with efficiency δ/β) into more of me."
The minus sign on the term is where the entire ecological tension lives. Without it, prey explode. Add it back, and the prey grow only when predators are scarce — which automatically makes predators grow — which suppresses prey again. The system "breathes."
The Four Parameters and What They Really Mean
Every Lotka–Volterra investigation hinges on four positive constants. Their dimensions matter — units make the formulas honest.
| Symbol | Name | Units | Mental picture |
|---|---|---|---|
| α | Prey birth rate | 1 / time | Rabbits with infinite grass double every ln 2 / α time units. |
| β | Predation rate | 1 / (predator · time) | One predator eats β · x prey per unit time when prey density is x. |
| γ | Predator death rate | 1 / time | Predators starve to half-population in ln 2 / γ time units without prey. |
| δ | Conversion efficiency | 1 / (prey · time) | Each prey eaten produces δ / β fraction of a new predator. Always δ < β in reality. |
The model is invariant under with . That means you can choose units of prey and predators freely — the PHASE PORTRAIT (the shape of the orbits) only cares about the dimensionless products and .
Equilibrium Points: Where Nothing Changes
An equilibrium of a system is a state where the velocity vector is zero — pop. you in there and you stay forever. To find them, set both right-hand sides to zero simultaneously:
Each equation factors into two roots. We need a state that satisfies BOTH equations at once, so we cross-pair the factors:
| Prey factor = 0 | Predator factor = 0 | Equilibrium |
|---|---|---|
| x = 0 | y = 0 | (0, 0) — extinction |
| x = 0 | −γ + δ·x = 0 → x = γ/δ | contradiction (x can't be both) |
| α − β·y = 0 → y = α/β | y = 0 | contradiction (y can't be both) |
| α − β·y = 0 → y = α/β | −γ + δ·x = 0 → x = γ/δ | (γ/δ, α/β) — coexistence |
Two equilibria, both intuitive:
- Trivial fixed point — nothing in the meadow, nothing happens.
- Coexistence fixed point — exactly enough prey to sustain the predators, exactly enough predators to balance prey growth.
Notice the swap: the equilibrium prey density depends on predator parameters (γ, δ) and the equilibrium predator density depends on prey parameters (α, β). That is what coupling looks like.
The Phase Plane: Following the State Vector
Time-series plots show and separately. But the system is 2-D: its complete state at any instant is a point in the positive quadrant of the plane. Watching THAT point move is the geometric picture.
At every state, the velocity vector tells you which way the orbit is heading. Walking around the coexistence point you can read off four qualitative regions, each named for the direction of motion:
| Region | Where | dx/dt | dy/dt | What's happening |
|---|---|---|---|---|
| Right of x*, below y* | x > γ/δ and y < α/β | + (prey grow) | + (predators grow) | Lots of prey, few predators — both populations rising. State moves up-right. |
| Right of x*, above y* | x > γ/δ and y > α/β | − (prey crash) | + (predators still grow) | Predators take over. State moves up-left. |
| Left of x*, above y* | x < γ/δ and y > α/β | − (prey still falling) | − (predators starve) | Both crashing. State moves down-left. |
| Left of x*, below y* | x < γ/δ and y < α/β | + (prey recovering) | − (predators still few) | Recovery. State moves down-right back to start. |
Round and round. The orbit is a counter-clockwise closed loop around . The next section's interactive widget lets you watch this with sliders.
Interactive: Move the Knobs
The widget below integrates the Lotka–Volterra system with RK4 (3000 steps over ). The left panel is the time series of (green) and (pink). The right panel is the same trajectory drawn in the phase plane . The white dot is the current state — watch how it walks along the closed orbit while the time-series oscillations slosh up and down.
Try this: push β (predation) up to 1.0 and watch the orbit shrink towards the fixed point — heavy predation means smaller oscillations around a stable balance. Push α (prey birth) up to 2.0 and the orbit balloons outward — more food at the bottom makes the cycle more violent. The center of the orbit moves too: it sits at , so increasing α slides the center upward.
Lotka–Volterra orbits are marginally stable, not asymptotically. If your numerical integrator has any dissipative error (Forward-Euler does!), the orbit will spiral OUT over many cycles. RK4 is conservative enough at this step size that you won't see drift — try setting in your own integrator and watch the cycle slowly grow.
Why the Orbits Close: A Conserved Quantity
Why CLOSED loops? In a general 2-D nonlinear system, orbits can spiral, wander chaotically, or blow up. Closed loops are the rare special case. For Lotka–Volterra they happen because there is a smooth function that is constant along every orbit — the equation picks out one loop for each value of .
Here is how to find it. Divide the two equations to eliminate :
This is now an ODE in with gone. Separate variables — get all s on one side and all s on the other:
Split the fractions and integrate term by term:
Move everything to one side, define :
Then every orbit satisfies . Different orbits = different constants = different level curves.
Why H has a minimum exactly at the fixed point
Compute the partial derivatives: and . Setting both to zero gives and — exactly the coexistence fixed point. The Hessian there is positive-definite (both and ), so has a strict minimum there. That is why orbits curl around the fixed point — they are level curves of a bowl-shaped surface.
Linearization at the Coexistence Point
The coexistence equilibrium is interesting; the extinction point is not (it's an unstable saddle — perturb either species upward and it grows). Near we can replace the nonlinear system with its linear approximation, the Jacobian matrix:
Substitute , . The diagonal entries vanish identically:
Trace is 0, determinant is . The characteristic polynomial is therefore , giving eigenvalues
Purely imaginary eigenvalues. The linearization says the equilibrium is a center: small perturbations rotate around it with angular frequency and don't decay or grow. This is consistent with what we already proved using the conserved quantity : closed orbits.
Linearization caveat
For a center, the linearization is not always reliable as a guide to the nonlinear system — generic nonlinear perturbations turn centers into spirals (either inward or outward). Lotka–Volterra happens to have the conserved , which forces the orbits to remain closed loops even at large amplitudes. Without that conservation law, ANY small perturbation could destroy the oscillation.
Interactive: The Jacobian as a Linear System
The widget below shows phase portraits of a generic linear 2-D system where you can slide the four entries of . The defaults match the Lotka–Volterra Jacobian at the coexistence point for our parameters : . Verify that the eigenvalues you read off match .
Experiment: set to a small NEGATIVE value (say −0.05). The center turns into a stable spiral — orbits decay to the origin. Set it slightly positive (+0.05) and the center becomes an unstable spiral — orbits explode outward. The boundary case a = 0 (where the trace vanishes) is the ONLY way to get a center. It is a knife-edge condition, and the Lotka–Volterra system sits exactly on it.
The Period of Small Oscillations
For small departures from the coexistence point, the linearization rotates with angular frequency . The period is therefore
Plug in , : time units. Look at the interactive widget's time series — count the gap between successive prey peaks. You will measure exactly ≈ 7 even though the oscillation is far from infinitesimal (we run amplitudes of order 1). The period formula is exact in the linear limit and remarkably accurate even for large orbits — a useful empirical bonus of the Hamiltonian structure.
The period depends ONLY on and — the "diagonal" rates of pure birth and pure death. The coupling constants and only set the AMPLITUDE of the oscillation and the position of the equilibrium, not its tempo. This is the kind of insight you would NEVER spot from numerics alone.
Phase Lag: Predators Always Arrive Late
In the interactive time series, the pink predator peak comes AFTER the green prey peak by roughly a quarter of the period — about time units in the default case. This is not a numerical artifact. It is what center-style oscillation does: the two variables trace out a circle in the phase plane, and a circle has its x-coordinate maximum exactly radians before its y-coordinate maximum.
Ecologically: predators feast on the prey peak, but it takes time for their offspring to mature and contribute to . By the time the predator population catches up, the prey is already crashing — which then starves the predators a quarter-cycle later. Hudson's Bay Company fur returns from the 1840s show the snowshoe-hare / lynx cycle with this exact phase lag, repeating with a ≈ 10-year period for over a century.
Volterra's paradox: a constant proportional harvest (say, fishing both species at rate ) replaces and . The fixed point shifts to — MORE prey, FEWER predators. That is exactly D'Ancona's observation in reverse: stop fishing (h → 0) and predator equilibrium rises while prey equilibrium falls.
Worked Example (Step-by-Step, Collapsible)
Open the box to follow a complete pen-and-paper computation: find the equilibria, derive the period, compute one RK4 step by hand, and check that is conserved across that step to 4 decimal places.
Expand: full hand-computed walkthrough with and .
Step 1 — Find the equilibria
Set both RHS to zero: and . Either (then ), giving the trivial point (0, 0); or
Coexistence at .
Step 2 — Linearize there
Determinant ; trace = 0. Eigenvalues from : .
Step 3 — Period of small oscillations
Step 4 — One RK4 step from (2.5, 1.0) with h = 0.1
Right-hand side helper: .
k1 = f(2.5, 1.0):
Midpoint with k1: . k2 = f(2.575, 0.99125):
Midpoint with k2: . k3 = f(2.5777, 0.99224):
Endpoint with k3: . k4 = f(2.6555, 0.98458):
Weighted update:
Step 5 — Verify H is conserved across this step
Before:
After:
Drift: after one step of . RK4 is 4th-order accurate so we expect ; the observed is consistent with the 6-decimal rounding we did on the intermediate slopes. The PyTorch test below will show drift ≪ at .
Plain Python: A Hand-Built RK4 Integrator
Read this block top to bottom. Every line is annotated. The point is not the integrator itself — that's a textbook RK4 — but to see how each constant in the code corresponds to a physical rate and how the right-hand side is JUST a literal translation of the equations.
To check yourself: at with the default parameters and initial condition, you should print and — the orbit has nearly closed (returning to a state very close to would happen near ).
PyTorch: Verifying the Conserved Quantity with Autograd
We claimed is invariant along the flow. Numerics with finite step sizes can't prove it — the drift you saw above is partly the truth and partly numerical error. PyTorch autograd, on the other hand, computes the gradient of EXACTLY by the chain rule. If evaluates to zero at every state we pick — not just on one orbit — then really is conserved.
Expected printed output: five lines, each with on the order of to . That is autograd confirming the theorem at random points — a pen-and-paper proof would compute algebraically, which is exactly what this code verifies numerically.
Real-World Applications
| Field | x = ? | y = ? | Real cycle observed |
|---|---|---|---|
| Boreal ecology | Snowshoe hare | Canada lynx | Hudson's Bay Company fur returns 1845–1935: ≈ 10-year cycle, predator lags prey by ≈ 2 yr. |
| Marine biology | Plankton | Predatory fish | D'Ancona's Adriatic data 1914–1923: predator fraction rose during WWI fishing pause. |
| Epidemiology | Susceptibles | Infectives | SIS / SIR variants are a special case — coupling term is contact rate β·S·I. |
| Chemical kinetics | Autocatalytic species | Inhibitor | Lotka's original 1910 motivation: oscillating Belousov–Zhabotinsky-style reactions. |
| Economics | Capital stock | Wage share | Goodwin's 1967 growth-cycle model is literally Lotka–Volterra in disguise. |
| Cybersecurity | Vulnerable hosts | Active worms | Code-Red and Slammer infection curves fit predator-prey before patches dampened them. |
Every entry in that table came from someone noticing the same coupling structure — "a thing that grows when it eats another thing that regrows when not being eaten." Once you recognise the pattern you will see Lotka–Volterra in places that have nothing to do with biology.
Where the Model Breaks (and What to Do Next)
Honest accounting. The classical Lotka–Volterra model is the simplest possible coupling — it deliberately ignores almost every real biological complication. Each failure mode points to a more realistic extension you will meet in section 23.9 and beyond:
- Unbounded prey in the absence of predators. The model says grows forever. Reality has carrying capacity . Fix: Logistic prey term — gives the modified Rosenzweig–MacArthur model with limit cycles (asymptotically stable, NOT marginal).
- Linear predator response to prey density. A real predator saturates — once it's eaten too many rabbits it slows down. Fix: Type-II functional response — captures handling time h.
- No interspecific competition within prey or predator.Adds quadratic / terms (Lotka–Volterra competition — section 23.9).
- Deterministic. Real populations are stochastic; for small populations the closed orbit can be lost to extinction in one unlucky cycle. Fix: stochastic differential equations driven by Brownian motion.
- Spatially uniform. Real predators and prey occupy patches. Fix: reaction-diffusion PDEs — gives travelling waves and Turing patterns (Chapter 26).
- Three or more species. Lotka–Volterra with three species can be CHAOTIC. The cycle structure that kept us safe with two species disappears.
A good rule of thumb: classical Lotka–Volterra is a cartoon. It exists not because it's realistic but because it's the ONLY nonlinear two-species model where a complete analytical theory exists. Every more accurate model uses numerical methods, the conserved quantity disappears, and you fall back on phase-plane geometry — exactly what we built here.
Summary
- The Lotka–Volterra system encodes mass-action coupling between a prey and a predator population. Every term has a direct ecological reading.
- It has two equilibria: extinction (saddle) and coexistence (center).
- A conserved quantity forces every orbit to be a closed loop around the coexistence point.
- Linearizing at the coexistence point gives a Jacobian with purely imaginary eigenvalues , predicting period for small oscillations.
- Predators always lag prey by — the geometric quarter-period of a center.
- Volterra's paradox: harvesting both species at the same rate shifts equilibrium UP for prey and DOWN for predators. Stop harvesting, the reverse happens — exactly the 1920s Adriatic observation.
- Pure-Python RK4 is enough to integrate this faithfully; PyTorch autograd confirms the conserved quantity to machine precision without a single hand-derivation.
The deepest message of Lotka–Volterra: nonlinear coupling between two variables, with no time delay, no noise, and no spatial structure, is already enough to produce sustained oscillation. Predator-prey cycles are not a quirk of biology — they are a consequence of one minus sign in the right place.