Floquet Theory and the Stability of Periodic Systems
Ordinary linear stability analysis assumes the linearized system has constant coefficients, so perturbations grow or decay like and the eigenvalues of a fixed Jacobian settle everything. That assumption breaks the moment the system is driven periodically: a seasonally forced epidemic, a population in a fluctuating environment, or a limit cycle all linearize to equations whose coefficients are themselves functions of time with period . Floquet theory is the extension of eigenvalue stability analysis to exactly this case, and it replaces the eigenvalues of a matrix with the eigenvalues of a single once-per-period map.
The setup: linear systems with periodic coefficients#
Consider a linear system whose coefficient matrix is periodic, where is an matrix and is the period. We cannot simply exponentiate , because and generally do not commute, so there is no constant matrix whose exponential solves the system. What survives is a weaker but powerful structure: the solution over one full period acts as a fixed linear map, and iterating that map controls the long-run behavior.
The fundamental and monodromy matrices#
Let be the principal fundamental matrix: the solution of Its columns are the solutions started from each standard basis vector, so any solution is . The value after one period is the monodromy matrix, the linear map that advances any initial condition by exactly one period. Because is -periodic, advancing by periods is just , so the entire question of stability reduces to the powers of a single constant matrix, whether or not we can write in closed form.
Floquet’s theorem#
Floquet’s theorem states that the fundamental matrix factors as where is -periodic with and is a constant matrix satisfying . The periodic factor carries the within-period wobble, and the constant matrix carries the growth or decay from one period to the next. This is the periodic analogue of : a constant-rate part dressed by a bounded periodic modulation.
Floquet multipliers and exponents#
The eigenvalues of the monodromy matrix are the Floquet multipliers . Each multiplier is the factor by which a solution along its mode is scaled across one period, so after periods that mode is scaled by . The corresponding Floquet exponents are defined through and play the role that eigenvalues play in the constant-coefficient case. The exponents are fixed only up to adding integer multiples of (because of a complex number is multivalued), but the multipliers themselves are unambiguous, which is why stability is cleanest to state in terms of the .
The stability criterion#
The magnitude of the multipliers decides everything, because stays bounded or blows up according to the spectral radius . For the periodic linear system :
- If every (equivalently every ), the zero solution is asymptotically stable: perturbations shrink each period.
- If any (any ), it is unstable: that mode grows geometrically period over period.
- If the largest magnitude equals , the solution is marginally stable (bounded but not decaying), and the nonlinear terms decide the true behavior.
The unit circle plays the role that the imaginary axis plays for ordinary eigenvalues: crossing it is what a bifurcation of a periodic system looks like.
The special multiplier for a limit cycle#
When the periodic solution is the limit cycle of an autonomous nonlinear system (rather than an externally forced one), one Floquet multiplier is always exactly . The reason is that the velocity vector along the orbit is itself a solution of the linearized (variational) equation, corresponding to a perturbation that just slides the state forward along the cycle without leaving it. That neutral direction is the freedom to shift the phase, and it never decays. The remaining multipliers are the ones that matter: the limit cycle is orbitally stable exactly when all of them lie strictly inside the unit circle. A useful check on any numerical computation is that this trivial multiplier comes back as to within integration error.
A worked example: the Mathieu equation#
The canonical test case is the Mathieu equation, a mass on a spring whose stiffness is modulated periodically, Writing puts it in first-order form with and the forcing has period . There is no elementary closed form for , so we integrate the two columns of numerically from to , read off , and take its eigenvalues.
Because the equation has no damping, the system is Hamiltonian and ; the two multipliers are therefore reciprocals, . That forces a clean dichotomy in terms of the trace: when the multipliers are a complex-conjugate pair on the unit circle (bounded, marginally stable), and when they are real reciprocals with one outside the circle (unstable). Taking lands in a stable band, while sits inside the first parametric-resonance tongue and is unstable. Sweeping the plane and shading where reproduces the Ince–Strutt chart in the figure, with instability tongues opening from .
In code#
R#
# Mathieu equation: monodromy matrix and Floquet multipliers.
library(deSolve)
# Variational system for the two columns of the fundamental matrix Phi.
# State is c(Phi[1,1], Phi[2,1], Phi[1,2], Phi[2,2]).
mathieu <- function(t, y, p) with(p, {
q <- delta + 2 * eps * cos(2 * t)
list(c(y[2], -q * y[1], # d/dt of column 1
y[4], -q * y[3])) # d/dt of column 2
})
monodromy <- function(delta, eps, Tper = pi) {
y0 <- c(1, 0, 0, 1) # Phi(0) = I
out <- ode(y0, c(0, Tper), mathieu, list(delta = delta, eps = eps),
rtol = 1e-11, atol = 1e-13)
matrix(out[2, -1], 2, 2) # M = Phi(T), filled column-wise
}
for (case in list(c(2, 0.2), c(1, 0.4))) {
M <- monodromy(case[1], case[2])
mu <- eigen(M)$values
cat(sprintf("delta=%.1f eps=%.1f | tr=%.3f det=%.3f | rho=%.3f (%s)\n",
case[1], case[2], sum(diag(M)), det(M), max(Mod(mu)),
ifelse(max(Mod(mu)) > 1 + 1e-6, "unstable", "stable")))
}
Python#
import numpy as np
from scipy.integrate import solve_ivp
T = np.pi # period of cos(2t) in the Mathieu equation
def monodromy(delta, eps):
"""Integrate the fundamental matrix Phi over one period; return M = Phi(T)."""
def rhs(t, Y):
q = delta + 2.0 * eps * np.cos(2.0 * t)
A = np.array([[0.0, 1.0], [-q, 0.0]])
return (A @ Y.reshape(2, 2)).ravel()
sol = solve_ivp(rhs, (0.0, T), np.eye(2).ravel(),
rtol=1e-11, atol=1e-13)
return sol.y[:, -1].reshape(2, 2)
for delta, eps, tag in [(2.0, 0.2, "stable band"),
(1.0, 0.4, "first tongue")]:
M = monodromy(delta, eps)
mu = np.linalg.eigvals(M) # Floquet multipliers
rho = np.max(np.abs(mu)) # spectral radius
verdict = "unstable" if rho > 1 + 1e-6 else "stable (bounded)"
print(f"delta={delta}, eps={eps} ({tag}):")
print(f" trace(M)={np.trace(M):+.3f}, det(M)={np.linalg.det(M):.3f}")
print(f" |multipliers| = {np.abs(mu)[0]:.3f}, {np.abs(mu)[1]:.3f}"
f" -> spectral radius {rho:.3f} ({verdict})")
delta=2.0, eps=0.2 (stable band):
trace(M)=-0.575, det(M)=1.000
|multipliers| = 1.000, 1.000 -> spectral radius 1.000 (stable (bounded))
delta=1.0, eps=0.4 (first tongue):
trace(M)=-2.393, det(M)=1.000
|multipliers| = 0.540, 1.853 -> spectral radius 1.853 (unstable)
Julia#
# Mathieu equation: monodromy matrix and Floquet multipliers.
using OrdinaryDiffEq, LinearAlgebra
function fundamental!(dΦ, Φ, p, t)
q = p.delta + 2 * p.eps * cos(2t)
A = [0.0 1.0; -q 0.0]
dΦ .= A * Φ
end
function monodromy(delta, eps; Tper = π)
prob = ODEProblem(fundamental!, Matrix{Float64}(I, 2, 2),
(0.0, Tper), (delta = delta, eps = eps))
sol = solve(prob, Vern9(), reltol = 1e-11, abstol = 1e-13)
sol.u[end] # M = Φ(T)
end
for (delta, eps) in ((2.0, 0.2), (1.0, 0.4))
M = monodromy(delta, eps)
μ = eigvals(M)
ρ = maximum(abs.(μ))
verdict = ρ > 1 + 1e-6 ? "unstable" : "stable"
println("delta=$delta eps=$eps | tr=$(round(tr(M), digits=3)) ",
"det=$(round(det(M), digits=3)) | ρ=$(round(ρ, digits=3)) ($verdict)")
end
The two runs bracket the stability boundary: the stable-band case keeps both multipliers on the unit circle (spectral radius , so perturbations neither grow nor decay), while the tongue case pushes one multiplier out to about , so a disturbance grows by that factor every half-period of the drive. The determinant returning in both cases is the Hamiltonian check that the integration conserved phase-space area.
A simpler example: a seasonally forced contact rate#
The Mathieu equation needed numerical integration because its two modes mix; the scalar case that most epidemiologists meet first has a closed form. Take a directly transmitted infection whose transmission rate is seasonally forced, and ask whether it can invade a fully susceptible population. Near the disease-free state , the infected class obeys a single linear equation with a periodic coefficient, This is Floquet theory in one dimension, and a scalar linear ODE integrates directly: the fundamental solution is so the monodromy “matrix” is the number because the seasonal term integrates to zero over a full period, . The single Floquet multiplier is and the Floquet exponent is , the mean growth rate. Invasion () therefore happens exactly when , that is when the time-averaged exceeds one.
The instructive part is what drops out: the forcing amplitude reshapes the periodic factor — the growth rate genuinely waxes and wanes within the year, and for large it goes negative for part of it — yet cancels from the multiplier entirely. For a scalar periodic rate, only the mean of the coefficient matters. This clean averaging is special to one dimension: once the susceptible pool itself cycles at an endemic state, the linearization becomes genuinely multidimensional, its coefficient matrices no longer commute across the period, and the seasonal amplitude does move the dominant multiplier — which is why the naive time-averaged can mislead for a fully forced endemic model.
R#
beta0 <- 0.25; gamma <- 0.20; Tper <- 365 # mean R0 = beta0/gamma = 1.25
grid <- seq(0, Tper, length.out = 2e5 + 1)
floquet <- function(eps) {
rate <- beta0 * (1 + eps * cos(2 * pi * grid / Tper)) - gamma
integral <- sum((rate[-1] + rate[-length(rate)]) / 2 * diff(grid)) # trapezoid
c(exponent = integral / Tper, multiplier = exp(integral))
}
sapply(c(0, 0.3, 0.6), floquet) # exponent is 0.05/day whatever the amplitude
Python#
import numpy as np
beta0 = 0.25 # mean transmission rate (per day)
gamma = 0.20 # recovery rate (per day); mean R0 = beta0/gamma = 1.25
T = 365.0 # forcing period (days)
t = np.linspace(0.0, T, 200_001) # one period on a fine grid
def growth_rate(t, eps): # linearized rate near the disease-free state
beta = beta0 * (1.0 + eps * np.cos(2 * np.pi * t / T))
return beta - gamma
for eps in (0.0, 0.3, 0.6):
integral = np.trapezoid(growth_rate(t, eps), t) # int (beta(t)-gamma) dt
rho = integral / T # Floquet exponent (per day)
mult = np.exp(integral) # multiplier over one year
print(f"eps={eps}: Floquet exponent {rho:+.5f}/day, "
f"annual multiplier {mult:.3e}")
print(f"closed form: exponent {beta0 - gamma:+.5f}/day = mean(beta) - gamma")
eps=0.0: Floquet exponent +0.05000/day, annual multiplier 8.431e+07
eps=0.3: Floquet exponent +0.05000/day, annual multiplier 8.431e+07
eps=0.6: Floquet exponent +0.05000/day, annual multiplier 8.431e+07
closed form: exponent +0.05000/day = mean(beta) - gamma
Julia#
beta0, gamma, Tper = 0.25, 0.20, 365.0 # mean R0 = beta0/gamma = 1.25
grid = range(0, Tper; length = 200_001)
function floquet(eps)
rate = @. beta0 * (1 + eps * cos(2π * grid / Tper)) - gamma
integral = sum((rate[2:end] .+ rate[1:end-1]) ./ 2 .* diff(grid)) # trapezoid
(exponent = integral / Tper, multiplier = exp(integral))
end
floquet.((0.0, 0.3, 0.6)) # exponent 0.05/day for every forcing amplitude
All three amplitudes return the same Floquet exponent, , and the same annual multiplier: the seasonal wiggle averages out of the scalar invasion criterion even though it dominates the within-year timing of cases.
Why it matters#
Floquet theory is the tool that lets stability analysis follow a system into a periodic world. The stability of a seasonally forced disease-free state, and thus a proper time-varying reproduction number, is a Floquet-multiplier calculation on the linearization about the periodic solution rather than an eigenvalue calculation on a frozen Jacobian: the pathogen invades when the dominant multiplier of the seasonal system exceeds one, which is not the same as the time-averaged exceeding one. The same machinery decides whether the limit cycles of predator–prey and other oscillating models are stable, underlies the parametric-resonance instabilities that make a periodically driven system blow up at drive frequencies where the static system is perfectly stable, and connects to the period-doubling route to chaos, where a multiplier leaving the unit circle through marks each successive bifurcation. Whenever the coefficients breathe with a period, the monodromy matrix is where stability lives.