Why model life with equations?
For almost a century the Hudson's Bay Company bought pelts from trappers across northern Canada, and its records show the numbers of lynx and snowshoe hare rising and falling in roughly ten-year cycles. In the Adriatic, the marine biologist Umberto D'Ancona noticed that during the First World War, when fishing was reduced, the proportion of predatory fish in the catch at ports such as Fiume (Rijeka) went up — and asked his father-in-law, the mathematician Vito Volterra, why. During the West African Ebola epidemic of 2014–16, teams of epidemiologists estimated how many people each case was infecting, to plan treatment centres and judge whether interventions were working. In each case a mathematical model turned a vague question into a precise one.
A model is a deliberately simplified description: we decide which quantities matter (population sizes, numbers infected), which processes change them (births, predation, infection), and write those processes as equations. Then mathematics tells us what the assumptions imply — equilibria, thresholds, oscillations, chaos — often things nobody expected. Finally the implications are compared with data, and the model is revised. The statistician George Box put the spirit memorably: "all models are wrong, but some are useful".
This lesson uses ordinary differential equations (solutions, equilibria, phase portraits) and linear algebra (eigenvalues, trace and determinant). Stochastic models, in which chance plays a role, connect to Markov chains.
Growth with limits
A population with plentiful resources grows at a rate proportional to its size: , so — Malthusian, exponential growth. No real population can do this for long. Pierre-François Verhulst (1838) proposed that the per-capita growth rate falls linearly as the population approaches a carrying capacity :
The logistic equation (with r = K = 1)
Small populations grow almost exponentially, then level off at the carrying capacity 1. Populations above K decline towards it. Click to draw solutions: none crosses the equilibria N = 0 and N = K.
dy/dx = y·(1 − y)
Click the field to draw a solution, or type a start point below.
Solution curves: 4. They are numerical approximations (fourth-order Runge–Kutta), not exact formulas.
Latest curve through (0, −0.05).
The picture already contains the whole qualitative story: two equilibria, (unstable: a few individuals grow) and (stable: every positive population approaches it), and S-shaped growth in between. The same idea in discrete time — a population counted once per generation — produces something far richer, as we shall see.
Equilibria, stability and the models
Definition— equilibrium, stability, Jacobian
For an autonomous system with continuously differentiable, an equilibrium is a point with . It is stable if solutions that start near stay near it for all , and locally asymptotically stable if moreover they converge to it. The Jacobian , the matrix of partial derivatives at , defines the linearisation for the deviation .
Definition— fixed points and cycles of maps
For a discrete-time model , a fixed point satisfies , and a -cycle is a set of distinct points permuted cyclically by (fixed points of ). Stability is defined as for equilibria, with in place of ; the multiplier of a fixed point is , and of a -cycle the product of along the cycle.
Definition— the classical models
- Logistic equation: , with .
- Logistic map: , with and .
- Lotka–Volterra predator–prey: prey and predators with , ().
- Lotka–Volterra competition (in scaled units): , ().
- SIR epidemic (Kermack–McKendrick): a population of split into susceptible , infectious and removed , with , , . Here is the rate of infectious contacts per infected person and the mean infectious period.
Definition— basic reproduction number
In the simple SIR model, : the expected number of new infections caused by one infectious individual — infecting at rate for an average time — in a population where everyone else is susceptible.
One species: logistic growth and harvesting
Theorem— Theorem (the logistic equation)
For the logistic equation has the unique solution
defined for all , and as . The equilibrium is unstable and is asymptotically stable.
Proof
Separating variables with partial fractions, , so , which rearranges to the formula; differentiating confirms it (this check is formalised in Lean). The right-hand side is continuously differentiable, hence locally Lipschitz, so by the Picard–Lindelöf theorem the solution is unique. Since , . For stability, has and : near solutions move away, and near they move towards it (on each side of the sign of pushes towards ).
∎
Example— Harvesting and collapse
A fishery removes a constant catch per year: . Equilibria solve , with discriminant . For there are two equilibria: the larger is stable and the smaller unstable (a population pushed below it collapses). At — the maximum sustainable yield, the largest value of , attained at — they merge, and for there is no equilibrium at all: everywhere and the population is driven to extinction. This saddle-node bifurcation is a mathematical reason why fisheries managed close to maximum yield can collapse suddenly after a small increase in effort or a bad year, as the cod fishery off Newfoundland did in the early 1990s.
Discrete generations: the logistic map and chaos
Many insects, annual plants and fish breed once a year in non-overlapping generations, so populations are naturally modelled by maps. The logistic map is the simplest non-linear one, and Robert May (1976) urged biologists to study it precisely because its behaviour is so rich.
Theorem— Theorem (stability of fixed points of maps)
Let be continuously differentiable near a fixed point . If , then is locally asymptotically stable: there is such that for every . If , it is unstable.
Proof
Choose with ; by continuity on an interval . By the mean value theorem, for , , so maps into itself and (this is the contraction-mapping argument behind the Banach fixed-point theorem). If on , the same estimate in reverse shows that every orbit starting in eventually leaves .
∎
For : the fixed points are and (in for ), with , so and . Hence extinction is stable for , and the positive equilibrium is stable exactly for . At the multiplier passes through , and a cycle of period two is born.
Theorem— Proposition (the period-2 cycle)
For the logistic map has exactly one 2-cycle, with . Its multiplier is , so it is stable for .
Proof
Points of period two are fixed points of that are not fixed points of . The quartic is divisible by , and the quotient is, up to a constant factor, , whose roots are ; they are real and distinct exactly when , i.e. . With we have , so the multiplier is . This lies in iff and , i.e. . (The identities and the multiplier are verified in Lean.)
∎
The logistic map: from equilibrium to chaos
r = 2.8: the orbit spirals into a fixed point. r = 3.2: a 2-cycle. r = 3.5: a 4-cycle. r = 3.7: no pattern. r = 3.83: a 3-cycle appears out of the chaos.
Iterates xₖ against k
- y = f(x)
- y = x
- cobweb path
Last iterates: x78 = 0.79946, x79 = 0.51304, x80 = 0.79946
The orbit appears to settle into a cycle of period 2: 0.513, 0.7995.
Fixed points in view (numerical): x* = 0 (repelling: |f′(x*)| > 1); x* = 0.6875 (repelling: |f′(x*)| > 1)
Based on 3,000 iterations in floating-point arithmetic: numerical evidence, not proof.
The road to chaos. As increases, the 2-cycle loses stability at and a stable 4-cycle appears, then an 8-cycle, and so on: a period-doubling cascade at parameters accumulating at . The gaps shrink geometrically: . Mitchell Feigenbaum (1978) discovered numerically that the same appears for every smooth unimodal map with a quadratic maximum — a universal constant — and Oscar Lanford (1982) proved his conjectures with a computer-assisted proof. Beyond , chaotic parameters and periodic "windows" interleave; the largest window, containing a stable 3-cycle, opens at . By the Li–Yorke theorem (1975), a continuous map of an interval with a point of period three has points of every period (a special case of Sharkovskii's theorem, 1964).
Chaos at . Substituting gives : the logistic map is conjugate to the doubling map , which shifts the binary digits of one place to the left. Two starting points that agree to binary digits disagree completely after about steps: sensitive dependence on initial conditions, with errors doubling at each step (Lyapunov exponent ). A deterministic model with one parameter can thus be unpredictable in practice — a sobering lesson for population forecasting, since field data with noisy, chaotic-looking fluctuations may come from very simple deterministic rules.
Stability by linearisation
Theorem— Theorem (linearised stability)
Let be an equilibrium of with continuously differentiable, and . If every eigenvalue of has negative real part, then is locally asymptotically stable. If some eigenvalue has positive real part, it is unstable.
Proof
Sketch of the stable case. Because the eigenvalues of have negative real parts, for some , so the matrix converges; it is symmetric positive definite and satisfies the Lyapunov equation . Let for . Along solutions , so for small . Since is comparable to , near , and , hence , decays exponentially. (The unstable case and the general theory are in Strogatz, Chapter 6, or Perko's Differential Equations and Dynamical Systems.)
∎
The Hartman–Grobman theorem says more: if no eigenvalue has zero real part (a hyperbolic equilibrium), the phase portrait near is a continuous deformation of that of . When some eigenvalue has zero real part — for example a centre, with purely imaginary eigenvalues — linearisation is inconclusive, and the nonlinear terms decide.
In the plane, the eigenvalues of are the roots of with and , so both have negative real part if and only if and (the trace–determinant test). The "if" direction is proved in Lean: if is a root, the imaginary part of the equation gives , so either , or and , which is impossible for . A saddle has .
Predators and prey
In the Lotka–Volterra model, prey grow exponentially without predators (), predators starve without prey (), and encounters, at a rate proportional to (the law of mass action), remove prey and feed predators. The equilibria are and , with Jacobians
The origin is a saddle. At the coexistence equilibrium and , so the eigenvalues are : a linear centre, and the linearisation theorem says nothing. A conserved quantity settles the question.
Theorem— Theorem (Volterra's conserved quantity)
Along every solution with , the function
is constant. has a strict global minimum on the open quadrant at , and its level sets above the minimum are closed curves around it, so every positive solution other than the equilibrium is periodic.
Proof
By the chain rule, (formalised in Lean). Write with and . Each is strictly convex, tends to at and at , and has its minimum at (resp. ). Hence for the set is a closed curve encircling the equilibrium: on each vertical line with it contains exactly two points, merging at the two values of where . A solution starting on this compact curve stays on it, contains no equilibrium, and moves with speed bounded below, so it goes round the curve in finite time and repeats.
∎
Predator–prey cycles
Every orbit is a closed curve around the coexistence equilibrium. Prey peaks come before predator peaks. The widget's numerical integrator follows the level curves of V.
x′ = x·(1 − 0.5·y), y′ = y·(0.5·x − 1)
Click the plane to start a trajectory, or type a start point below.
Trajectories: 3. They are numerical approximations (fourth-order Runge–Kutta), not exact formulas. The latest trajectory closes up: it is a periodic orbit.
Nonlinear system. Equilibria found numerically in this window, classified by their linearisation (Jacobian matrix):
- 1. (0, 0): saddle (unstable) (λ = −1, 1)
- 2. (2, 2): centre: closed orbits around it (λ = 0 ± 1i)
A centre of the linearisation is inconclusive for a nonlinear system: the true orbits may close up (as for the pendulum and predator–prey) or spiral slowly.
Theorem— Theorem (cycle averages)
If a positive solution has period , then its averages over a period are the equilibrium values: and .
Proof
. Integrating over a period, . The same argument with gives the predator average. (The prey half is formalised in Lean, using the fundamental theorem of calculus.)
∎
This is Volterra's principle. Fishing that removes both species in proportion to their numbers changes to and to , so the average prey rises and the average predators fall. Reduced fishing during the war did the opposite — D'Ancona's observation explained. The same principle warns that spraying an insecticide that kills both a pest and its natural enemy can increase the pest on average.
Structural instability. The closed orbits are fragile: almost any change to the model destroys them. With self-limiting prey, , the coexistence equilibrium (when it exists) has and and becomes a stable spiral: oscillations damp out. With a saturating ("Holling type II", 1959) predator response , as in the Rosenzweig–MacArthur model (1963), the equilibrium can lose stability through a Hopf bifurcation and give way to a stable limit cycle — robust oscillations of a fixed amplitude, closer to the lynx–hare records. Rosenzweig's "paradox of enrichment" (1971) is a consequence: increasing the prey's carrying capacity can destabilise the system. A model whose qualitative behaviour changes under arbitrarily small perturbations should not be trusted for quantitative predictions.
Competition
Two species competing for the same resources follow , , where measures the effect of on relative to 's effect on itself. Besides , and , there may be a coexistence equilibrium
which is positive when are both less than or both greater than .
Theorem— Proposition (coexistence or exclusion)
If and , the coexistence equilibrium is locally asymptotically stable. If and , it is a saddle, and the two single-species equilibria are both stable (the winner depends on the starting point). If , there is no coexistence equilibrium and is stable: species excludes .
Proof
At a positive equilibrium, and , so the Jacobian simplifies to , with and . If both , then and the trace–determinant test gives stability; if both exceed , : a saddle. At , has eigenvalues and , negative exactly when ; symmetrically for . The global statements follow from the nullclines, which divide the quadrant into regions where the direction of motion is fixed.
∎
Strong competition: a ≈ 1.5 both ways
The coexistence equilibrium (0.4, 0.4) is a saddle; starting points on either side of its stable separatrix lead to the exclusion of one species.
x′ = x·(1 − x − 1.5·y), y′ = y·(1 − y − 1.5·x)
Click the plane to start a trajectory, or type a start point below.
Trajectories: 4. They are numerical approximations (fourth-order Runge–Kutta), not exact formulas.
Nonlinear system. Equilibria found numerically in this window, classified by their linearisation (Jacobian matrix):
- 1. (0, 0): unstable degenerate node (repeated eigenvalue) (λ = 1 (×2))
- 2. (0, 1): stable node (λ = −1, −0.5)
- 3. (0.4, 0.4): saddle (unstable) (λ = −1, 0.2)
- 4. (1, 0): stable node (λ = −1, −0.5)
Coexistence requires that each species limits itself more than it limits its competitor. Georgii Gause's laboratory experiments with Paramecium (1934) supported the resulting competitive exclusion principle: two species competing for exactly the same limiting resource cannot coexist indefinitely.
Epidemics: the SIR model
The SIR model assumes a closed population (no births, deaths or migration on the time scale of the epidemic), homogeneous mixing (everyone equally likely to meet everyone), infection at rate (mass action), recovery at constant rate (an exponentially distributed infectious period with mean ), and lasting immunity. Adding the three equations, : the population is constant.
Theorem— Theorem (threshold)
While , the number infected is increasing exactly when . In particular, an infection introduced into a fully susceptible population () initially grows if and only if ; since decreases, the epidemic peaks exactly when falls to and declines afterwards.
Proof
, and . Since , the factor decreases in time, so increases while it is positive and decreases once it is negative.
∎
Theorem— Theorem (final-size relation)
Along every solution with , the quantity is constant. Consequently, if the epidemic starts with and negligible , the number who escape infection satisfies
For and , the escaping fraction is the unique root in of .
Proof
The derivative of is (formalised in Lean). As , ( is increasing and bounded, so , and is bounded). Equating the invariant at (with ) and at gives the relation. For , let : , and , so just below and has a root in ; it is unique because is strictly concave. The root corresponds to no epidemic.
∎
The final-size relation
The curves meet at s = 1 and, when R₀ > 1, at one more point: the fraction that escapes infection. For R₀ = 2.5 about 11% escape, so 89% are eventually infected.
- s
- exp(−R₀(1 − s))
- s: y = 1
- exp(−R₀(1 − s)): y = 1
Herd immunity and overshoot. If a fraction is immune (by vaccination or previous infection), an introduced infection grows only if ; the herd immunity threshold is — about for and – for measles, whose is classically estimated at –. An unchecked epidemic infects more than this — for — because infections already under way continue after has fallen below . The peak prevalence, from the invariant at the moment , is (for ).
SIR in the (S, I) plane, R₀ = 3 (N = 1)
Infections rise while S > 1/3 and fall afterwards. Each orbit ends on the S-axis at the fraction that escapes; starting from S ≈ 1, about 6%.
x′ = −0.3·x·y, y′ = 0.3·x·y − 0.1·y
Click the plane to start a trajectory, or type a start point below.
Trajectories: 4. They are numerical approximations (fourth-order Runge–Kutta), not exact formulas.
Nonlinear system. Equilibria found numerically in this window, classified by their linearisation (Jacobian matrix):
- 1. (0.071, 0): degenerate: the determinant is 0, so the equilibria are not isolated (λ = −0.079, 0)
- 2. (0.214, 0): degenerate: the determinant is 0, so the equilibria are not isolated (λ = −0.036, 0)
- 3. (0.357, 0): degenerate: the determinant is 0, so the equilibria are not isolated (λ = 0, 0.007)
- 4. (0.5, 0): degenerate: the determinant is 0, so the equilibria are not isolated (λ = 0, 0.05)
- 5. (0.643, 0): degenerate: the determinant is 0, so the equilibria are not isolated (λ = 0, 0.093)
- 6. (0.786, 0): degenerate: the determinant is 0, so the equilibria are not isolated (λ = 0, 0.136)
What is, and is not. combines a contact rate and a duration: it depends on how people meet (household size, schools, markets, transport), on the environment (season, ventilation, mosquito density for malaria), on the pathogen, and on behaviour. It is a property of a pathogen in a population, not a biological constant. Estimates for the same disease vary widely between settings, and the effective reproduction number , which accounts for immunity and interventions, is what epidemic monitoring tracks. In 2014 the WHO Ebola Response Team estimated reproduction numbers of roughly – in Guinea, Liberia and Sierra Leone before control measures took hold.
Chance, space and the limits of models
Small numbers and chance. Deterministic equations describe large populations. When an infection is introduced by one person, chance dominates: each case might infect nobody. In the stochastic SIR model the early phase is a branching process, and with an exponentially distributed infectious period the probability that the introduction dies out by chance is (for ). Small populations also drift genetically and can go extinct by bad luck even when the deterministic model predicts persistence; the Wright–Fisher chain in Markov chains is the basic model.
Space. Populations spread: Fisher (1937) and Kolmogorov, Petrovsky and Piskunov (1937) showed that has travelling waves moving at speed , a model for the spread of advantageous genes and invading species. Alan Turing (1952) showed that two diffusing, reacting chemicals can spontaneously form spatial patterns — a proposed mechanism for spots and stripes on animal coats, now supported in several developmental systems.
Models are not forecasts. A model's output is a conditional statement: if these mechanisms, with these parameters, then this. Its predictions are only as good as its assumptions, and structural choices (homogeneous mixing versus contact networks, fixed versus changing behaviour) can matter more than parameter values. Epidemic models are often used to compare scenarios — what happens with and without an intervention — rather than to forecast the future, and because people change their behaviour in response to an epidemic (and to model projections), a projection that leads to action may never come true, which does not make it wrong. Good practice reports parameter uncertainty, tests sensitivity to assumptions, compares several model structures, and confronts the model with data.
Mathematical biology in Lean and Mathlib
Mathlib has the calculus needed here — HasDerivAt, logarithms and exponentials, the fundamental theorem of calculus — but no library of biological models, and not the linearisation theorem, Hartman–Grobman or Poincaré–Bendixson. The module proves the lesson's computations as theorems about arbitrary solutions: a hypothesis HasDerivAt x (α * x t - β * x t * y t) t says that x satisfies the prey equation at time t.
Logistic growth: the explicit solution, verified by differentiation, and its limit:
/-- **The logistic equation** `N' = r N (1 − N/K)` is solved by
`N(t) = K / (1 + C e^{−rt})` (found by separation of variables; here verified by
differentiation). With `C = K/N₀ − 1` this is the solution with `N(0) = N₀`. -/
theorem logistic_ode_solves {r K C : ℝ} (hK : K ≠ 0) (hC : 0 ≤ C) (t : ℝ) :
HasDerivAt (fun s => K / (1 + C * exp (-(r * s))))
(r * (K / (1 + C * exp (-(r * t)))) * (1 - K / (1 + C * exp (-(r * t))) / K)) t := by
have hpos : 0 < 1 + C * exp (-(r * t)) := by positivity
have h0 := (hasDerivAt_id t).const_mul (-r)
have hinner : HasDerivAt (fun s => -(r * s)) (-r) t := by
convert h0 using 1
· funext s
simp only [id]
ring
· ring
have hd : HasDerivAt (fun s => 1 + C * exp (-(r * s))) (C * (exp (-(r * t)) * (-r))) t :=
((hinner.exp).const_mul C).const_add 1
have h := (hasDerivAt_const t K).div hd hpos.ne'
convert h using 1
field_simp
ring/-- Every positive logistic solution approaches the **carrying capacity**: if `r > 0` then
`K / (1 + C e^{−rt}) → K` as `t → ∞`, because `e^{−rt} → 0`. -/
theorem logistic_ode_limit {r K C : ℝ} (hr : 0 < r) :
Tendsto (fun t => K / (1 + C * exp (-(r * t)))) atTop (𝓝 K) := by
have hexp : Tendsto (fun t => exp (-(r * t))) atTop (𝓝 0) := by
have : Tendsto (fun t => r * t) atTop atTop := tendsto_id.const_mul_atTop hr
exact tendsto_exp_neg_atTop_nhds_zero.comp this |>.congr fun t => rfl
have hden : Tendsto (fun t => 1 + C * exp (-(r * t))) atTop (𝓝 1) := by
simpa using (hexp.const_mul C).const_add 1
have h := (tendsto_const_nhds (x := K)).div hden one_ne_zero
rw [div_one] at h
exact hThe logistic map: fixed points, the multiplier , the stability range , the 2-cycle and its multiplier; and Mathlib's Banach fixed-point theorem, the principle behind the stability criterion:
/-- The **logistic map** `f_r(x) = r x (1 − x)`, a discrete-time model of a population
(as a fraction of its maximum) in non-overlapping generations. -/
def logisticMap (r x : ℝ) : ℝ := r * x * (1 - x)/-- **Fixed points of the logistic map.** For `r ≠ 0`, `f_r(x) = x` exactly when `x = 0`
(extinction) or `x = 1 − 1/r` (the non-trivial equilibrium, in `(0, 1)` when `r > 1`).
Proof: `r x (1 − x) − x = x (r − 1 − r x)`. -/
theorem logistic_fixed_points {r : ℝ} (hr : r ≠ 0) (x : ℝ) :
logisticMap r x = x ↔ x = 0 ∨ x = 1 - 1 / r := by
unfold logisticMap
constructor
· intro h
have hfac : x * (r - 1 - r * x) = 0 := by linarith [h]
rcases mul_eq_zero.mp hfac with h0 | h1
· exact Or.inl h0
· right
field_simp
linarith
· rintro (rfl | rfl)
· ring
· field_simp
ring/-- The derivative of the logistic map is `f_r'(x) = r (1 − 2x)`; at the non-trivial fixed
point `x* = 1 − 1/r` it equals `2 − r`. -/
theorem logistic_map_deriv {r : ℝ} (hr : r ≠ 0) :
HasDerivAt (logisticMap r) (2 - r) (1 - 1 / r) := by
have h : HasDerivAt (fun x => r * x * (1 - x)) (r * 1 * (1 - (1 - 1 / r)) + r * (1 - 1 / r) * (-1))
(1 - 1 / r) :=
((hasDerivAt_id' _).const_mul r).mul ((hasDerivAt_id' _).const_sub 1 |>.congr_deriv (by simp))
have hv : r * 1 * (1 - (1 - 1 / r)) + r * (1 - 1 / r) * (-1) = 2 - r := by
field_simp
ring
rw [hv] at h
exact h/-- **Stability of the non-trivial equilibrium.** A fixed point of a smooth map is
asymptotically stable when `|f'(x*)| < 1`; here `|2 − r| < 1` exactly when `1 < r < 3`.
At `r = 3` the multiplier passes through `−1` and the period-doubling cascade begins. -/
theorem logistic_stable_iff (r : ℝ) : |2 - r| < 1 ↔ 1 < r ∧ r < 3 := by
rw [abs_lt]
constructor <;> intro h <;> constructor <;> linarith [h.1, h.2]/-- **The period-2 cycle.** For `r ≥ 3`, with `s = √((r − 3)(r + 1))`, the points
`x± = (r + 1 ± s)/(2r)` are swapped by the logistic map: `f_r(x₊) = x₋` and `f_r(x₋) = x₊`.
(They coincide with the fixed point `1 − 1/r` at `r = 3` and separate for `r > 3`.)
Proof: expand, using `s² = r² − 2r − 3`. -/
theorem logistic_period_two {r : ℝ} (hr : 3 ≤ r) :
let s := Real.sqrt ((r - 3) * (r + 1))
logisticMap r ((r + 1 + s) / (2 * r)) = (r + 1 - s) / (2 * r) ∧
logisticMap r ((r + 1 - s) / (2 * r)) = (r + 1 + s) / (2 * r) := by
intro s
have hr0 : r ≠ 0 := by linarith
have hs : s ^ 2 = (r - 3) * (r + 1) := Real.sq_sqrt (by nlinarith)
unfold logisticMap
constructor
· field_simp
linear_combination -hs
· field_simp
linear_combination -hs/-- **Stability of the 2-cycle.** The multiplier of the cycle — the derivative of `f_r ∘ f_r`
along it, `f_r'(x₊) f_r'(x₋) = r²(1 − 2x₊)(1 − 2x₋)` — equals `−r² + 2r + 4`. It lies in
`(−1, 1)` exactly for `3 < r < 1 + √6 ≈ 3.449`, where the 2-cycle is stable and the next
doubling begins. -/
theorem period_two_multiplier {r : ℝ} (hr : 3 ≤ r) :
let s := Real.sqrt ((r - 3) * (r + 1))
(r * (1 - 2 * ((r + 1 + s) / (2 * r)))) * (r * (1 - 2 * ((r + 1 - s) / (2 * r)))) =
-r ^ 2 + 2 * r + 4 := by
intro s
have hr0 : r ≠ 0 := by linarith
have hs : s ^ 2 = (r - 3) * (r + 1) := Real.sq_sqrt (by nlinarith)
field_simp
linear_combination -hs/-- **Why stable fixed points attract: the Banach fixed-point theorem.** If a map of a complete
metric space is a contraction (`dist (f x) (f y) ≤ K dist x y` with `K < 1`), its iterates
from any starting point converge to its unique fixed point. Near a fixed point with
`|f'(x*)| < 1` a smooth map is a contraction of a small interval, which is the proof of the
stability criterion. This is Mathlib's `ContractingWith.tendsto_iterate_fixedPoint`. -/
theorem contraction_converges {α : Type*} [MetricSpace α] [CompleteSpace α] [Nonempty α]
{K : NNReal} {f : α → α} (hf : ContractingWith K f) (x : α) :
Tendsto (fun n => f^[n] x) atTop (𝓝 (ContractingWith.fixedPoint f hf)) :=
hf.tendsto_iterate_fixedPoint xPredator and prey: Volterra's conserved quantity along any positive solution, and the prey average over a cycle, via the fundamental theorem of calculus:
/-- **The Lotka–Volterra conserved quantity.** Along any positive solution of
`x' = αx − βxy` (prey), `y' = δxy − γy` (predators), the function
`V(x, y) = δx − γ log x + βy − α log y` has zero derivative, so it is constant and orbits lie
on its level curves (which are closed curves around the equilibrium `(γ/δ, α/β)`).
Proof: `V' = (δ − γ/x) x' + (β − α/y) y' = (δx − γ)(α − βy) + (βy − α)(δx − γ) = 0`. -/
theorem lotka_volterra_conserved {α β γ δ : ℝ} {x y : ℝ → ℝ} {t : ℝ} (hx : 0 < x t)
(hy : 0 < y t) (hx' : HasDerivAt x (α * x t - β * x t * y t) t)
(hy' : HasDerivAt y (δ * x t * y t - γ * y t) t) :
HasDerivAt (fun s => δ * x s - γ * log (x s) + β * y s - α * log (y s)) 0 t := by
have h1 := hx'.const_mul δ
have h2 := (hx'.log hx.ne').const_mul γ
have h3 := hy'.const_mul β
have h4 := (hy'.log hy.ne').const_mul α
have h := ((h1.sub h2).add h3).sub h4
convert h using 1
field_simp
ring/-- **Averages over a cycle equal the equilibrium.** If a positive solution is periodic with
period `T > 0` (`y(T) = y(0)`), then the average prey population over a period is
`(1/T) ∫₀ᵀ x = γ/δ`. Proof: `(log y)' = y'/y = δx − γ`; integrate over a period, where
`log y` returns to its initial value. (This is Volterra's explanation of D'Ancona's
observation that reduced fishing during the First World War raised the share of predators.) -/
theorem lotka_volterra_average {α β γ δ T : ℝ} (hδ : δ ≠ 0) (hT : 0 < T) {x y : ℝ → ℝ}
(hy : ∀ t, 0 < y t) (hx' : ∀ t, HasDerivAt x (α * x t - β * x t * y t) t)
(hy' : ∀ t, HasDerivAt y (δ * x t * y t - γ * y t) t) (hper : y T = y 0) :
(∫ t in (0 : ℝ)..T, x t) / T = γ / δ := by
have hxc : Continuous x := continuous_iff_continuousAt.mpr fun t => (hx' t).continuousAt
-- `(log y)' = δ x − γ`.
have hlog : ∀ t, HasDerivAt (fun s => log (y s)) (δ * x t - γ) t := by
intro t
have := (hy' t).log (hy t).ne'
convert this using 1
field_simp [(hy t).ne']
have hint : IntervalIntegrable (fun t => δ * x t - γ) MeasureTheory.volume 0 T :=
((hxc.const_mul δ).sub continuous_const).intervalIntegrable 0 T
have hftc := intervalIntegral.integral_eq_sub_of_hasDerivAt (fun t _ => hlog t) hint
rw [hper, sub_self, intervalIntegral.integral_sub
((hxc.const_mul δ).intervalIntegrable 0 T) intervalIntegrable_const,
intervalIntegral.integral_const_mul, intervalIntegral.integral_const, smul_eq_mul,
sub_zero] at hftc
field_simp
linarithEpidemics: conservation of the population, the threshold, the invariant behind the final-size relation, and the herd immunity threshold:
/-- **The SIR model conserves the population.** If `S' = −βSI/N`, `I' = βSI/N − γI` and
`R' = γI`, then `(S + I + R)' = 0`: everyone is always susceptible, infected or removed. -/
theorem sir_total_constant {β γ N : ℝ} {S I R : ℝ → ℝ} {t : ℝ}
(hS : HasDerivAt S (-(β * S t * I t / N)) t)
(hI : HasDerivAt I (β * S t * I t / N - γ * I t) t) (hR : HasDerivAt R (γ * I t) t) :
HasDerivAt (fun s => S s + I s + R s) 0 t := by
convert (hS.add hI).add hR using 1
ring/-- **The epidemic threshold.** When infection is present (`I > 0`), the number infected is
increasing (`I' > 0`) exactly when `R₀ · S/N > 1`, where `R₀ = β/γ` (`β, γ, N > 0`). -/
theorem sir_threshold {β γ N S I : ℝ} (hγ : 0 < γ) (hN : 0 < N) (hI : 0 < I) :
0 < β * S * I / N - γ * I ↔ 1 < β / γ * (S / N) := by
have key : β * S * I / N - γ * I = γ * I * (β / γ * (S / N) - 1) := by
field_simp
rw [key]
constructor
· intro h
have := (mul_pos_iff_of_pos_left (mul_pos hγ hI)).mp h
linarith
· intro h
exact mul_pos (mul_pos hγ hI) (by linarith)/-- **The invariant behind the final-size relation.** Along a solution of the SIR model with
`S > 0`, the quantity `S + I − (N/R₀) log S` (with `R₀ = β/γ`, written `γN/β`) is constant.
Proof: its derivative is `S' + I' − (γN/β) S'/S = −γI + γI = 0`. Comparing its values at the
start (`I ≈ 0`, `S ≈ N`) and the end (`I = 0`) of an epidemic gives the final-size relation
`log(S∞/S₀) = R₀ (S∞ − S₀)/N`. -/
theorem sir_invariant {β γ N : ℝ} (hβ : β ≠ 0) (hN : N ≠ 0) {S I : ℝ → ℝ} {t : ℝ}
(hpos : 0 < S t) (hS : HasDerivAt S (-(β * S t * I t / N)) t)
(hI : HasDerivAt I (β * S t * I t / N - γ * I t) t) :
HasDerivAt (fun s => S s + I s - γ * N / β * log (S s)) 0 t := by
convert (hS.add hI).sub ((hS.log hpos.ne').const_mul (γ * N / β)) using 1
field_simp
ring/-- **Herd immunity threshold.** If a fraction `p` of the population is immune, an
introduced infection grows only if `R₀ (1 − p) > 1`. So it cannot grow exactly when
`p ≥ 1 − 1/R₀` (for `R₀ > 0`): about `92–94%` for measles (`R₀ ≈ 12–18`). -/
theorem herd_immunity {R₀ p : ℝ} (hR : 0 < R₀) : R₀ * (1 - p) ≤ 1 ↔ 1 - 1 / R₀ ≤ p := by
constructor
· intro h
have : 1 - p ≤ 1 / R₀ := by
rw [le_div_iff₀ hR]
linarith
linarith
· intro h
have : 1 - p ≤ 1 / R₀ := by linarith
rw [le_div_iff₀ hR] at this
linarithLinear stability: the trace–determinant test, for complex roots of the characteristic polynomial:
/-- **The trace–determinant test.** If a real `2 × 2` Jacobian has trace `τ < 0` and
determinant `Δ > 0`, every root `λ` of its characteristic polynomial `λ² − τλ + Δ` has
negative real part, so the equilibrium is locally asymptotically stable (by the linearisation
theorem). Proof: write `λ = a + bi`. The imaginary part of the equation is `b(2a − τ) = 0`;
if `b ≠ 0` then `a = τ/2 < 0`, and if `b = 0` then `a² − τa + Δ = 0` forces `a < 0`. -/
theorem trace_det_stable {τ Δ : ℝ} (hτ : τ < 0) (hΔ : 0 < Δ) {z : ℂ}
(hz : z ^ 2 - τ * z + Δ = 0) : z.re < 0 := by
have hre := congrArg Complex.re hz
have him := congrArg Complex.im hz
simp [pow_two] at hre him
by_cases hb : z.im = 0
· rw [hb] at hre
by_contra hnn
push Not at hnn
nlinarith
· have h2 : 2 * z.re - τ = 0 := by
have : z.im * (2 * z.re - τ) = 0 := by linarith
rcases mul_eq_zero.mp this with h | h
· exact absurd h hb
· exact h
linarithWhat is and is not formalised. Proved here: all the identities and inequalities above — explicit solutions, fixed points and multipliers, the 2-cycle, the conserved quantities, the cycle average of the prey, the SIR threshold and invariant, herd immunity and the trace–determinant test (in the "if" direction). Cited from Mathlib: the Banach fixed-point theorem. Not formalised: existence and uniqueness of solutions for these models (Mathlib's Picard–Lindelöf theorem would provide them), the linearisation theorem, the closedness of the Lotka–Volterra level curves, the final-size limit argument (), the competition analysis, period doubling beyond period two, Feigenbaum universality and chaos.
Cascades, cycles and epidemics, computed
First, the logistic map: attractor periods, superstable parameters found by Newton's method with the ratios of their gaps approaching Feigenbaum's constant, and Lyapunov exponents.
"""The logistic map x -> r x (1 - x): period doubling, Feigenbaum's constant and chaos.
For each growth rate r we iterate the map past its transient and detect the
period of the attractor. Then we locate the "superstable" parameters R_n at
which the critical point 1/2 lies on a cycle of period 2^n, by Newton's
method, and form the ratios (R_n - R_(n-1)) / (R_(n+1) - R_n), which approach
Feigenbaum's constant 4.669... Finally the Lyapunov exponent - the average of
log|f'(x_k)| along the orbit - is negative on stable cycles and equals log 2
at r = 4, where nearby orbits separate exponentially.
"""
import math
def logistic(r, x):
"""One step of the logistic map."""
return r * x * (1 - x)
def orbit(r, x0, n):
"""[x_0, ..., x_n] for the logistic map."""
xs = [x0]
for _ in range(n):
xs.append(logistic(r, xs[-1]))
return xs
def attractor_period(r, x0=0.2, transient=3000, max_period=64, tol=1e-9):
"""Smallest p <= max_period with x_(n+p) = x_n after the transient, else None."""
x = x0
for _ in range(transient):
x = logistic(r, x)
start = x
for p in range(1, max_period + 1):
x = logistic(r, x)
if abs(x - start) < tol:
return p
return None
def superstable_gap(r, n):
"""f_r^(2^n)(1/2) - 1/2: zero exactly when 1/2 lies on a 2^n-cycle."""
x = 0.5
for _ in range(2 ** n):
x = logistic(r, x)
return x - 0.5
def superstable_parameters(count):
"""R_0 = 2, R_1 = 1 + sqrt 5, ... found by Newton's method with a numerical
derivative, each started from an extrapolation of the previous gaps."""
rs = [2.0, 1 + math.sqrt(5)]
delta = 4.0
while len(rs) < count:
n = len(rs)
r = rs[-1] + (rs[-1] - rs[-2]) / delta
for _ in range(60):
g = superstable_gap(r, n)
h = 1e-10
dg = (superstable_gap(r + h, n) - superstable_gap(r - h, n)) / (2 * h)
step = g / dg
r -= step
if abs(step) < 1e-14:
break
rs.append(r)
delta = (rs[-2] - rs[-3]) / (rs[-1] - rs[-2])
return rs
def lyapunov(r, x0=0.2, transient=1000, n=20000):
"""Average of log|f_r'(x_k)| = log|r (1 - 2 x_k)| along the orbit."""
x = x0
for _ in range(transient):
x = logistic(r, x)
total = 0.0
for _ in range(n):
total += math.log(abs(r * (1 - 2 * x)) + 1e-300)
x = logistic(r, x)
return total / n
def main():
print("Attractor period after 3000 iterations from x0 = 0.2:")
for r in [2.8, 3.2, 3.5, 3.56, 3.7, 3.83]:
p = attractor_period(r)
print(f" r = {r:<5} period {p if p else 'none found (chaotic?)'}")
rs = superstable_parameters(8)
print()
print("Superstable parameters R_n and Feigenbaum ratios:")
for n, r in enumerate(rs):
line = f" R_{n} = {r:.10f}"
if n >= 2:
ratio = (rs[n - 1] - rs[n - 2]) / (rs[n] - rs[n - 1])
line += f" ratio {ratio:.5f}"
print(line)
print(" (Feigenbaum's constant: 4.66920...)")
print()
print("Lyapunov exponents (negative: stable cycle; positive: chaos):")
for r in [2.8, 3.2, 3.5, 3.7, 3.83, 4.0]:
print(f" r = {r:<5} {lyapunov(r):+.4f}")
print(f" log 2 = {math.log(2):.4f}")
a, b = orbit(4.0, 0.3, 40), orbit(4.0, 0.3 + 1e-10, 40)
gaps = [abs(x - y) for x, y in zip(a, b)]
first = next(i for i, g in enumerate(gaps) if g > 0.1)
print(f"At r = 4, starts 1e-10 apart differ by > 0.1 after {first} steps.")
print()
print("Epistemic status: floating-point iteration of finitely many steps.")
print("Periods, ratios and exponents are numerical estimates; the")
print("universality of Feigenbaum's constant was proved by Lanford (1982)")
print("with computer assistance, and chaos at r = 4 is a theorem.")
if __name__ == "__main__":
main()Next, the Lotka–Volterra model: Euler's method spirals outward and violates the conservation law; the Runge–Kutta method respects it, and the cycle averages match the equilibrium.
"""Lotka-Volterra cycles: what a conserved quantity says about numerical methods.
Prey x and predators y obey x' = a x - b x y, y' = d x y - c y. The quantity
V = d x - c ln x + b y - a ln y is exactly conserved, so true orbits are closed.
We integrate with Euler's method and with classical Runge-Kutta (RK4) and
watch V: Euler's orbit spirals outward (V grows steadily), while RK4 keeps V
constant to many digits. With RK4 we also measure the period and check that
the average prey population over one cycle equals the equilibrium value c/d.
"""
import math
A, B, C, D = 1.0, 0.5, 0.75, 0.25 # prey growth, predation, predator death, conversion
def rhs(x, y):
"""Lotka-Volterra vector field."""
return A * x - B * x * y, D * x * y - C * y
def conserved(x, y):
"""V = d x - c ln x + b y - a ln y."""
return D * x - C * math.log(x) + B * y - A * math.log(y)
def euler_step(x, y, h):
"""One explicit Euler step."""
fx, fy = rhs(x, y)
return x + h * fx, y + h * fy
def rk4_step(x, y, h):
"""One classical Runge-Kutta step."""
k1 = rhs(x, y)
k2 = rhs(x + h / 2 * k1[0], y + h / 2 * k1[1])
k3 = rhs(x + h / 2 * k2[0], y + h / 2 * k2[1])
k4 = rhs(x + h * k3[0], y + h * k3[1])
return (x + h / 6 * (k1[0] + 2 * k2[0] + 2 * k3[0] + k4[0]),
y + h / 6 * (k1[1] + 2 * k2[1] + 2 * k3[1] + k4[1]))
def run(step, x, y, h, n):
"""Positions after 0, 1, ..., n steps."""
path = [(x, y)]
for _ in range(n):
x, y = step(x, y, h)
path.append((x, y))
return path
def period_and_averages(x0, y0, h=0.001, t_max=40.0):
"""Period (time between successive upward crossings of y = a/b by the
prey-rich branch) and the averages of x and y over that period, with RK4."""
x, y = x0, y0
crossings, t = [], 0.0
sums, sx, sy = [], 0.0, 0.0
while t < t_max and len(crossings) < 3:
nx, ny = rk4_step(x, y, h)
sx += h * (x + nx) / 2
sy += h * (y + ny) / 2
if y < A / B <= ny and nx > C / D:
crossings.append(t + h)
sums.append((sx, sy))
x, y, t = nx, ny, t + h
period = crossings[2] - crossings[1]
avg_x = (sums[2][0] - sums[1][0]) / period
avg_y = (sums[2][1] - sums[1][1]) / period
return period, avg_x, avg_y
def main():
x0, y0, h, n = 4.0, 1.0, 0.01, 3000
v0 = conserved(x0, y0)
eul = run(euler_step, x0, y0, h, n)
rk = run(rk4_step, x0, y0, h, n)
print(f"a = {A}, b = {B}, c = {C}, d = {D}; equilibrium (c/d, a/b) ="
f" ({C / D:g}, {A / B:g})")
print(f"Start (x, y) = ({x0}, {y0}), V = {v0:.6f}; step h = {h}")
print(" t V (Euler) V (RK4) max prey (Euler, RK4)")
for k in [0, 500, 1000, 2000, 3000]:
me = max(p[0] for p in eul[:k + 1])
mr = max(p[0] for p in rk[:k + 1])
print(f"{k * h:>5.0f} {conserved(*eul[k]):>12.6f} {conserved(*rk[k]):>12.6f}"
f" {me:>8.3f} {mr:>8.3f}")
print("Euler's orbit spirals outward; RK4 stays on the closed level curve.")
print()
period, ax, ay = period_and_averages(x0, y0)
print(f"RK4 period of the cycle: {period:.3f}"
f" (small-oscillation value 2 pi/sqrt(ac) = {2 * math.pi / math.sqrt(A * C):.3f})")
print(f"Average prey over a cycle {ax:.4f} (equilibrium c/d = {C / D:.4f})")
print(f"Average predators {ay:.4f} (equilibrium a/b = {A / B:.4f})")
print()
print("Epistemic status: floating-point integration with fixed steps. The")
print("conservation of V and the cycle averages are theorems (V and the")
print("prey average are proved in Lean); the numbers show how well each")
print("method respects them.")
if __name__ == "__main__":
main()Finally, the SIR model solved numerically for several values of and compared with the final-size relation, the peak formula and the herd immunity threshold.
"""The SIR epidemic: numerical solution versus the final-size relation.
We solve S' = -beta S I, I' = beta S I - gamma I, R' = gamma I (fractions of a
population, N = 1) with the classical Runge-Kutta method, starting from one
infected person in ten thousand. We compare the simulated final size and peak
with the exact predictions of the lesson: the final susceptible fraction s
solves s = exp(-R0 (1 - s)), and the peak prevalence is
1 - (1 + ln R0)/R0. We also check that the invariant S + I - (1/R0) ln S stays
constant along the numerical solution.
"""
import math
def sir_rhs(state, beta, gamma):
"""Right-hand side of the SIR equations (population fractions)."""
s, i, r = state
infection = beta * s * i
return (-infection, infection - gamma * i, gamma * i)
def rk4_step(state, h, beta, gamma):
"""One classical Runge-Kutta step."""
def shift(base, k, c):
return tuple(b + c * x for b, x in zip(base, k))
k1 = sir_rhs(state, beta, gamma)
k2 = sir_rhs(shift(state, k1, h / 2), beta, gamma)
k3 = sir_rhs(shift(state, k2, h / 2), beta, gamma)
k4 = sir_rhs(shift(state, k3, h), beta, gamma)
return tuple(x + h / 6 * (a + 2 * b + 2 * c + d)
for x, a, b, c, d in zip(state, k1, k2, k3, k4))
def simulate(r0, gamma=0.1, i0=1e-4, h=0.5, days=1500):
"""Return (final S, peak I, day of peak, max drift of the invariant)."""
beta = r0 * gamma
state = (1 - i0, i0, 0.0)
def invariant(st):
return st[0] + st[1] - math.log(st[0]) / r0
v0 = invariant(state)
peak, peak_day, drift = state[1], 0.0, 0.0
for step in range(1, int(days / h) + 1):
state = rk4_step(state, h, beta, gamma)
if state[1] > peak:
peak, peak_day = state[1], step * h
drift = max(drift, abs(invariant(state) - v0))
return state[0], peak, peak_day, drift
def final_size(r0, s0=1.0, tol=1e-15):
"""Root s in (0, 1) of s = s0 exp(-R0 (1 - s)) for R0 > 1, by Newton's method."""
s = 0.5 / r0
for _ in range(100):
g = s - s0 * math.exp(-r0 * (1 - s))
dg = 1 - r0 * s0 * math.exp(-r0 * (1 - s))
step = g / dg
s -= step
if abs(step) < tol:
break
return s
def peak_prevalence(r0):
"""Peak infected fraction 1 - (1 + ln R0)/R0 (for S(0) = 1, I(0) = 0+)."""
return 1 - (1 + math.log(r0)) / r0
def main():
print("SIR with gamma = 0.1/day, I(0) = 1e-4, RK4 with step 0.5 days")
print(" R0 | attack rate: RK4 final-size | peak I: RK4 formula | day"
" | herd 1-1/R0")
for r0 in [1.5, 2.0, 3.0, 5.0]:
s_end, peak, day, drift = simulate(r0)
s_inf = final_size(r0)
print(f" {r0:>3} | {1 - s_end:.4f} {1 - s_inf:.4f} |"
f" {peak:.4f} {peak_prevalence(r0):.4f} | {day:>3.0f} |"
f" {1 - 1 / r0:.3f}")
print()
_, _, _, drift = simulate(3.0)
print(f"Largest drift of S + I - ln(S)/R0 along the R0 = 3 run: {drift:.1e}")
print("Attack rates exceed the herd immunity threshold: the epidemic")
print("overshoots, because infections continue after S/N falls below 1/R0.")
print("Small differences from the formulas come from I(0) > 0.")
print()
print("Epistemic status: numerical solution (RK4) of one deterministic model")
print("with chosen parameters, compared with exact relations derived in the")
print("lesson. It says nothing about whether real epidemics follow the SIR")
print("assumptions (homogeneous mixing, fixed parameters, lasting immunity).")
if __name__ == "__main__":
main()All three are numerical evidence (floating-point solutions of model equations). They test our formulas and our numerical methods; whether real populations follow these models is a separate, empirical question.
Exercises
1. Mathematical biology
Warm-upHumanFor the logistic map , what is the non-zero fixed point, in percent of the maximum population?
2. Mathematical biology
CoreHumanFor which growth rates is the non-zero fixed point of the logistic map locally asymptotically stable? (Choose all that apply.)
3. Mathematical biology
Warm-upHumanIn the simple SIR model, an infection has . What percentage of the population must be immune for an introduced infection not to grow (the herd immunity threshold)?
4. Mathematical biology
Warm-upHumanWhich statement about the basic reproduction number is correct?
5. Mathematical biology
CoreHumanAt the coexistence equilibrium of the Lotka–Volterra predator–prey model the Jacobian has eigenvalues . What can we conclude from the linearisation alone?
6. Mathematical biology
CoreHumanIn the competition model , , which parameters give a stable coexistence equilibrium?
7. Mathematical biology
CoreHumanA fish stock grows logistically with per year and carrying capacity tonnes, and is harvested at a constant rate of tonnes per year. What is the largest for which a positive equilibrium exists (the maximum sustainable yield), in tonnes per year?
8. Mathematical biology
StretchHumanIn the SIR model with and almost everyone initially susceptible, when does the number of infected people reach its maximum?
9. Mathematical biology
CoreFormalWhich Lean statement says that Volterra's quantity is conserved along a solution of the predator–prey equations at time t?
10. Mathematical biology
StretchComputationalWrite final_size(R0) returning the fraction of a population eventually infected in the SIR model, where solves (return 0.0 when ). Use bisection on . Since as well, bracket the non-trivial root in , where .
Common misconceptions
Misconception
"A model's prediction is a forecast of what will happen." A model makes conditional statements: if its mechanisms and parameter values are right, then this follows. Its outputs inherit every assumption — homogeneous mixing, constant rates, no behavioural change. Projections under different scenarios are tools for comparing choices; when the scenario does not occur (often because people acted on the projection), the model has not been refuted.
Misconception
" is a fixed biological constant of a disease." depends on contact patterns, population density, environment and behaviour as well as on the pathogen. The same infection can have very different in a crowded city and a scattered rural population, or in different seasons; and what changes during an outbreak is the effective reproduction number .
Misconception
"The predator–prey cycles are explained by the linearisation." At the coexistence equilibrium the linearisation is a centre, which is inconclusive: small nonlinear terms could turn it into a spiral either way. The closed orbits come from the exact conserved quantity — and they disappear when the model is perturbed, so real cycles need a robust mechanism such as a limit cycle.
Misconception
"Complicated behaviour needs a complicated model." The logistic map, with one parameter and one quadratic formula, produces period doubling and chaos. Conversely, irregular data do not prove that the underlying process is random — or that it is chaotic.
Misconception
"Reaching the herd immunity threshold ends an epidemic at that point." Once infections decline, but those already infected keep transmitting, so an uncontrolled epidemic overshoots: with the threshold is , but about are eventually infected.
Many ways of seeing
Topology— What the phase plane allows
Planar dynamics is constrained by topology. Trajectories cannot cross, so by the Poincaré–Bendixson theorem a bounded trajectory of a planar system that stays away from equilibria approaches a periodic orbit: two-species continuous-time models cannot be chaotic, which is why chaos in population biology needs discrete generations (as in the logistic map), time delays, seasonal forcing or at least three species. Index theory adds another constraint: going once round a periodic orbit, the vector field turns exactly once, and the indices of the equilibria inside add up to ; since a saddle has index and a node, spiral or centre , every predator–prey cycle must enclose an equilibrium that is not a saddle — the coexistence point. The VectorField widget's loop mode computes such indices. See dynamical systems.
Geometry— Lotka–Volterra is a Hamiltonian system in disguise
In logarithmic coordinates , , the predator–prey equations become , . With — Volterra's rewritten — these are and : Hamilton's equations, as for a frictionless mechanical system. The flow therefore preserves area in the -plane, which is why there can be no attracting or repelling cycles: an attractor would shrink area. Geometrically, the orbits are the level curves of , a convex function whose sublevel sets are convex discs. Numerical methods that respect this structure (symplectic integrators) keep the orbits closed far better than Euler's method, as the second experiment shows for RK4 versus Euler. See classical mechanics.
Analysis— Lyapunov functions and conservation laws
The two central tools of the lesson are functions that do not increase along solutions. A conserved quantity ( for predator–prey, for SIR) confines orbits to its level sets; a Lyapunov function — positive except at an equilibrium and decreasing along solutions — proves stability without solving anything, as the quadratic form did in the linearisation theorem. For the predator–prey model with self-limiting prey, a function of Volterra's form built from the new equilibrium, , becomes a Lyapunov function: , and LaSalle's invariance principle then gives global convergence to the equilibrium. Finding such functions is an art; for large classes of chemical and ecological models, functions of the form — relative entropies in disguise — do the job.
Probability— Branching processes and extinction
When few individuals are involved, the right model is random. In a Galton–Watson branching process each individual independently produces a random number of offspring with mean ; the extinction probability is the smallest root in of , where is the offspring generating function, and exactly when . For an infection with , a deterministic epidemic is replaced by a gamble: with a geometric number of secondary cases (exponential infectious period), and . Outbreaks that take off then behave like the deterministic SIR model — a law of large numbers for Markov processes (Kurtz, 1970) makes this precise. The same mathematics governs the survival of a new mutation (Haldane, 1927: a mutation with small selective advantage survives with probability about ) and the extinction of family names, Galton and Watson's original question (1874).
Physics— Mass action and chemical kinetics
The interaction terms and come from the law of mass action of chemical kinetics (Guldberg and Waage, 1864): the rate of a reaction between two well-mixed species is proportional to the product of their concentrations, because it is proportional to the rate of encounters. Lotka, a physical chemist, built his ecological models by analogy with autocatalytic reactions, and the predator–prey equations are formally the kinetics of the reactions , , . The analogy is heuristic: animals and people do not mix like molecules in a stirred beaker, and network and spatial structure (who meets whom) can change thresholds and dynamics substantially. Chemical oscillators such as the Belousov–Zhabotinsky reaction, by contrast, really are well-mixed, and show the limit cycles that the robust ecological models predict.
History and global context
- Growth. Leonardo of Pisa's rabbit problem in the Liber Abaci (1202) is an early population model. Thomas Malthus's Essay on the Principle of Population (1798) contrasted geometric population growth with arithmetic growth of food; Pierre-François Verhulst introduced the logistic equation in 1838, and Raymond Pearl and Lowell Reed rediscovered it in 1920. Patrick Leslie's matrix models for age-structured populations followed in 1945.
- Interacting species. Alfred Lotka (Elements of Physical Biology, 1925) and Vito Volterra (1926, prompted by Umberto D'Ancona's Adriatic fishery data) independently proposed the predator–prey equations. Georgii Gause's experiments in Moscow (The Struggle for Existence, 1934) tested competition models. Crawford Holling (1959) described predators' functional responses, and Michael Rosenzweig and Robert MacArthur (1963) analysed predator–prey stability graphically.
- Epidemics. Daniel Bernoulli (1760) used a mathematical model to argue for smallpox inoculation. Ronald Ross, who had shown in India (1897) that mosquitoes transmit malaria, argued with a mathematical model (1911) that malaria could be controlled by reducing mosquito numbers below a threshold, without eliminating them; George Macdonald (1957) developed this into the theory of the reproduction number. William Kermack and Anderson McKendrick's 1927 paper introduced the SIR model and the threshold theorem. Roy Anderson and Robert May's Infectious Diseases of Humans (1991) synthesised the modern theory, used for HIV, measles, SARS (2003), Ebola (2014–16) and COVID-19 (2020 onwards).
- Patterns and chaos. Ronald Fisher (1937) and Andrei Kolmogorov, Ivan Petrovsky and Nikolai Piskunov (1937) studied travelling waves of advantageous genes; Alan Turing's "The chemical basis of morphogenesis" (1952) proposed reaction–diffusion patterns. Robert May's review "Simple mathematical models with very complicated dynamics" (Nature, 1976) brought the logistic map's chaos to biologists; Mitchell Feigenbaum (1978) discovered its universality, and Oleksandr Sharkovsky (1964) and Tien-Yien Li and James Yorke (1975) had found the theorems on periods.
Further reading
- J. D. Murray, Mathematical Biology (1989; 3rd edition in two volumes, 2002–03) — the classic reference, from population dynamics to pattern formation.
- Leah Edelstein-Keshet, Mathematical Models in Biology (1988; reprinted 2005) — discrete and continuous models with a gentle, thorough approach.
- Steven H. Strogatz, Nonlinear Dynamics and Chaos (1994; 2nd edition 2015) — phase planes, bifurcations, the logistic map and chaos, with many biological examples.
- Fred Brauer and Carlos Castillo-Chavez, Mathematical Models in Population Biology and Epidemiology (2001; 2nd edition 2012).
- Roy M. Anderson and Robert M. May, Infectious Diseases of Humans: Dynamics and Control (1991).
- Matt J. Keeling and Pejman Rohani, Modeling Infectious Diseases in Humans and Animals (2008) — with code, stochastic and network models.
- Sarah P. Otto and Troy Day, A Biologist's Guide to Mathematical Modeling in Ecology and Evolution (2007).