Skip to content
The Lumenary
Early undergraduateUndergraduate (first or second year) 120 min

Ordinary differential equations

Equations whose unknown is a whole function — how to solve the ones that can be solved, why most cannot, and the theorem that tells you a solution exists, is unique, and may still run off to infinity.

Formally verified · 15 LeanComputationally tested · 2 PythonAI-drafted · awaiting human reviewMathematically reviewed: Awaiting human review
MotivationH

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).

IntuitionH

Following the arrows

An equation x′=f(t,x)x' = f(t, x) assigns a slope f(t,x)f(t, x) to every point of the (t,x)(t, x)-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 hh 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.

Examples

dy/dx = y·(1 − y)

Click the field to draw a solution, or type a start point below.

Start point

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 ff). And the horizontal lines x=0x = 0 and x=1x = 1 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.

DefinitionsH

Definitions

Definition— ordinary differential equation

An ordinary differential equation of order nn is an equation F(t,x,x′,…,x(n))=0F\big(t, x, x', \dots, x^{(n)}\big) = 0 relating an unknown function xx of one variable tt to its first nn derivatives. A solution on an interval II is an nn times differentiable function x:I→Rx : I \to \R (or Rd\R^d) satisfying the equation for every t∈It \in I. An initial value problem (IVP) adds conditions x(t0)=x0,…,x(n−1)(t0)=xn−1x(t_0) = x_0, \dots, x^{(n-1)}(t_0) = x_{n-1}.

The equation is linear if it has the form an(t)x(n)+⋯+a1(t)x′+a0(t)x=g(t)a_n(t)x^{(n)} + \cdots + a_1(t)x' + a_0(t)x = g(t), and homogeneous if moreover g=0g = 0. A first-order system is x′=f(t,x)x' = f(t, x) with x(t)∈Rdx(t) \in \R^d; it is autonomous if ff does not depend on tt. Every nn-th order equation x(n)=G(t,x,…,x(n−1))x^{(n)} = G(t, x, \dots, x^{(n-1)}) becomes a first-order system for the vector (x,x′,…,x(n−1))(x, x', \dots, x^{(n-1)}) — for example, x′′=−xx'' = -x becomes (x,v)′=(v,−x)(x, v)' = (v, -x) — so first-order systems are the general case.

Definition— equilibrium and stability

An equilibrium of the autonomous system x′=f(x)x' = f(x) is a point x∗x^* with f(x∗)=0f(x^*) = 0; the constant function x(t)=x∗x(t) = x^* is then a solution. It is stable if solutions starting close to x∗x^* stay close for all t≥0t \ge 0 (for every ε>0\varepsilon > 0 there is δ>0\delta > 0 such that ∣x(0)−x∗∣<δ|x(0) - x^*| < \delta implies ∣x(t)−x∗∣<ε|x(t) - x^*| < \varepsilon for all t≥0t \ge 0), and asymptotically stable if moreover such solutions tend to x∗x^*.

Definition— Lipschitz condition

f(t,x)f(t, x) is Lipschitz in xx on a set RR, with constant KK, if ∣f(t,x)−f(t,y)∣≤K∣x−y∣|f(t, x) - f(t, y)| \le K|x - y| for all (t,x),(t,y)∈R(t, x), (t, y) \in R. By the mean value theorem, this holds on any closed box where ∂f/∂x\partial f/\partial x is continuous (take KK = the maximum of its absolute value, or of the norm of the Jacobian matrix in several variables).

ProofH

Equations we can solve

Separable equations x′=g(t) h(x)x' = g(t)\,h(x). Wherever h(x)≠0h(x) \ne 0 we may write x′h(x)=g(t)\frac{x'}{h(x)} = g(t). If H′=1/hH' = 1/h and G′=gG' = g, the chain rule shows that ddtH(x(t))=x′(t)h(x(t))=g(t)\frac{d}{dt}H(x(t)) = \frac{x'(t)}{h(x(t))} = g(t), so H(x(t))=G(t)+CH(x(t)) = G(t) + C; solve for xx. In addition every zero x∗x^* of hh gives a constant solution x≡x∗x \equiv x^*. (The familiar shorthand "∫dx/h(x)=∫g(t) dt\int dx/h(x) = \int g(t)\,dt" is exactly this argument.)

Example— Three separable equations, three behaviours

  • Logistic growth x′=x(1−x)x' = x(1 - x). Partial fractions 1x(1−x)=1x+11−x\frac{1}{x(1-x)} = \frac1x + \frac1{1-x} give ln⁡∣x1−x∣=t+c\ln\left|\frac{x}{1 - x}\right| = t + c, so x(t)=11+Ce−tx(t) = \dfrac{1}{1 + Ce^{-t}}. For C>0C > 0 these rise from 00 to 11; together with x≡0x \equiv 0 and x≡1x \equiv 1 they are all the solutions in [0,1][0, 1].
  • Blow-up x′=x2x' = x^2, x(0)=1x(0) = 1. Here −1x=t+c-\frac1x = t + c, so x(t)=11−tx(t) = \dfrac{1}{1 - t}, which tends to +∞+\infty as t→1−t \to 1^-. The right-hand side x2x^2 is perfectly smooth, yet the solution exists only on (−∞,1)(-\infty, 1).
  • Non-uniqueness x′=∣x∣x' = \sqrt{|x|}, x(0)=0x(0) = 0. Besides x≡0x \equiv 0 (from the zero of hh), separation on x>0x > 0 gives 2x=t+c2\sqrt{x} = t + c, and gluing gives the solution x(t)=t2/4x(t) = t^2/4 for t≥0t \ge 0, x(t)=0x(t) = 0 for t<0t < 0. Indeed for every c≥0c \ge 0, the function equal to 00 for t≤ct \le c and (t−c)2/4(t - c)^2/4 for t≥ct \ge c 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?

Examples

dy/dx = sqrt(abs(y))

Click the field to draw a solution, or type a start point below.

Start point

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 p,qp, q be continuous on an interval II, t0∈It_0 \in I, and P(t)=∫t0tp(s) dsP(t) = \int_{t_0}^t p(s)\,ds. The initial value problem y′+p(t) y=q(t)y' + p(t)\,y = q(t), y(t0)=y0y(t_0) = y_0 has exactly one solution on II, namely

y(t)=e−P(t)(y0+∫t0tq(s) eP(s) ds).y(t) = e^{-P(t)}\left(y_0 + \int_{t_0}^{t} q(s)\,e^{P(s)}\,ds\right).

Proof

The key identity, from the product and chain rules and P′=pP' = p, is

(y eP)′=(y′+p y) eP.\big(y\,e^{P}\big)' = (y' + p\,y)\,e^{P} .

So yy solves the equation if and only if (yeP)′=qeP(y e^P)' = q e^P. Existence: the displayed yy has yeP=y0+∫t0tqePy e^{P} = y_0 + \int_{t_0}^t q e^{P}, whose derivative is qePq e^P by the fundamental theorem of calculus, and y(t0)=y0y(t_0) = y_0. Uniqueness: if y1,y2y_1, y_2 are solutions, z=y1−y2z = y_1 - y_2 satisfies (zeP)′=0(z e^{P})' = 0 on the interval II, so zePz e^P is constant, equal to its value 00 at t0t_0.

∎

The factor ePe^{P} is the integrating factor. With p=−ap = -a and q=0q = 0 the theorem says that the only solutions of x′=axx' = ax are x(t)=x(0)eatx(t) = x(0)e^{at} — the law of exponential growth and decay.

Linear equations with constant coefficients. For y′′+b y′+c y=0y'' + b\,y' + c\,y = 0, try y=erty = e^{rt}: it is a solution exactly when r2+br+c=0r^2 + br + c = 0, the characteristic equation.

Theorem— Theorem (second-order constant-coefficient equations)

The real solutions of y′′+by′+cy=0y'' + by' + cy = 0 form a two-dimensional vector space, with basis

  • er1t,  er2te^{r_1 t},\; e^{r_2 t} if the characteristic roots r1≠r2r_1 \ne r_2 are real;
  • ert,  t erte^{rt},\; t\,e^{rt} if there is a double root rr;
  • eαtcos⁡βt,  eαtsin⁡βte^{\alpha t}\cos\beta t,\; e^{\alpha t}\sin\beta t if the roots are α±iβ\alpha \pm i\beta with β≠0\beta \ne 0.

Proof

Write D=d/dtD = d/dt; the equation is (D−r1)(D−r2) y=0(D - r_1)(D - r_2)\,y = 0, where r1,r2r_1, r_2 are the roots (the factors commute because the coefficients are constant). Each listed function is a solution: (D−r)ert=0(D - r)e^{rt} = 0; for a double root, (D−r)(tert)=ert(D - r)(t e^{rt}) = e^{rt} and then (D−r)ert=0(D - r)e^{rt} = 0; for complex roots, e(α+iβ)te^{(\alpha + i\beta)t} 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 t=0t = 0 are r2−r1r_2 - r_1, 11 and β\beta, all non-zero. The space is two-dimensional: by the existence and uniqueness theorem below, the linear map y↦(y(0),y′(0))y \mapsto (y(0), y'(0)) from solutions to R2\R^2 is a bijection.

∎

The damped oscillator my′′+γy′+ky=0m y'' + \gamma y' + k y = 0 (m,k>0m, k > 0, γ≥0\gamma \ge 0) has roots −γ±γ2−4mk2m\frac{-\gamma \pm \sqrt{\gamma^2 - 4mk}}{2m}: overdamped if γ2>4mk\gamma^2 > 4mk (two decaying exponentials), critically damped if γ2=4mk\gamma^2 = 4mk, and underdamped if γ2<4mk\gamma^2 < 4mk (decaying oscillations e−γt/2mcos⁡(ωt+ϕ)e^{-\gamma t/2m}\cos(\omega t + \phi)). For an inhomogeneous equation y′′+by′+cy=g(t)y'' + by' + cy = g(t), the general solution is one particular solution plus the general homogeneous solution, because the difference of two solutions solves the homogeneous equation.

ProofH

Linear systems and phase portraits

For a constant d×dd \times d matrix AA, define the matrix exponential

etA=∑k=0∞tkAkk!=I+tA+t2A22+⋯ .e^{tA} = \sum_{k=0}^{\infty} \frac{t^k A^k}{k!} = I + tA + \frac{t^2A^2}{2} + \cdots .

The series converges absolutely for every tt in any operator norm, since ∥Ak∥≤∥A∥k\|A^k\| \le \|A\|^k, and it can be differentiated term by term (it is a power series in tt with infinite radius of convergence): ddtetA=A etA=etAA\frac{d}{dt}e^{tA} = A\,e^{tA} = e^{tA}A. If AB=BAAB = BA then eA+B=eAeBe^{A + B} = e^A e^B (multiply the series, exactly as for numbers); in particular e−tAetA=Ie^{-tA}e^{tA} = I.

Theorem— Theorem (linear systems)

For every x0∈Rdx_0 \in \R^d, the unique solution of x′=Axx' = Ax with x(0)=x0x(0) = x_0 is x(t)=etAx0x(t) = e^{tA}x_0, defined for all t∈Rt \in \R. If AA has a basis of eigenvectors v1,…,vdv_1, \dots, v_d with eigenvalues λ1,…,λd\lambda_1, \dots, \lambda_d and x0=∑civix_0 = \sum c_i v_i, then x(t)=∑cieλitvix(t) = \sum c_i e^{\lambda_i t} v_i.

Proof

x(t)=etAx0x(t) = e^{tA}x_0 satisfies x′=AetAx0=Axx' = Ae^{tA}x_0 = Ax and x(0)=x0x(0) = x_0. If yy is any solution, then (e−tAy(t))′=−Ae−tAy+e−tAAy=0\big(e^{-tA}y(t)\big)' = -Ae^{-tA}y + e^{-tA}Ay = 0 (as AA commutes with e−tAe^{-tA}), so e−tAy(t)=y(0)=x0e^{-tA}y(t) = y(0) = x_0 and y(t)=etAx0y(t) = e^{tA}x_0. Finally, Akvi=λikviA^k v_i = \lambda_i^k v_i gives etAvi=eλitvie^{tA}v_i = e^{\lambda_i t}v_i.

∎

Complex eigenvalues α±iβ\alpha \pm i\beta produce rotation combined with growth or decay at rate eαte^{\alpha t}: for A=(01−10)A = \begin{pmatrix} 0 & 1 \\ -1 & 0 \end{pmatrix} (that is, x′′=−xx'' = -x), etA=(cos⁡tsin⁡t−sin⁡tcos⁡t)e^{tA} = \begin{pmatrix} \cos t & \sin t \\ -\sin t & \cos t \end{pmatrix}, a rotation. Non-diagonalisable matrices, via the Jordan form, add polynomial factors tkeλtt^k e^{\lambda t}.

Theorem— Theorem (classification of planar linear systems)

Let AA be a real 2×22 \times 2 matrix with trace τ\tau and determinant Δ≠0\Delta \ne 0, and consider x′=Axx' = Ax, whose only equilibrium is 00. Then:

  • Δ<0\Delta < 0: a saddle (unstable);
  • Δ>0\Delta > 0 and τ2>4Δ\tau^2 > 4\Delta: a node, asymptotically stable if τ<0\tau < 0, unstable if τ>0\tau > 0;
  • Δ>0\Delta > 0 and τ2<4Δ\tau^2 < 4\Delta: a spiral (focus), asymptotically stable if τ<0\tau < 0, unstable if τ>0\tau > 0, and a centre (stable, not asymptotically) if τ=0\tau = 0.

Proof

The eigenvalues are the roots of λ2−τλ+Δ=0\lambda^2 - \tau\lambda + \Delta = 0, so λ1λ2=Δ\lambda_1\lambda_2 = \Delta and λ1+λ2=τ\lambda_1 + \lambda_2 = \tau. If Δ<0\Delta < 0 they are real with opposite signs: along one eigenvector solutions decay, along the other they grow. If Δ>0\Delta > 0 and τ2>4Δ\tau^2 > 4\Delta they are real, distinct, and of the same sign as τ\tau: all solutions c1eλ1tv1+c2eλ2tv2c_1e^{\lambda_1 t}v_1 + c_2e^{\lambda_2 t}v_2 decay (or all grow). If τ2<4Δ\tau^2 < 4\Delta they are τ2±iβ\frac\tau2 \pm i\beta, and solutions are rotations multiplied by eτt/2e^{\tau t/2}. (The boundary case τ2=4Δ\tau^2 = 4\Delta gives degenerate or star nodes, with the stability of the sign of τ\tau.)

∎

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.

Examples

x′ = y, y′ = −2·x − 0.6·y

darker, longer arrows = faster forward in time backward in timeequilibrium (hollow = stable or centre)

Click the plane to start a trajectory, or type a start point below.

Start point

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)

TDD = T²/4
Trace T and determinant D: below the T-axis, saddles; above the dashed parabola D = T²/4, spirals; between them, nodes; on the upper D-axis, centres. Left of the D-axis is stable, right is unstable.
ProofH

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 ∣⋅∣|\cdot| for a norm on Rd\R^d.

Theorem— Theorem (Picard–Lindelöf)

Let f(t,x)f(t, x) be continuous on the box R={(t,x):∣t−t0∣≤a, ∣x−x0∣≤b}R = \{(t, x) : |t - t_0| \le a,\ |x - x_0| \le b\}, with ∣f∣≤M|f| \le M on RR, and Lipschitz in xx on RR with constant KK. Let h=min⁡(a,b/M)h = \min(a, b/M). Then the initial value problem x′=f(t,x)x' = f(t, x), x(t0)=x0x(t_0) = x_0 has exactly one solution on [t0−h,t0+h][t_0 - h, t_0 + h].

Proof

Write I=[t0−h,t0+h]I = [t_0 - h, t_0 + h].

Step 1: an integral equation. By the fundamental theorem of calculus, a continuous x:I→Rdx : I \to \R^d with ∣x(t)−x0∣≤b|x(t) - x_0| \le b solves the IVP if and only if

x(t)=x0+∫t0tf(s,x(s)) dsfor all t∈I.x(t) = x_0 + \int_{t_0}^{t} f(s, x(s))\,ds \qquad \text{for all } t \in I.

Step 2: Picard iterates. Put x0(t)=x0x_0(t) = x_0 and xk+1(t)=x0+∫t0tf(s,xk(s)) dsx_{k+1}(t) = x_0 + \int_{t_0}^t f(s, x_k(s))\,ds. By induction each xkx_k is continuous on II with ∣xk(t)−x0∣≤M∣t−t0∣≤Mh≤b|x_k(t) - x_0| \le M|t - t_0| \le Mh \le b, so (s,xk(s))(s, x_k(s)) stays in RR and the next iterate is defined.

Step 3: an estimate. We claim ∣xk+1(t)−xk(t)∣≤MKk∣t−t0∣k+1(k+1)!|x_{k+1}(t) - x_k(t)| \le \dfrac{M K^k |t - t_0|^{k+1}}{(k + 1)!}. For k=0k = 0 this is ∣x1(t)−x0∣≤M∣t−t0∣|x_1(t) - x_0| \le M|t - t_0|. If it holds for kk, then by the Lipschitz condition (for t≥t0t \ge t_0; t≤t0t \le t_0 is symmetric)

∣xk+2(t)−xk+1(t)∣≤∫t0tK ∣xk+1(s)−xk(s)∣ ds≤∫t0tMKk+1(s−t0)k+1(k+1)! ds=MKk+1(t−t0)k+2(k+2)!.|x_{k+2}(t) - x_{k+1}(t)| \le \int_{t_0}^{t} K\,|x_{k+1}(s) - x_k(s)|\,ds \le \int_{t_0}^t \frac{M K^{k+1}(s - t_0)^{k+1}}{(k+1)!}\,ds = \frac{M K^{k+1}(t - t_0)^{k+2}}{(k+2)!}.

Step 4: convergence. Since ∑kMKkhk+1/(k+1)!\sum_k M K^k h^{k+1}/(k+1)! converges (to MK(eKh−1)\frac{M}{K}(e^{Kh} - 1), or MhMh if K=0K = 0), the Weierstrass M-test shows that xk=x0+∑j<k(xj+1−xj)x_k = x_0 + \sum_{j < k}(x_{j+1} - x_j) converges uniformly on II to a continuous function xx with ∣x(t)−x0∣≤b|x(t) - x_0| \le b. As ∣f(s,xk(s))−f(s,x(s))∣≤K∣xk(s)−x(s)∣→0|f(s, x_k(s)) - f(s, x(s))| \le K|x_k(s) - x(s)| \to 0 uniformly, we may pass to the limit under the integral sign in the definition of xk+1x_{k+1}: xx satisfies the integral equation, hence solves the IVP.

Step 5: uniqueness (Grönwall's argument). A solution yy on II stays in the box: up to the first time it could leave, ∣y(t)−x0∣≤M∣t−t0∣≤Mh≤b|y(t) - x_0| \le M|t - t_0| \le Mh \le b, so it never does. So let x,yx, y be two solutions with values in the box, and for t∈[t0,t0+h]t \in [t_0, t_0 + h] put u(t)=∣x(t)−y(t)∣u(t) = |x(t) - y(t)|. Subtracting the integral equations, u(t)≤K∫t0tu(s) dsu(t) \le K\int_{t_0}^t u(s)\,ds. Let U(t)=∫t0tuU(t) = \int_{t_0}^t u; then U′=u≤KUU' = u \le KU, so

(e−K(t−t0)U(t))′=e−K(t−t0)(u(t)−KU(t))≤0.\big(e^{-K(t - t_0)}U(t)\big)' = e^{-K(t-t_0)}\big(u(t) - KU(t)\big) \le 0 .

Since U(t0)=0U(t_0) = 0, we get e−K(t−t0)U(t)≤0e^{-K(t-t_0)}U(t) \le 0; but U≥0U \ge 0, so U≡0U \equiv 0 and then u≤KU=0u \le KU = 0. The same argument works for t≤t0t \le t_0.

∎

The same Grönwall argument gives continuous dependence on initial data: two solutions with x(t0)=x0x(t_0) = x_0 and y(t0)=y0y(t_0) = y_0 satisfy ∣x(t)−y(t)∣≤∣x0−y0∣ eK∣t−t0∣|x(t) - y(t)| \le |x_0 - y_0|\,e^{K|t - t_0|}. The factor eK∣t−t0∣e^{K|t - t_0|} can be enormous, which is the seed of chaos in nonlinear systems.

What the theorem does not say.

  1. It is local. The solution of x′=x2x' = x^2, x(0)=1x(0) = 1 exists only for t<1t < 1, although x2x^2 is smooth everywhere. In general, if ff is locally Lipschitz on R×Rd\R \times \R^d, every solution extends to a maximal open interval (α,ω)(\alpha, \omega), and if ω<∞\omega < \infty then ∣x(t)∣→∞|x(t)| \to \infty as t→ωt \to \omega: solutions die only by escaping to infinity. Linear systems never blow up.
  2. Continuity alone gives existence but not uniqueness. Peano's theorem (1890) says that if ff is merely continuous, a solution exists for a short time; the proof uses compactness (Arzelà–Ascoli) instead of a contraction. For x′=∣x∣x' = \sqrt{|x|} the right-hand side is continuous but not Lipschitz near x=0x = 0, and uniqueness fails, as we saw.
  3. It gives no formula. Liouville showed in 1841 that the Riccati equation x′=t2+x2x' = t^2 + x^2 cannot be solved by quadratures (elementary functions and their integrals); its solutions need Bessel functions. Most ODEs, including the pendulum θ′′=−sin⁡θ\theta'' = -\sin\theta and the three-body problem, have no elementary closed-form solution — hence the importance of qualitative and numerical methods.
ExplanationH

Qualitative behaviour

For an autonomous scalar equation x′=f(x)x' = f(x) with ff locally Lipschitz, uniqueness alone gives a complete picture. Solutions cannot cross the equilibria (zeros of ff), so a solution starting between two consecutive equilibria stays between them, and since x′=f(x)x' = f(x) has a fixed sign there, it is monotone and tends to one of them (or to ±∞\pm\infty). Hence an equilibrium x∗x^* with f′(x∗)<0f'(x^*) < 0 is asymptotically stable (arrows point towards it on both sides) and one with f′(x∗)>0f'(x^*) > 0 is unstable. For the logistic equation f(x)=x(1−x)f(x) = x(1 - x), f′(0)=1f'(0) = 1 and f′(1)=−1f'(1) = -1: the population moves away from extinction and settles at the carrying capacity.

In the plane, an equilibrium x∗x^* of x′=f(x)x' = f(x) is studied through the linearisation y′=Df(x∗) yy' = Df(x^*)\,y. The Hartman–Grobman theorem (Grobman 1959, Hartman 1960) says that if Df(x∗)Df(x^*) has no eigenvalue with zero real part (a hyperbolic equilibrium), then near x∗x^* 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 VV with a strict minimum at x∗x^* that does not increase along solutions. For the damped pendulum θ′′=−sin⁡θ−γθ′\theta'' = -\sin\theta - \gamma\theta', the energy E=12θ′2+(1−cos⁡θ)E = \tfrac12\theta'^2 + (1 - \cos\theta) satisfies E′=θ′θ′′+sin⁡θ θ′=−γθ′2≤0E' = \theta'\theta'' + \sin\theta\,\theta' = -\gamma\theta'^2 \le 0, so the bottom equilibrium is stable — without solving the equation. Energy is also a powerful uniqueness tool: for x′′=−xx'' = -x, E=x2+x′2E = x^2 + x'^2 is constant, so a solution with x(0)=x′(0)=0x(0) = x'(0) = 0 stays at 00 (this is formalised below).

Formal mathematicsF

Differential equations in Lean and Mathlib

In Mathlib, "xx solves x′=f(t,x)x' = f(t, x)" 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 e−ate^{-at} — a quantity with zero derivative is constant:

exp_solvesLean 4Proved here from basic factsFormally verified
/-- `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
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor
exp_uniqueLean 4Proved here from basic factsFormally verified
/-- **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]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor
integrating_factorLean 4Proved here from basic factsFormally verified
/-- **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
  ring
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor

The logistic solution found by separation of variables, verified by differentiation:

logistic_solvesLean 4Proved here from basic factsFormally verified
/-- 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
  ring
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor

Blow-up: 1/(1−t)1/(1 - t) solves x′=x2x' = x^2 and escapes to infinity as t→1−t \to 1^-:

blowup_solvesLean 4Proved here from basic factsFormally verified
/-- `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
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor
blowup_tendstoLean 4Proved here from basic factsFormally verified
/-- …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]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor

Non-uniqueness for x′=∣x∣x' = \sqrt{|x|}. The glued solution is written (max⁡(t,0))2/4(\max(t, 0))^2/4; at t=0t = 0 its derivative is found by squeezing, ∣x(h)∣≤h2/4=o(h)|x(h)| \le h^2/4 = o(h). Then the reason Picard–Lindelöf does not apply — ∣x∣\sqrt{|x|} is not Lipschitz:

halfSqLean 4Formally verified
/-- 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
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor
halfSq_solvesLean 4Proved here from basic factsFormally verified
/-- `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
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor
nonuniqueLean 4Proved here from basic factsFormally verified
/-- **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]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor
sqrt_abs_not_lipschitzLean 4Proved here from basic factsFormally verified
/-- 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
  linarith
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor

Mathlib's Picard–Lindelöf theorem. Its hypotheses IsPicardLindelof f t₀ x₀ a r L K are those of the lesson (continuity in tt, Lipschitz constant KK on a ball of radius aa, bound LL — our MM — and a time interval short enough that L⋅L \cdot length ≤a\le a); 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_lindelofLean 4Applies a library theoremFormally verified
/-- **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₀
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor
unique_of_lipschitzLean 4Applies a library theoremFormally verified
/-- **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 _⟩) heq
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor

Second-order equations: the harmonic oscillator's solutions, and uniqueness by conservation of energy — a proof that does not use Picard–Lindelöf at all:

harmonic_solvesLean 4Proved here from basic factsFormally verified
/-- `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
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor
oscillator_uniqueLean 4Proved here from basic factsFormally verified
/-- **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)]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor

Linear systems: the derivative of etAe^{tA} for a square matrix AA. Mathlib proves it in any complete normed algebra; to view matrices as one we choose an operator norm (as local instances, since Rd×d\R^{d \times d} has several natural norms):

matrix_exp_solvesLean 4Applies a library theoremFormally verified
/-- 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 t
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/OrdinaryDifferentialEquations.leanTry it in the Lean web editor

What is and is not formalised. Independently proved: the explicit solutions above, uniqueness for x′=axx' = ax and for x′′=−xx'' = -x, 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.

ComputationC

Exact iterates and numerical solutions

Picard's proof is constructive: the iterates can be computed. For x′=xx' = x they are the Taylor polynomials of ete^t; for x′=x2x' = x^2 they rebuild, term by term, the geometric series of 1/(1−t)1/(1 - t) — whose radius of convergence 11 foreshadows the blow-up.

Picard iteration: exact successive approximationsPythonSymbolic computationExact computationComputationally tested
"""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()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

In practice ODEs are solved numerically. Euler's method xk+1=xk+h f(tk,xk)x_{k+1} = x_k + h\,f(t_k, x_k) applied to x′=xx' = x on [0,1][0, 1] with nn steps gives exactly (1+1/n)n(1 + 1/n)^n — compound interest — whose error is about e/(2n)e/(2n): 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 and Runge-Kutta: convergence, blow-up, non-uniquenessPythonNumerical approximationExperimental evidenceComputationally tested
"""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()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

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

Exercises

1. Ordinary differential equations

Warm-upHuman

Carbon-14 decays according to x′=−kxx' = -kx, with a half-life of about 57305730 years. A piece of charcoal from an ancient hearth is 17 19017\,190 years old. Its carbon-14 content is 1/n1/n of the original amount. Find nn.

2. Ordinary differential equations

Warm-upHuman

Solve y′+2y=4y' + 2y = 4 with y(0)=0y(0) = 0 using an integrating factor. What is lim⁡t→∞y(t)\lim_{t \to \infty} y(t)?

3. Ordinary differential equations

CoreHuman

Which is the general real solution of y′′+2y′+5y=0y'' + 2y' + 5y = 0?

Choose one

4. Ordinary differential equations

CoreHuman

Classify the equilibrium 00 of x′=Axx' = Ax for A=(1232)A = \begin{pmatrix} 1 & 2 \\ 3 & 2 \end{pmatrix}.

Choose one

5. Ordinary differential equations

CoreHuman

For which initial value problems does the Picard–Lindelöf theorem guarantee a unique solution near t=0t = 0? (Select all that apply.)

Choose all that apply

6. Ordinary differential equations

StretchHuman

The solution of x′=x2x' = x^2 with x(0)=13x(0) = \tfrac13 blows up at a finite time T>0T > 0. Find TT.

7. Ordinary differential equations

StretchHuman

In the uniqueness proof, u(t)=∣x(t)−y(t)∣u(t) = |x(t) - y(t)| satisfies u(t)≤K∫t0tu(s) dsu(t) \le K\int_{t_0}^t u(s)\,ds for t≥t0t \ge t_0, and U(t)=∫t0tuU(t) = \int_{t_0}^t u. Which step finishes the proof that u≡0u \equiv 0?

Choose one

8. Ordinary differential equations

CoreFormal

Which Lean statement says that x:R→Rx : \R \to \R solves the differential equation x′=a xx' = a\,x at every time?

Choose one

9. Ordinary differential equations

StretchComputational

Write euler(f, t0, x0, t1, n) implementing Euler's method xk+1=xk+hf(tk,xk)x_{k+1} = x_k + h f(t_k, x_k) with nn equal steps of size h=(t1−t0)/nh = (t_1 - t_0)/n, returning the approximation of x(t1)x(t_1). Check that for x′=xx' = x, x(0)=1x(0) = 1 it returns (1+1/n)n(1 + 1/n)^n, and that doubling nn roughly halves the error.

Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.
Common misconceptionsH

Common misconceptions

Misconception

"Solutions exist for all time." Even with a smooth right-hand side, solutions can blow up in finite time: x′=x2x' = x^2, x(0)=1x(0) = 1 has the solution 1/(1−t)1/(1 - t), which exists only for t<1t < 1. 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. x′=e−t2x' = e^{-t^2} has no elementary solution (its solutions are ∫e−t2dt\int e^{-t^2}dt, the error function), and Liouville proved that x′=t2+x2x' = t^2 + x^2 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 C1C^1) in xx. x′=∣x∣x' = \sqrt{|x|}, x(0)=0x(0) = 0 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 x(0)=0x(0) = 0, every method for x′=∣x∣x' = \sqrt{|x|} stays at 00, 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 h(x)h(x) loses the constant solutions at zeros of hh — exactly where non-uniqueness or equilibria live. Always check the zeros of hh separately.

ViewpointsH

Many ways of seeing

Geometry— Vector fields and flows

An autonomous system x′=f(x)x' = f(x) 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 φt\varphi_t sending x0x_0 to the solution's position at time tt is the flow: φ0=id\varphi_0 = \mathrm{id} and φs+t=φs∘φt\varphi_{s + t} = \varphi_s \circ \varphi_t (by uniqueness), a one-parameter group of diffeomorphisms. For x′=Axx' = Ax the flow is etAe^{tA}, a homomorphism from (R,+)(\R, +) to the matrix group GL(d,R)GL(d, \R). 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 C1C^1 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 +1+1 for nodes, spirals and centres and −1-1 for saddles, and the indices of the equilibria inside a periodic orbit add up to +1+1 — 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 T(x)(t)=x0+∫t0tf(s,x(s)) dsT(x)(t) = x_0 + \int_{t_0}^t f(s, x(s))\,ds acts on a complete metric space of continuous functions, and (on a short interval, or with a weighted norm sup⁡te−2K∣t−t0∣∣x(t)∣\sup_t e^{-2K|t - t_0|}|x(t)|) 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 Rn\R^n. Constant-coefficient equations are the polynomial algebra R[D]\R[D] acting on functions: p(D)y=0p(D)y = 0 factors as ∏(D−ri)mi\prod (D - r_i)^{m_i}, and the solution space decomposes accordingly — the same mathematics as the Jordan normal form, since tkertt^k e^{rt} spans the generalised eigenspaces of DD. 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 x′=f(x)x' = f(x) by "x′=f(x)+σ⋅x' = f(x) + \sigma \cdot noise". Made precise by Itô (1944), this is a stochastic differential equation dX=f(X) dt+σ dWdX = f(X)\,dt + \sigma\,dW, driven by Brownian motion WW. 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 σh Z\sigma\sqrt{h}\,Z for standard normal ZZ. 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 mx′′=F(x,x′,t)m x'' = F(x, x', t) 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 r′′=rr'' = \sqrt r, 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 x′=∣x∣x' = \sqrt{|x|} phenomenon in physical clothing.) The same damped-oscillator equation Ly′′+Ry′+y/C=0Ly'' + Ry' + y/C = 0 governs an electrical circuit with inductance, resistance and capacitance, so mechanical and electrical engineers share one theory. See classical mechanics.

History & global context

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

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.