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(gy, type = "n", xlab = "x", ylab = "y")
arrows(gy, gy + 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.