Laws of change
Most laws of nature do not tell you what a quantity is; they tell you how fast it changes. Radioactive carbon decays at a rate proportional to the amount present — which is how archaeologists date charcoal from Mungo in Australia or bone from Olduvai Gorge. A cup of chai cools at a rate proportional to its excess temperature over the room. An epidemic spreads at a rate proportional to the number of contacts between infected and susceptible people. A pendulum's acceleration is proportional to the sine of its angle.
Each law is an ordinary differential equation (ODE): an equation relating an unknown function of one variable (usually time) to its derivatives. Solving it means finding the whole function. Three kinds of question arise:
- Explicit solution. Some equations can be solved by formulas; this lesson covers the main classes.
- Existence and uniqueness. Most equations cannot be solved by formulas. Is there a solution anyway? Only one? For how long?
- Qualitative behaviour. Without a formula, what can we say — does the solution settle down, oscillate, blow up?
This lesson assumes calculus of one variable and linear algebra (eigenvalues and eigenvectors).
Following the arrows
An equation assigns a slope to every point of the -plane. Draw a short segment with that slope at many points: this is a slope field. A solution is a curve that is tangent to the segments everywhere — you "follow the arrows". Euler's method makes this literal: take a small step in the direction of the arrow, look again, repeat.
The logistic equation x′ = x(1 − x)
Click to draw solutions. Every solution starting with 0 < x < 1 rises towards 1; none ever crosses the lines x = 0 or x = 1.
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).
Notice two things. Solutions never cross: if two solutions met at a point, they would be two solutions of the same initial value problem, and the uniqueness theorem below forbids that (for well-behaved ). And the horizontal lines and are solutions themselves — equilibria — which the other solutions approach or flee. Much of the theory is visible in the picture before any formula is written.
Definitions
Definition— ordinary differential equation
An ordinary differential equation of order is an equation relating an unknown function of one variable to its first derivatives. A solution on an interval is an times differentiable function (or ) satisfying the equation for every . An initial value problem (IVP) adds conditions .
The equation is linear if it has the form , and homogeneous if moreover . A first-order system is with ; it is autonomous if does not depend on . Every -th order equation becomes a first-order system for the vector — for example, becomes — so first-order systems are the general case.
Definition— equilibrium and stability
An equilibrium of the autonomous system is a point with ; the constant function is then a solution. It is stable if solutions starting close to stay close for all (for every there is such that implies for all ), and asymptotically stable if moreover such solutions tend to .
Definition— Lipschitz condition
is Lipschitz in on a set , with constant , if for all . By the mean value theorem, this holds on any closed box where is continuous (take = the maximum of its absolute value, or of the norm of the Jacobian matrix in several variables).
Equations we can solve
Separable equations . Wherever we may write . If and , the chain rule shows that , so ; solve for . In addition every zero of gives a constant solution . (The familiar shorthand "" is exactly this argument.)
Example— Three separable equations, three behaviours
- Logistic growth . Partial fractions give , so . For these rise from to ; together with and they are all the solutions in .
- Blow-up , . Here , so , which tends to as . The right-hand side is perfectly smooth, yet the solution exists only on .
- Non-uniqueness , . Besides (from the zero of ), separation on gives , and gluing gives the solution for , for . Indeed for every , the function equal to for and for is a solution: infinitely many solutions share the same initial value.
x′ = √|x|: many solutions through one point
The parabola through (2, 1) reaches the axis at t = 0 — exactly where the zero solution also passes. Which way should a solution starting at (0, 0) go?
dy/dx = sqrt(abs(y))
Click the field to draw a solution, or type a start point below.
Solution curves: 3. They are numerical approximations (fourth-order Runge–Kutta), not exact formulas.
Latest curve through (2, 1).
Theorem— Theorem (first-order linear equations)
Let be continuous on an interval , , and . The initial value problem , has exactly one solution on , namely
Proof
The key identity, from the product and chain rules and , is
So solves the equation if and only if . Existence: the displayed has , whose derivative is by the fundamental theorem of calculus, and . Uniqueness: if are solutions, satisfies on the interval , so is constant, equal to its value at .
∎
The factor is the integrating factor. With and the theorem says that the only solutions of are — the law of exponential growth and decay.
Linear equations with constant coefficients. For , try : it is a solution exactly when , the characteristic equation.
Theorem— Theorem (second-order constant-coefficient equations)
The real solutions of form a two-dimensional vector space, with basis
- if the characteristic roots are real;
- if there is a double root ;
- if the roots are with .
Proof
Write ; the equation is , where are the roots (the factors commute because the coefficients are constant). Each listed function is a solution: ; for a double root, and then ; for complex roots, is a complex solution, and since the equation has real coefficients its real and imaginary parts are real solutions. They are independent: their Wronskians at are , and , all non-zero. The space is two-dimensional: by the existence and uniqueness theorem below, the linear map from solutions to is a bijection.
∎
The damped oscillator (, ) has roots : overdamped if (two decaying exponentials), critically damped if , and underdamped if (decaying oscillations ). For an inhomogeneous equation , the general solution is one particular solution plus the general homogeneous solution, because the difference of two solutions solves the homogeneous equation.
Linear systems and phase portraits
For a constant matrix , define the matrix exponential
The series converges absolutely for every in any operator norm, since , and it can be differentiated term by term (it is a power series in with infinite radius of convergence): . If then (multiply the series, exactly as for numbers); in particular .
Theorem— Theorem (linear systems)
For every , the unique solution of with is , defined for all . If has a basis of eigenvectors with eigenvalues and , then .
Proof
satisfies and . If is any solution, then (as commutes with ), so and . Finally, gives .
∎
Complex eigenvalues produce rotation combined with growth or decay at rate : for (that is, ), , a rotation. Non-diagonalisable matrices, via the Jordan form, add polynomial factors .
Theorem— Theorem (classification of planar linear systems)
Let be a real matrix with trace and determinant , and consider , whose only equilibrium is . Then:
- : a saddle (unstable);
- and : a node, asymptotically stable if , unstable if ;
- and : a spiral (focus), asymptotically stable if , unstable if , and a centre (stable, not asymptotically) if .
Proof
The eigenvalues are the roots of , so and . If they are real with opposite signs: along one eigenvector solutions decay, along the other they grow. If and they are real, distinct, and of the same sign as : all solutions decay (or all grow). If they are , and solutions are rotations multiplied by . (The boundary case gives degenerate or star nodes, with the stability of the sign of .)
∎
A damped oscillator: x″ + 0.6x′ + 2x = 0
Trace −0.6, determinant 2: a stable spiral. Edit the equations to make a saddle, a centre or a node.
x′ = y, y′ = −2·x − 0.6·y
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.
Linear systemA = [01−2−0.6]
T = −0.6, D = 2, T² − 4D = −7.64; λ = −0.3 ± 1.382i
Equilibrium at (0, 0): spiral sink (stable)
Existence and uniqueness: the Picard–Lindelöf theorem
We now prove that well-behaved initial value problems have exactly one solution, at least for a short time. We write for a norm on .
Theorem— Theorem (Picard–Lindelöf)
Let be continuous on the box , with on , and Lipschitz in on with constant . Let . Then the initial value problem , has exactly one solution on .
Proof
Write .
Step 1: an integral equation. By the fundamental theorem of calculus, a continuous with solves the IVP if and only if
Step 2: Picard iterates. Put and . By induction each is continuous on with , so stays in and the next iterate is defined.
Step 3: an estimate. We claim . For this is . If it holds for , then by the Lipschitz condition (for ; is symmetric)
Step 4: convergence. Since converges (to , or if ), the Weierstrass M-test shows that converges uniformly on to a continuous function with . As uniformly, we may pass to the limit under the integral sign in the definition of : satisfies the integral equation, hence solves the IVP.
Step 5: uniqueness (Grönwall's argument). A solution on stays in the box: up to the first time it could leave, , so it never does. So let be two solutions with values in the box, and for put . Subtracting the integral equations, . Let ; then , so
Since , we get ; but , so and then . The same argument works for .
∎
The same Grönwall argument gives continuous dependence on initial data: two solutions with and satisfy . The factor can be enormous, which is the seed of chaos in nonlinear systems.
What the theorem does not say.
- It is local. The solution of , exists only for , although is smooth everywhere. In general, if is locally Lipschitz on , every solution extends to a maximal open interval , and if then as : solutions die only by escaping to infinity. Linear systems never blow up.
- Continuity alone gives existence but not uniqueness. Peano's theorem (1890) says that if is merely continuous, a solution exists for a short time; the proof uses compactness (Arzelà–Ascoli) instead of a contraction. For the right-hand side is continuous but not Lipschitz near , and uniqueness fails, as we saw.
- It gives no formula. Liouville showed in 1841 that the Riccati equation cannot be solved by quadratures (elementary functions and their integrals); its solutions need Bessel functions. Most ODEs, including the pendulum and the three-body problem, have no elementary closed-form solution — hence the importance of qualitative and numerical methods.
Qualitative behaviour
For an autonomous scalar equation with locally Lipschitz, uniqueness alone gives a complete picture. Solutions cannot cross the equilibria (zeros of ), so a solution starting between two consecutive equilibria stays between them, and since has a fixed sign there, it is monotone and tends to one of them (or to ). Hence an equilibrium with is asymptotically stable (arrows point towards it on both sides) and one with is unstable. For the logistic equation , and : the population moves away from extinction and settles at the carrying capacity.
In the plane, an equilibrium of is studied through the linearisation . The Hartman–Grobman theorem (Grobman 1959, Hartman 1960) says that if has no eigenvalue with zero real part (a hyperbolic equilibrium), then near the phase portrait is a continuous deformation of the linear one: saddles stay saddles and sinks stay sinks. A linear centre is not hyperbolic, and the nonlinear terms decide.
Another tool is a Lyapunov function: a function with a strict minimum at that does not increase along solutions. For the damped pendulum , the energy satisfies , so the bottom equilibrium is stable — without solving the equation. Energy is also a powerful uniqueness tool: for , is constant, so a solution with stays at (this is formalised below).
Differential equations in Lean and Mathlib
In Mathlib, " solves " is written ∀ t, HasDerivAt x (f t (x t)) t. The lesson's module verifies explicit solutions, proves the integrating-factor uniqueness argument and the energy argument, and makes the failure modes precise.
Exponential growth: the solution, and its uniqueness by the integrating factor — a quantity with zero derivative is constant:
/-- `x(t) = x₀ e^{a t}` solves `x' = a x` with `x(0) = x₀`. -/
theorem exp_solves (a x₀ t : ℝ) :
HasDerivAt (fun s => x₀ * exp (a * s)) (a * (x₀ * exp (a * t))) t := by
have h := ((hasDerivAt_id' t).const_mul a).exp.const_mul x₀
convert h using 1
ring/-- **Uniqueness for `x' = a x`.** If `y' = a y` everywhere, then `y(t) = y(0) e^{a t}`.
Proof (the integrating factor): `g(t) = y(t) e^{-a t}` has `g' = y' e^{-at} - a y e^{-at} = 0`,
so `g` is constant (Mathlib's `is_const_of_deriv_eq_zero`), and `g(0) = y(0)`. -/
theorem exp_unique {a : ℝ} {y : ℝ → ℝ} (hy : ∀ t, HasDerivAt y (a * y t) t) (t : ℝ) :
y t = y 0 * exp (a * t) := by
let g : ℝ → ℝ := fun s => y s * exp (-a * s)
have hg : ∀ s, HasDerivAt g 0 s := by
intro s
have h := (hy s).mul (((hasDerivAt_id' s).const_mul (-a)).exp)
convert h using 1
ring
have hconst : g t = g 0 :=
is_const_of_deriv_eq_zero (fun s => (hg s).differentiableAt) (fun s => (hg s).deriv) t 0
simp only [g, mul_zero, exp_zero, mul_one] at hconst
calc y t = y t * exp (-a * t) * exp (a * t) := by
rw [mul_assoc, ← exp_add]; ring_nf; simp
_ = y 0 * exp (a * t) := by rw [hconst]/-- **The integrating factor.** If `P' = p`, then `(y e^{P})' = (y' + p y) e^{P}`. So the linear
equation `y' + p(t) y = q(t)` becomes `(y e^{P})' = q e^{P}`, which is solved by integrating. -/
theorem integrating_factor {y P : ℝ → ℝ} {y' p t : ℝ} (hy : HasDerivAt y y' t)
(hP : HasDerivAt P p t) :
HasDerivAt (fun s => y s * exp (P s)) ((y' + p * y t) * exp (P t)) t := by
have h := hy.mul hP.exp
convert h using 1
ringThe logistic solution found by separation of variables, verified by differentiation:
/-- The logistic equation `x' = x (1 - x)` is solved by `x(t) = 1 / (1 + C e^{-t})` for every
`C ≥ 0` (found by separating variables and partial fractions; here we verify it). -/
theorem logistic_solves {C : ℝ} (hC : 0 ≤ C) (t : ℝ) :
HasDerivAt (fun s => 1 / (1 + C * exp (-s)))
((1 / (1 + C * exp (-t))) * (1 - 1 / (1 + C * exp (-t)))) t := by
have hpos : 0 < 1 + C * exp (-t) := by positivity
have hd : HasDerivAt (fun s => 1 + C * exp (-s)) (C * (exp (-t) * (-1))) t :=
(((hasDerivAt_neg' t).exp).const_mul C).const_add 1
have h := (hasDerivAt_const t (1 : ℝ)).div hd hpos.ne'
convert h using 1
field_simp
ringBlow-up: solves and escapes to infinity as :
/-- `x(t) = 1/(1 - t)` solves `x' = x²` with `x(0) = 1` for `t ≠ 1`… -/
theorem blowup_solves {t : ℝ} (ht : t ≠ 1) :
HasDerivAt (fun s => 1 / (1 - s)) ((1 / (1 - t)) ^ 2) t := by
have h1 : 1 - t ≠ 0 := sub_ne_zero.mpr (Ne.symm ht)
have hd : HasDerivAt (fun s => 1 - s) (-1) t := by
simpa using (hasDerivAt_id' t).const_sub 1
have h := (hasDerivAt_const t (1 : ℝ)).div hd h1
convert h using 1
field_simp
ring/-- …and it escapes to `+∞` as `t → 1⁻`: the solution exists only on `(-∞, 1)`, although the
right-hand side `x²` is smooth on all of `ℝ`. -/
theorem blowup_tendsto : Tendsto (fun s : ℝ => 1 / (1 - s)) (𝓝[<] 1) atTop := by
have hc : Tendsto (fun s : ℝ => 1 - s) (𝓝 1) (𝓝 0) := by
have := (tendsto_const_nhds (x := (1 : ℝ))).sub (tendsto_id (x := 𝓝 (1 : ℝ)))
simpa using this
have h : Tendsto (fun s : ℝ => 1 - s) (𝓝[<] 1) (𝓝[>] 0) := by
refine tendsto_nhdsWithin_iff.mpr ⟨hc.mono_left nhdsWithin_le_nhds, ?_⟩
filter_upwards [self_mem_nhdsWithin] with s hs
exact mem_Ioi.mpr (sub_pos.mpr (show s < 1 from hs))
refine h.inv_tendsto_nhdsGT_zero.congr ?_
intro s
simp [one_div]Non-uniqueness for . The glued solution is written ; at its derivative is found by squeezing, . Then the reason Picard–Lindelöf does not apply — is not Lipschitz:
/-- The second solution of `x' = √|x|`, `x(0) = 0`: it stays at `0` until time `0`, then rises
as `t²/4`. Written as `(max t 0)² / 4`. -/
noncomputable def halfSq (t : ℝ) : ℝ := (max t 0) ^ 2 / 4/-- `halfSq` solves `x' = √|x|` at every time. For `t > 0` it is `t²/4` near `t`, with derivative
`t/2 = √(t²/4)`; for `t < 0` it is `0` near `t`; at `t = 0` it is squeezed: `|x(h)| ≤ h²/4`,
which is `o(h)`, so the derivative is `0 = √0`. -/
theorem halfSq_solves (t : ℝ) : HasDerivAt halfSq (√|halfSq t|) t := by
rcases lt_trichotomy t 0 with ht | rfl | ht
· -- Left of zero: locally the zero function.
have hval : halfSq t = 0 := by simp [halfSq, max_eq_right ht.le]
rw [hval, abs_zero, sqrt_zero]
refine (hasDerivAt_const t (0 : ℝ)).congr_of_eventuallyEq ?_
filter_upwards [gt_mem_nhds ht] with s hs
simp [halfSq, max_eq_right hs.le]
· -- At zero: the bound `|x(h)| ≤ h²/4`.
have hval : halfSq 0 = 0 := by simp [halfSq]
rw [hval, abs_zero, sqrt_zero, hasDerivAt_iff_isLittleO_nhds_zero]
have hbig : (fun h : ℝ => halfSq (0 + h) - halfSq 0 - h • (0 : ℝ)) =O[𝓝 0]
fun h : ℝ => h ^ 2 := by
refine Asymptotics.IsBigO.of_bound 1 (Eventually.of_forall fun h => ?_)
simp only [zero_add, hval, sub_zero, smul_zero, norm_pow, Real.norm_eq_abs, one_mul]
rw [abs_of_nonneg (show (0 : ℝ) ≤ halfSq h by unfold halfSq; positivity)]
unfold halfSq
rcases le_total h 0 with hh | hh
· rw [max_eq_right hh]; nlinarith [sq_nonneg |h|]
· rw [max_eq_left hh, sq_abs]; linarith [sq_nonneg h]
exact hbig.trans_isLittleO (Asymptotics.isLittleO_pow_id one_lt_two)
· -- Right of zero: locally `s²/4`.
have hval : halfSq t = t ^ 2 / 4 := by simp [halfSq, max_eq_left ht.le]
have hsq : √|halfSq t| = t / 2 := by
rw [hval, abs_of_nonneg (by positivity)]
rw [show t ^ 2 / 4 = (t / 2) ^ 2 by ring]
exact sqrt_sq (by linarith)
rw [hsq]
have h := (hasDerivAt_pow 2 t).div_const 4
refine (h.congr_of_eventuallyEq ?_).congr_deriv ?_
· filter_upwards [lt_mem_nhds ht] with s hs
simp [halfSq, max_eq_left hs.le]
· push_cast; ring/-- **Non-uniqueness.** Both `x ≡ 0` and `halfSq` solve `x' = √|x|` with `x(0) = 0`, and they
are different functions (at `t = 2`, `halfSq 2 = 1`). Picard–Lindelöf does not apply. -/
theorem nonunique :
(∀ t, HasDerivAt (fun _ : ℝ => (0 : ℝ)) (√|(0 : ℝ)|) t) ∧
(∀ t, HasDerivAt halfSq (√|halfSq t|) t) ∧ halfSq 0 = 0 ∧ halfSq 2 ≠ 0 := by
refine ⟨fun t => by simpa using hasDerivAt_const t (0 : ℝ), halfSq_solves, ?_, ?_⟩
· simp [halfSq]
· norm_num [halfSq]/-- The reason: `x ↦ √|x|` is not Lipschitz on `[-1, 1]` (Mathlib's `LipschitzOnWith`). A
Lipschitz constant `K` would give `√x ≤ K x` for `0 < x ≤ 1` (compare with the value `0` at `0`),
but `x = 1/(K+1)²` has `√x = 1/(K+1) > K/(K+1)² = K x`. -/
theorem sqrt_abs_not_lipschitz :
¬ ∃ K : NNReal, LipschitzOnWith K (fun x : ℝ => √|x|) (Icc (-1) 1) := by
rintro ⟨K, hK⟩
set k : ℝ := (K : ℝ) with hk
have hk0 : 0 ≤ k := K.2
set x : ℝ := 1 / (k + 1) ^ 2 with hx
have hxpos : 0 < x := by positivity
have hxle : x ≤ 1 := by
rw [hx, div_le_one (by positivity)]
nlinarith
have hsqrt : √x = 1 / (k + 1) := by
rw [hx, one_div, one_div, sqrt_inv, sqrt_sq (by linarith)]
-- The Lipschitz bound between `x` and `0`.
have hb := hK.dist_le_mul x ⟨by linarith, hxle⟩ 0 ⟨by norm_num, by norm_num⟩
simp only [Real.dist_eq, abs_zero, sqrt_zero, sub_zero, abs_of_pos hxpos,
abs_of_nonneg (sqrt_nonneg _)] at hb
rw [hsqrt] at hb
-- But `k x = k/(k+1)² < 1/(k+1)`.
have hlt : k * x < 1 / (k + 1) := by
have hk1 : 0 < k + 1 := by linarith
rw [hx, mul_one_div, div_lt_div_iff₀ (by positivity) hk1]
nlinarith
linarithMathlib's Picard–Lindelöf theorem. Its hypotheses IsPicardLindelof f t₀ x₀ a r L K are those of the lesson (continuity in , Lipschitz constant on a ball of radius , bound — our — and a time interval short enough that length ); its proof shows that an iterate of the Picard map is a contraction, a variant of our Steps 2–4. Uniqueness is proved with Grönwall's inequality, as in Step 5:
/-- **Picard–Lindelöf, existence.** If on the time interval `[tmin, tmax] ∋ t₀` the vector field
`f t x` is continuous in `t`, `K`-Lipschitz in `x` on the ball of radius `a` about `x₀`, bounded
by `L` there, and `L · (length of time on either side of t₀) ≤ a`, then there is a solution with
`α(t₀) = x₀` on the whole interval. This applies Mathlib's
`IsPicardLindelof.exists_eq_forall_mem_Icc_hasDerivWithinAt₀`, proved by showing that an iterate
of the Picard map `α ↦ x₀ + ∫_{t₀}^t f(τ, α τ) dτ` is a contraction. -/
theorem picard_lindelof {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E]
{f : ℝ → E → E} {tmin tmax : ℝ} {t₀ : Icc tmin tmax} {x₀ : E} {a L K : NNReal}
(hf : IsPicardLindelof f t₀ x₀ a 0 L K) :
∃ α : ℝ → E, α t₀ = x₀ ∧
∀ t ∈ Icc tmin tmax, HasDerivWithinAt α (f t (α t)) (Icc tmin tmax) t :=
hf.exists_eq_forall_mem_Icc_hasDerivWithinAt₀/-- **Uniqueness.** If `v t` is `K`-Lipschitz for every `t`, two solutions of `x' = v(t, x)`
defined for all time that agree at one time `t₀` agree everywhere. This applies Mathlib's
`ODE_solution_unique_univ`, whose proof uses Grönwall's inequality, as in the lesson. -/
theorem unique_of_lipschitz {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E]
{v : ℝ → E → E} {K : NNReal} {f g : ℝ → E} {t₀ : ℝ}
(hv : ∀ t, LipschitzWith K (v t))
(hf : ∀ t, HasDerivAt f (v t (f t)) t) (hg : ∀ t, HasDerivAt g (v t (g t)) t)
(heq : f t₀ = g t₀) : f = g :=
ODE_solution_unique_univ (s := fun _ => univ) (fun t => (hv t).lipschitzOnWith)
(fun t => ⟨hf t, mem_univ _⟩) (fun t => ⟨hg t, mem_univ _⟩) heqSecond-order equations: the harmonic oscillator's solutions, and uniqueness by conservation of energy — a proof that does not use Picard–Lindelöf at all:
/-- `x(t) = A cos ωt + B sin ωt` solves `x'' = -ω² x`: its velocity is
`v(t) = -Aω sin ωt + Bω cos ωt`, and the derivative of `v` is `-ω² x(t)`. -/
theorem harmonic_solves (A B ω t : ℝ) :
HasDerivAt (fun s => A * cos (ω * s) + B * sin (ω * s))
(-A * ω * sin (ω * t) + B * ω * cos (ω * t)) t ∧
HasDerivAt (fun s => -A * ω * sin (ω * s) + B * ω * cos (ω * s))
(-ω ^ 2 * (A * cos (ω * t) + B * sin (ω * t))) t := by
have hl : HasDerivAt (fun s => ω * s) ω t := by
simpa using (hasDerivAt_id' t).const_mul ω
constructor
· have h := ((hl.cos).const_mul A).add ((hl.sin).const_mul B)
convert h using 1
ring
· have h := ((hl.sin).const_mul (-A * ω)).add ((hl.cos).const_mul (B * ω))
convert h using 1
ring/-- **Uniqueness via energy.** If `x' = v` and `v' = -x` (so `x'' + x = 0`) with
`x(0) = v(0) = 0`, then `x ≡ 0`. Proof: the energy `E = x² + v²` has
`E' = 2x v + 2v(-x) = 0`, so `E` is constant, equal to `E(0) = 0`; hence `x(t)² ≤ E(t) = 0`. -/
theorem oscillator_unique {x v : ℝ → ℝ} (hx : ∀ t, HasDerivAt x (v t) t)
(hv : ∀ t, HasDerivAt v (-x t) t) (hx0 : x 0 = 0) (hv0 : v 0 = 0) (t : ℝ) : x t = 0 := by
let E : ℝ → ℝ := fun s => x s ^ 2 + v s ^ 2
have hE : ∀ s, HasDerivAt E 0 s := by
intro s
have h := ((hx s).pow 2).add ((hv s).pow 2)
convert h using 1
push_cast
ring
have hconst : E t = E 0 :=
is_const_of_deriv_eq_zero (fun s => (hE s).differentiableAt) (fun s => (hE s).deriv) t 0
have hE0 : E 0 = 0 := by simp [E, hx0, hv0]
have hsq : x t ^ 2 + v t ^ 2 = 0 := by
have := hconst.trans hE0
simpa [E] using this
nlinarith [sq_nonneg (x t), sq_nonneg (v t)]Linear systems: the derivative of for a square matrix . Mathlib proves it in any complete normed algebra; to view matrices as one we choose an operator norm (as local instances, since has several natural norms):
/-- For a square matrix `A`, the fundamental matrix `X(t) = e^{tA}` solves `X' = A X` (and
`X(0) = I`), so `x(t) = e^{tA} x₀` solves `x' = A x`, `x(0) = x₀`. This applies Mathlib's
`hasDerivAt_exp_smul_const'`, valid in any complete normed algebra; matrices become one by
choosing an operator norm (here the `L^∞` operator norm, as local instances). -/
theorem matrix_exp_solves {n : ℕ} (A : Matrix (Fin n) (Fin n) ℝ) (t : ℝ) :
HasDerivAt (fun s : ℝ => NormedSpace.exp (s • A)) (A * NormedSpace.exp (t • A)) t :=
hasDerivAt_exp_smul_const' A tWhat is and is not formalised. Independently proved: the explicit solutions above, uniqueness for and for , the integrating-factor identity, blow-up, non-uniqueness and the failure of the Lipschitz condition. Cited from Mathlib: Picard–Lindelöf existence, Grönwall-based uniqueness and the derivative of the matrix exponential. Not formalised here: the constant-coefficient theorem (dimension two of the solution space), the trace–determinant classification, Peano's theorem, Hartman–Grobman, and the maximal-interval "escape" theorem.
Exact iterates and numerical solutions
Picard's proof is constructive: the iterates can be computed. For they are the Taylor polynomials of ; for they rebuild, term by term, the geometric series of — whose radius of convergence foreshadows the blow-up.
"""Picard iteration, computed exactly with SymPy.
The initial value problem x' = f(t, x), x(t0) = x0 is equivalent to the
integral equation x(t) = x0 + integral from t0 to t of f(s, x(s)) ds. Picard's
iteration starts from the constant x0 and repeatedly substitutes into the
right-hand side. For x' = x it produces the Taylor polynomials of e^t; for
x' = t + x the iterates converge to 2e^t - t - 1; for x' = x^2, x(0) = 1 they
reproduce, term by term, the series 1 + t + t^2 + ... of the solution
1/(1 - t), which only converges for |t| < 1 - a shadow of the blow-up at t = 1.
Runs in the browser (Pyodide) with the sympy package loaded.
"""
import sympy
t, s = sympy.symbols("t s", real=True)
def picard_iterates(f, x0, count, t0=0):
"""Return [x_0, x_1, ..., x_count] with x_0 = x0 and
x_{k+1}(t) = x0 + integral_{t0}^{t} f(s, x_k(s)) ds."""
iterates = [sympy.Integer(x0) if isinstance(x0, int) else sympy.sympify(x0)]
for _ in range(count):
prev = iterates[-1]
integrand = f(s, prev.subs(t, s))
nxt = x0 + sympy.integrate(integrand, (s, t0, t))
iterates.append(sympy.expand(nxt))
return iterates
def agreement_order(approx, exact, max_order=20):
"""Largest n such that approx and exact agree up to and including t^n."""
diff = sympy.series(approx - exact, t, 0, max_order + 1).removeO()
poly = sympy.Poly(sympy.expand(diff), t)
nonzero = [m[0] for m, c in zip(poly.monoms(), poly.coeffs()) if c != 0]
return (min(nonzero) - 1) if nonzero else max_order
def main():
print(f"Picard iteration with SymPy {sympy.__version__}, all with x(0) = 1")
xs = picard_iterates(lambda u, x: x, 1, 5)
print("x' = x:")
for k in [1, 2, 3, 5]:
print(f" x_{k}(t) = {xs[k]}")
print(" these are the Taylor polynomials of the solution e^t")
ys = picard_iterates(lambda u, x: u + x, 1, 6)
exact = 2 * sympy.exp(t) - t - 1
print("x' = t + x (exact solution 2 e^t - t - 1):")
for k in [2, 4, 6]:
order = agreement_order(ys[k], exact)
value = float(ys[k].subs(t, 1))
print(f" x_{k}(1) = {value:.6f}, agrees with the solution up to t^{order}")
print(f" exact value at t = 1: {float(exact.subs(t, 1)):.6f}")
zs = picard_iterates(lambda u, x: x**2, 1, 4)
sol = 1 / (1 - t)
print("x' = x^2 (exact solution 1/(1 - t), which blows up at t = 1):")
for k in range(1, 5):
order = agreement_order(zs[k], sol)
degree = int(sympy.degree(zs[k], t))
print(f" x_{k}: degree {degree:2d}, agrees with 1/(1 - t) up to t^{order}")
print(" the series 1 + t + t^2 + ... converges only for |t| < 1;")
print(f" x_4(0.5) = {float(zs[4].subs(t, 0.5)):.6f} vs 1/(1 - 0.5) = 2,")
print(f" x_4(1.5) = {float(zs[4].subs(t, 1.5)):.1f}, but the solution ends at t = 1")
print()
print("Epistemic status: exact symbolic computation of a few iterates (as")
print("reliable as SymPy's integration). That the iterates converge to the")
print("unique solution on a short interval is the Picard-Lindelof theorem,")
print("proved in the lesson; the computation only illustrates it.")
if __name__ == "__main__":
main()In practice ODEs are solved numerically. Euler's method applied to on with steps gives exactly — compound interest — whose error is about : Euler's method is first order. The classical Runge–Kutta method (Runge 1895, Kutta 1901) uses four slopes per step and is fourth order. The experiment also shows two things numerics cannot tell you: when a solution is about to blow up, and when a solution is not unique.
"""Euler's method and RK4: convergence, blow-up and non-uniqueness.
1. For x' = x, x(0) = 1 on [0, 1], Euler's method with n steps gives exactly
(1 + 1/n)^n, with error about e/(2n): first order. The classical
fourth-order Runge-Kutta method (RK4) has error about C/n^4.
2. For x' = x^2, x(0) = 1 the solution 1/(1 - t) blows up at t = 1; RK4
values explode as t approaches 1.
3. For x' = sqrt|x|, x(0) = 0 both x = 0 and x = t^2/4 are solutions. Every
numerical method started exactly at 0 stays at 0; started at 1e-12 it
follows the parabola. Numerics cannot detect non-uniqueness.
Runs unchanged in the browser (Pyodide) using only the standard library.
"""
import math
def euler(f, t0, x0, t1, n):
"""Euler's method with n equal steps from (t0, x0) to time t1."""
h = (t1 - t0) / n
t, x = t0, x0
for _ in range(n):
x = x + h * f(t, x)
t = t + h
return x
def rk4(f, t0, x0, t1, n):
"""Classical fourth-order Runge-Kutta method with n equal steps."""
h = (t1 - t0) / n
t, x = t0, x0
for _ in range(n):
k1 = f(t, x)
k2 = f(t + h / 2, x + h / 2 * k1)
k3 = f(t + h / 2, x + h / 2 * k2)
k4 = f(t + h, x + h * k3)
x = x + h / 6 * (k1 + 2 * k2 + 2 * k3 + k4)
t = t + h
return x
def observed_order(error_n, error_2n):
"""Order p estimated from errors with n and 2n steps (error ~ C h^p)."""
return math.log2(abs(error_n) / abs(error_2n))
def growth(t, x):
"""Right-hand side of x' = x."""
return x
def square(t, x):
"""Right-hand side of x' = x^2."""
return x * x
def sqrt_abs(t, x):
"""Right-hand side of x' = sqrt|x|."""
return math.sqrt(abs(x))
def main():
print("1. x' = x, x(0) = 1: error at t = 1 (exact value e)")
print(" n Euler error (1 + 1/n)^n - e RK4 error")
prev = None
for n in [10, 20, 40, 80]:
e_err = euler(growth, 0.0, 1.0, 1.0, n) - math.e
formula = (1 + 1 / n) ** n - math.e
r_err = rk4(growth, 0.0, 1.0, 1.0, n) - math.e
print(f" {n:3d} {e_err:+.3e} {formula:+.3e} {r_err:+.3e}")
if prev:
p_e = observed_order(prev[0], e_err)
p_r = observed_order(prev[1], r_err)
prev = (e_err, r_err)
print(f" observed orders (n = 40 -> 80): Euler {p_e:.2f}, RK4 {p_r:.2f}")
print("2. x' = x^2, x(0) = 1 (solution 1/(1 - t)), RK4 with h = 0.001:")
for t1 in [0.9, 0.99, 0.999]:
value = rk4(square, 0.0, 1.0, t1, round(t1 * 1000))
print(f" t = {t1:<5} RK4 {value:12.3f} exact {1 / (1 - t1):12.3f}")
print(" with a fixed step RK4 lags behind near the singularity, and past")
print(" t = 1 there is no solution to approximate.")
print("3. x' = sqrt|x|: two solutions through (0, 0)")
for x0 in [0.0, 1e-12]:
value = rk4(sqrt_abs, 0.0, x0, 2.0, 2000)
print(f" start x(0) = {x0:<6g}: RK4 gives x(2) = {value:.6f}")
print(" (x = 0 and x = t^2/4 both solve the problem; t^2/4 = 1 at t = 2)")
print()
print("Epistemic status: floating-point experiments. They agree with the")
print("theory (Euler is first order, RK4 fourth order for smooth problems;")
print("1/(1 - t) blows up) but prove nothing; in part 3 the numerics silently")
print("pick one of two solutions, which only the theory reveals.")
if __name__ == "__main__":
main()The SymPy iterates are exact for the finitely many steps computed; the numerical solutions are floating-point evidence. Only the theorems guarantee convergence, existence and uniqueness.
Exercises
1. Ordinary differential equations
Warm-upHumanCarbon-14 decays according to , with a half-life of about years. A piece of charcoal from an ancient hearth is years old. Its carbon-14 content is of the original amount. Find .
2. Ordinary differential equations
Warm-upHumanSolve with using an integrating factor. What is ?
3. Ordinary differential equations
CoreHumanWhich is the general real solution of ?
4. Ordinary differential equations
CoreHumanClassify the equilibrium of for .
5. Ordinary differential equations
CoreHumanFor which initial value problems does the Picard–Lindelöf theorem guarantee a unique solution near ? (Select all that apply.)
6. Ordinary differential equations
StretchHumanThe solution of with blows up at a finite time . Find .
7. Ordinary differential equations
StretchHumanIn the uniqueness proof, satisfies for , and . Which step finishes the proof that ?
8. Ordinary differential equations
CoreFormalWhich Lean statement says that solves the differential equation at every time?
9. Ordinary differential equations
StretchComputationalWrite euler(f, t0, x0, t1, n) implementing Euler's method with equal steps of size , returning the approximation of . Check that for , it returns , and that doubling roughly halves the error.
Common misconceptions
Misconception
"Solutions exist for all time." Even with a smooth right-hand side, solutions can blow up in finite time: , has the solution , which exists only for . Picard–Lindelöf is a local theorem. (Linear equations with continuous coefficients are the exception: their solutions exist on the whole interval.)
Misconception
"Every differential equation can be solved in closed form." Most cannot. has no elementary solution (its solutions are , the error function), and Liouville proved that cannot be solved by quadratures. Existence theorems, qualitative analysis and numerical methods are how we understand most equations.
Misconception
"An initial condition always determines the solution." Only when the right-hand side is Lipschitz (for example ) in . , has infinitely many solutions.
Misconception
"A numerical solution that looks good is the solution." Numerical methods follow one solution and can be fooled: started at , every method for stays at , missing the other solutions; and near a blow-up a fixed-step method lags behind. Error estimates assume smoothness that may fail.
Misconception
"Separating variables means dividing by zero is fine." Dividing by loses the constant solutions at zeros of — exactly where non-uniqueness or equilibria live. Always check the zeros of separately.
Many ways of seeing
Geometry— Vector fields and flows
An autonomous system is a vector field on phase space, and its solutions are the integral curves — curves whose velocity at each point is the vector there. When solutions exist for all time, the map sending to the solution's position at time is the flow: and (by uniqueness), a one-parameter group of diffeomorphisms. For the flow is , a homomorphism from to the matrix group . On a manifold the same ideas apply verbatim; Sophus Lie's study of the symmetries of differential equations led him to the groups now named after him. See smooth manifolds and Lie groups.
Topology— What the plane allows: Poincaré–Bendixson and index theory
Topology constrains long-term behaviour. The Poincaré–Bendixson theorem says that a bounded trajectory of a planar system that does not approach any equilibrium tends to a periodic orbit (a limit cycle); so chaos is impossible for autonomous systems in the plane — it needs at least three dimensions, as in the Lorenz equations. The proof uses the Jordan curve theorem. The index of an isolated equilibrium (the number of turns the vector field makes along a small loop around it) is for nodes, spirals and centres and for saddles, and the indices of the equilibria inside a periodic orbit add up to — so every periodic orbit in the plane encloses at least one equilibrium. The VectorField widget's loop mode computes such indices. See dynamical systems.
Analysis— Fixed points in function spaces
The proof of Picard–Lindelöf is an instance of the Banach fixed-point theorem: the Picard map acts on a complete metric space of continuous functions, and (on a short interval, or with a weighted norm ) it is a contraction, so it has exactly one fixed point — the solution. Peano's theorem replaces the contraction by compactness: Euler polygons form an equicontinuous family, and the Arzelà–Ascoli theorem extracts a convergent subsequence. Both proofs view a differential equation as an equation for a point in an infinite-dimensional space, the starting idea of functional analysis.
Algebra— Linear equations are linear algebra
For a linear homogeneous equation the solutions form a vector space, and the existence and uniqueness theorem makes "initial data" an isomorphism with . Constant-coefficient equations are the polynomial algebra acting on functions: factors as , and the solution space decomposes accordingly — the same mathematics as the Jordan normal form, since spans the generalised eigenspaces of . Deeper, differential Galois theory (Picard and Vessiot, 1880s–1890s; Kolchin, 1940s) attaches to a linear ODE a group of symmetries of its solutions and characterises the equations solvable by quadratures by a solvability property of that group — a perfect parallel of Galois theory for polynomial equations. See fields and Galois theory.
Probability— Adding noise
Replace by " noise". Made precise by Itô (1944), this is a stochastic differential equation , driven by Brownian motion . Its solutions are random functions; their probability density evolves by a partial differential equation (the Fokker–Planck equation); and Euler's method becomes the Euler–Maruyama method, with increments for standard normal . Noise can push a solution over a barrier between two stable equilibria, a mechanism used to model climate transitions and chemical reactions. See stochastic processes.
Physics— Newton's second law is an ODE
Newton's law is a second-order ODE; with initial position and velocity, Picard–Lindelöf makes classical mechanics deterministic — for forces that are Lipschitz. (Norton's "dome", a frictionless surface on which , is a much-discussed example where the force fails to be Lipschitz and a ball at rest may start moving at an arbitrary time: the phenomenon in physical clothing.) The same damped-oscillator equation governs an electrical circuit with inductance, resistance and capacitance, so mechanical and electrical engineers share one theory. See classical mechanics.
History and global context
- Newton, Leibniz and the Bernoullis. Isaac Newton's Principia (1687) solved the motion of planets — differential equations in all but name — and his fluxion manuscripts (1671) classify first-order equations. Gottfried Wilhelm Leibniz found the method of separation of variables (1691), and the brothers Jacob and Johann Bernoulli solved the equation now called Bernoulli's (1695–97). Jacopo Riccati studied his equation in 1724.
- Euler and Lagrange. Leonhard Euler solved linear equations with constant coefficients through the characteristic equation (1743), introduced integrating factors, and described the numerical method named after him (Institutiones calculi integralis, 1768). Joseph-Louis Lagrange developed variation of parameters (1770s).
- Existence theory. Augustin-Louis Cauchy gave the first existence proofs in the 1820s, using Euler polygons and later power series. Joseph Liouville proved that some equations cannot be solved by quadratures (1841) and introduced successive approximations for linear equations (1838); Rudolf Lipschitz isolated his condition (1868–76). Émile Picard developed successive approximations in general (1890) and Ernst Lindelöf sharpened the result (1894); Giuseppe Peano proved existence under continuity alone (1886, 1890). Sofya Kovalevskaya proved the Cauchy–Kovalevskaya existence theorem for analytic equations (1874) and found a new integrable case of the spinning top (1888), for which she won the Prix Bordin.
- Qualitative theory. Henri Poincaré created the qualitative theory of differential equations (1881–86) and, studying the three-body problem (1890), discovered the sensitive dependence now called chaos. Aleksandr Lyapunov, in his 1892 thesis written in Kharkiv, founded stability theory. Ivar Bendixson completed the Poincaré–Bendixson theorem (1901). David Grobman (1959) and Philip Hartman (1960) proved the linearisation theorem, and Edward Lorenz's 1963 weather model made chaos famous.
- Numerical methods. Carl Runge (1895), Karl Heun (1900) and Martin Kutta (1901) developed the Runge–Kutta methods; today's adaptive and stiff solvers descend from work by Germund Dahlquist, John Butcher and many others from the 1950s onwards.
Further reading
- Morris W. Hirsch, Stephen Smale and Robert L. Devaney, Differential Equations, Dynamical Systems, and an Introduction to Chaos (2004; 3rd edition 2013) — linear systems, phase portraits and existence theory, at this lesson's level.
- Steven H. Strogatz, Nonlinear Dynamics and Chaos (1994; 2nd edition 2015) — qualitative methods with many applications.
- Vladimir I. Arnold, Ordinary Differential Equations (1973; English translations 1978 and 1992) — geometric, with vector fields and flows from the start.
- George F. Simmons, Differential Equations with Applications and Historical Notes (1972; 3rd edition 2016) — classical methods with rich history.
- William E. Boyce and Richard C. DiPrima, Elementary Differential Equations and Boundary Value Problems (1965; many later editions) — a standard first course.
- Ernst Hairer, Syvert P. Nørsett and Gerhard Wanner, Solving Ordinary Differential Equations I: Nonstiff Problems (1987) — numerical methods, with their history.
Next in the Lumenary: classical mechanics, dynamical systems, partial differential equations, mathematical biology and numerical analysis.