Equilibria and Linear Stability
Whether a population settles at a carrying capacity, an epidemic burns out, or a predator and its prey cycle forever is decided by a handful of special states and their stability. Equilibria are the resting points of a dynamical system, and linear stability analysis tells us which ones the system will actually be found near.
Equilibria#
An equilibrium (or fixed point, or steady state) of an ordinary differential equation system is a state where every rate of change vanishes, Once the system reaches such a point it stays there forever, because nothing is changing. Finding equilibria means solving the algebraic equations for the state .
Nullclines#
In the plane, with variables and , it helps to draw the nullclines: the curves where each derivative is individually zero. The -nullcline is the set where and the -nullcline is the set where . Equilibria sit exactly where an -nullcline crosses a -nullcline, since only there are both derivatives zero at once. Nullclines also carve the phase plane into regions where the flow moves up-or-down and left-or-right, so they organize the whole phase portrait.
Linear stability#
An equilibrium is stable if small perturbations decay and the system returns to it, and unstable if perturbations grow. To decide, we linearize: near a small displacement obeys, to first order, where is the Jacobian matrix of first-order partial derivatives evaluated at the equilibrium.
The linear system grows or decays like along its eigenvectors, so stability is governed entirely by the eigenvalues of . An equilibrium is locally asymptotically stable if and only if every eigenvalue of the Jacobian has negative real part, . If any eigenvalue has , the equilibrium is unstable.
One dimension#
For a single equation , the Jacobian is just the scalar . The equilibrium is stable when and unstable when . Geometrically, if the graph of crosses zero with a downward slope, the flow pushes back toward from both sides.
Two dimensions: trace and determinant#
For a Jacobian we rarely need the eigenvalues explicitly. Write the trace and the determinant ; the eigenvalues solve The sign pattern of classifies the equilibrium:
- — saddle (one positive, one negative eigenvalue), always unstable.
- and — stable node (if ) or stable spiral (if ).
- and — unstable node or spiral.
- , — center: purely imaginary eigenvalues, neutral cycles (borderline; nonlinear terms decide the true behavior).
A stable node or spiral requires both and — the standard two-dimensional stability test.
A worked example#
Consider the system This is a competition-style model.
Equilibria#
Setting requires or ; setting requires or . Combining the branches gives four equilibria: , , , and the interior point where and , which solves to .
Jacobian#
Differentiating,
Classify each#
At : , eigenvalues — unstable node.
At : , eigenvalues — , : stable node.
At : , eigenvalues — stable node.
At : , , : saddle (unstable).
So the interior coexistence point is a saddle, and the two single-species states are both stable — the classic bistable outcome of strong competition, where the winner depends on initial conditions.
Simulation#
We draw the nullclines, mark the equilibria, and overlay a phase portrait (a direction field with a few trajectories).
R#
library(deSolve)
f <- function(t, s, p) {
x <- s[1]; y <- s[2]
list(c(x * (3 - x - 2*y), y * (2 - x - y)))
}
# vector field on a grid
g <- expand.grid(x = seq(0, 3.2, 0.3), y = seq(0, 2.4, 0.3))
d <- with(g, cbind(x*(3 - x - 2*y), y*(2 - x - y)))
plot(g$x, g$y, type = "n", xlab = "x", ylab = "y")
arrows(g$x, g$y, g$x + 0.08*d[,1], g$y + 0.08*d[,2], length = 0.03)
abline(a = 1.5, b = -0.5, col = "blue") # x-nullcline x + 2y = 3
abline(a = 2, b = -1, col = "red") # y-nullcline x + y = 2
points(rbind(c(0,0), c(3,0), c(0,2), c(1,1)), pch = 19)
# trajectory near the saddle drifts to a stable node
ode(c(1.1, 0.9), seq(0, 20, 0.1), f, NULL)[201, ] # -> near (3, 0)
Python#
import numpy as np
from scipy.integrate import solve_ivp
def f(t, s):
x, y = s
return [x*(3 - x - 2*y), y*(2 - x - y)]
# Jacobian eigenvalues at the interior equilibrium (1, 1)
J = np.array([[-1., -2.], [-1., -1.]])
print(np.linalg.eigvals(J)) # ~[ 0.414, -2.414] -> saddle
sol = solve_ivp(f, (0, 20), [1.1, 0.9], rtol=1e-8, dense_output=True)
print(sol.y[:, -1]) # ~[3, 0]: settles at the stable node
[ 0.41421356 -2.41421356]
[3.00000032e+00 3.98062839e-07]
Julia#
using LinearAlgebra
J(x, y) = [3 - 2x - 2y -2x;
-y 2 - x - 2y]
for (x, y) in ((0,0), (3,0), (0,2), (1,1))
println((x, y), " -> ", eigvals(J(x, y)))
end
# (1, 1) gives one positive, one negative eigenvalue: a saddle
Why it matters#
Nearly every question about long-run ecological and epidemic dynamics reduces to “which equilibria exist, and which are stable.” The disease-free equilibrium of an epidemic model is stable exactly when , and it loses stability as crosses ; a predator-prey coexistence point can be a stable spiral (damped oscillations) or a neutral center (persistent cycles). Because stability can flip as a parameter moves, this analysis is also the entry point to bifurcations, where equilibria are created, destroyed, or exchange stability.