Chapter 23
25 min read
Section 197 of 353

The Eigenvalue Method

Systems of Differential Equations

Learning Objectives

By the end of this section you will be able to:

  1. Explain why the educated guess x(t)=eλtvx(t) = e^{\lambda t} v turns the coupled system x=Axx' = A x into the algebraic problem Av=λvAv = \lambda v.
  2. Compute eigenvalues and eigenvectors of a 2×22 \times 2 matrix by hand from det(AλI)=0\det(A - \lambda I) = 0.
  3. Build the general solution x(t)=c1eλ1tv1+c2eλ2tv2x(t) = c_1 e^{\lambda_1 t} v_1 + c_2 e^{\lambda_2 t} v_2 and fit the constants c1,c2c_1, c_2 to an initial condition.
  4. Interpret the sign of each eigenvalue as a growth or decay rate, and each eigenvector as the direction of an uncoupled mode.
  5. Verify the eigen-method solution against direct numerical integration in Python and PyTorch.

The Big Picture: Why Eigenvalues?

"A linear system x=Axx' = Ax is two coupled equations — until you look in the right directions. In the eigen-directions the system uncouples, and each piece becomes a one-dimensional ODE you already know how to solve."

Suppose you are tracking two interacting populations, two springs joined by a coupling, or two reservoirs of charge that bleed into one another. The state at time tt is a vector x(t)=(x1(t),x2(t))x(t) = (x_1(t), x_2(t)), and the rule of motion is

ddt[x1x2]=[a11a12a21a22][x1x2].\frac{d}{dt}\begin{bmatrix}x_1\\x_2\end{bmatrix} = \begin{bmatrix}a_{11} & a_{12}\\a_{21} & a_{22}\end{bmatrix}\begin{bmatrix}x_1\\x_2\end{bmatrix}.

Each component xix_i depends on all the others through the off-diagonal entries of AA. That coupling is what makes the system feel hard. Direct substitution gets you a tangled algebra problem; the method of undetermined coefficients gives you nothing useful.

The eigenvalue method is the change of perspective that breaks the coupling. Instead of asking "what does x1x_1 do?" we ask: are there directions in state space along which the matrix AA acts like a pure stretch? If yes, motion along such a direction stays on that direction forever — it is effectively one-dimensional. And in one dimension we already know the answer: x(t)=λx(t)x(t)=eλtx(0)x'(t) = \lambda x(t) \Rightarrow x(t) = e^{\lambda t} x(0).

The slogan

Find the special directions; ride them. Every solution of a linear constant-coefficient system is a linear combination of pure-exponential rides along eigenvectors.


The Educated Guess: Try x(t)=eλtvx(t) = e^{\lambda t} v

For the scalar equation x=axx' = a x the solution is x(t)=Ceatx(t) = C e^{a t}. So it is reasonable to ask: can the vector system x=Axx' = A x have a solution of the form

x(t)=eλtv,x(t) = e^{\lambda t}\, v,

where λ\lambda is some (yet-unknown) scalar and vv is some constant vector?

Differentiate the guess and plug it in:

x(t)=λeλtvandAx(t)=eλtAv.x'(t) = \lambda\, e^{\lambda t}\, v \quad\text{and}\quad A\, x(t) = e^{\lambda t}\, A v.

For the guess to satisfy x=Axx' = Ax at every tt, the factor eλte^{\lambda t} cancels and we are left with a statement that involves no tt at all:

  Av=λv.  \boxed{\;A v = \lambda\, v.\;}

This is the eigenvalue equation. A pair (λ,v)(\lambda, v) satisfying it is an eigenvalue–eigenvector pair. The miracle is that we traded an equation involving derivatives in time for one piece of linear algebra that has no time in it at all. Time will come back in through the factor eλte^{\lambda t} — but only after the algebra is solved.

Why the cancellation matters

The differential equation says "velocity at time tt equals AA times the state at time tt." If the trajectory stays on the line through  v\;v, then both sides scale together as time passes and the relationship between them is just a number λ\lambda. Removing time from the requirement is exactly what an eigenvector is for.


What an Eigenvector Means, Geometrically

Forget calculus for a moment. Take any 2D vector vv and apply the matrix AA to it. The result AvA v is almost always a vector that points in a different direction than vv. The matrix rotated and stretched it.

An eigenvector of AA is one of the rare directions where the rotation part disappears — AvA v stays parallel to vv, only its length is multiplied. The stretch factor is the eigenvalue λ\lambda:

Av=λvApply A, get the same direction back, stretched by λ.A v = \lambda v\quad\Longleftrightarrow\quad \text{Apply } A,\text{ get the same direction back, stretched by } \lambda.

For a generic 2×22\times 2 matrix there are usually two such directions, and they are the natural "axes" for everything the matrix does.

Loading eigen-direction visualizer…

Play, then read on

Drag the slider above. The orange arrow is AvA v; the cyan arrow is vv. Almost everywhere they disagree by some angle — the panel reports it as (v,Av)\angle(v, Av). When you snap to either eigen-direction the angle drops to 0° (or 180° for a negative eigenvalue): same line, same direction, length scaled by the eigenvalue.


The Characteristic Polynomial

How do we find the eigenvalues without guessing? Rewrite the eigenvalue equation as

(AλI)v=0.(A - \lambda I)\, v = 0.

We need a non-zero vv in the null space of AλIA - \lambda I. A square matrix has a non-trivial null space if and only if its determinant is zero. So λ\lambda must satisfy

  det(AλI)=0.  \boxed{\;\det(A - \lambda I) = 0.\;}

For a 2×22\times 2 matrix this expands to a quadratic — the characteristic polynomial:

det[a11λa12a21a22λ]=λ2(trA)λ+detA=0.\det\begin{bmatrix}a_{11}-\lambda & a_{12}\\a_{21} & a_{22}-\lambda\end{bmatrix} = \lambda^2 - (\operatorname{tr} A)\,\lambda + \det A = 0.

Two quantities decide everything: the trace trA=a11+a22\operatorname{tr} A = a_{11} + a_{22} and the determinant detA=a11a22a12a21\det A = a_{11} a_{22} - a_{12} a_{21}. The eigenvalues are the roots

λ1,2=trA±(trA)24detA2.\lambda_{1,2} = \frac{\operatorname{tr} A \pm \sqrt{(\operatorname{tr} A)^2 - 4 \det A}}{2}.

Three cases, one formula

The discriminant Δ=(trA)24detA\Delta = (\operatorname{tr} A)^2 - 4\det A decides the qualitative story:

  • Δ>0\Delta > 0: two distinct real eigenvalues — pure exponential modes (node or saddle).
  • Δ<0\Delta < 0: complex conjugate pair α±iβ\alpha \pm i \beta — oscillation with envelope eαte^{\alpha t} (spiral or center).
  • Δ=0\Delta = 0: one repeated real eigenvalue — borderline case, calls for a generalised eigenvector.

Finding an eigenvector once you have an eigenvalue

Substitute λ\lambda back into (AλI)v=0(A - \lambda I) v = 0. The two rows of AλIA - \lambda I are guaranteed to be linearly dependent (that is the whole point of the determinant being zero), so you only need one of them. Pick the first row (a11λ)v1+a12v2=0(a_{11}-\lambda)\,v_1 + a_{12}\,v_2 = 0 and solve for the ratio v1/v2v_1/v_2. The eigenvector is defined only up to a scalar, so any convenient scaling works.


Building the General Solution

Once we have two eigenpairs (λ1,v1)(\lambda_1, v_1) and (λ2,v2)(\lambda_2, v_2), each of eλ1tv1e^{\lambda_1 t} v_1 and eλ2tv2e^{\lambda_2 t} v_2 solves x=Axx' = A x. Because the system is linear, any constant-coefficient combination also solves it:

  x(t)=c1eλ1tv1  +  c2eλ2tv2.  \boxed{\;x(t) = c_1\, e^{\lambda_1 t}\, v_1 \;+\; c_2\, e^{\lambda_2 t}\, v_2.\;}

Two free constants are exactly what a 2-D first-order system has room for — one for each component of the initial state. To fix them, impose x(0)=x0x(0) = x_0. Setting t=0t = 0 gives

x0=c1v1+c2v2.x_0 = c_1\, v_1 + c_2\, v_2.

That is a linear equation for (c1,c2)(c_1, c_2). As long as v1,v2v_1, v_2 are independent, it has a unique solution — exactly the projection of x0x_0 onto the eigenbasis.

The big picture in one diagram

x' = A x ← coupled system in original axes │ │ change to eigenbasis (rotate so coordinate axes = v1, v2) ↓ y_1' = λ_1 y_1 ← uncoupled scalar ODEs y_2' = λ_2 y_2 │ │ integrate each one ↓ y_1(t) = c_1 e^{λ_1 t} y_2(t) = c_2 e^{λ_2 t} │ │ rotate back to original axes ↓ x(t) = c_1 e^{λ_1 t} v_1 + c_2 e^{λ_2 t} v_2

Worked Example: A 2×2 Saddle

Solve the initial-value problem

x=[1142]x,x(0)=[21].x' = \begin{bmatrix}1 & 1\\4 & -2\end{bmatrix} x,\quad x(0) = \begin{bmatrix}2\\1\end{bmatrix}.
Click to expand the full hand calculation
Step 1 — Characteristic polynomial.

trA=1+(2)=1\operatorname{tr} A = 1 + (-2) = -1 and detA=(1)(2)(1)(4)=6\det A = (1)(-2) - (1)(4) = -6. So

λ2(trA)λ+detA=λ2+λ6=0.\lambda^2 - (\operatorname{tr} A)\lambda + \det A = \lambda^2 + \lambda - 6 = 0.

Factor: (λ2)(λ+3)=0(\lambda - 2)(\lambda + 3) = 0, so λ1=2,    λ2=3\lambda_1 = 2,\;\; \lambda_2 = -3. Two real eigenvalues of opposite sign — saddle territory.

Step 2 — Eigenvector for λ1=2\lambda_1 = 2.

A2I=[1144]A - 2I = \begin{bmatrix}-1 & 1\\4 & -4\end{bmatrix}. The first row gives v11+v12=0-v_{11} + v_{12} = 0, i.e. v11=v12v_{11} = v_{12}. Take v1=[11]v_1 = \begin{bmatrix}1\\1\end{bmatrix}.

Sanity: Av1=(1+1,  42)=(2,2)=2v1A v_1 = (1+1,\; 4-2) = (2,\,2) = 2\,v_1 ✓.

Step 3 — Eigenvector for λ2=3\lambda_2 = -3.

A+3I=[4141]A + 3I = \begin{bmatrix}4 & 1\\4 & 1\end{bmatrix}. First row: 4v21+v22=04 v_{21} + v_{22} = 0, so v22=4v21v_{22} = -4 v_{21}. Take v2=[14]v_2 = \begin{bmatrix}1\\-4\end{bmatrix}.

Sanity: Av2=(14,  4+8)=(3,12)=3v2A v_2 = (1-4,\; 4+8) = (-3, 12) = -3\,v_2 ✓.

Step 4 — General solution.
x(t)=c1e2t[11]+c2e3t[14].x(t) = c_1 e^{2 t}\begin{bmatrix}1\\1\end{bmatrix} + c_2 e^{-3 t}\begin{bmatrix}1\\-4\end{bmatrix}.
Step 5 — Plug in the initial condition.

x(0)=c1(1,1)+c2(1,4)=(c1+c2,  c14c2)x(0) = c_1 (1,1) + c_2 (1, -4) = (c_1 + c_2,\; c_1 - 4 c_2) must equal (2,1)(2, 1):

c1+c2=2,c14c2=1.c_1 + c_2 = 2,\quad c_1 - 4 c_2 = 1.

Subtract the equations: 5c2=1c2=1/55 c_2 = 1 \Rightarrow c_2 = 1/5, then c1=21/5=9/5c_1 = 2 - 1/5 = 9/5.

Step 6 — Particular solution.
  x(t)=95e2t[11]+15e3t[14].  \boxed{\;x(t) = \tfrac{9}{5}\, e^{2 t}\begin{bmatrix}1\\1\end{bmatrix} + \tfrac{1}{5}\, e^{-3 t}\begin{bmatrix}1\\-4\end{bmatrix}.\;}
Step 7 — Sanity check the initial condition.

At t=0t=0: x(0)=95(1,1)+15(1,4)=(105,55)=(2,1)x(0) = \tfrac{9}{5}(1,1) + \tfrac{1}{5}(1,-4) = (\tfrac{10}{5}, \tfrac{5}{5}) = (2, 1) ✓.

Step 8 — Plug back into the ODE.

Differentiate component-wise: x(t)=295e2t(1,1)+(3)15e3t(1,4)=185e2t(1,1)35e3t(1,4)x'(t) = 2 \cdot \tfrac{9}{5} e^{2 t}(1,1) + (-3) \cdot \tfrac{1}{5} e^{-3 t}(1,-4) = \tfrac{18}{5} e^{2 t}(1,1) - \tfrac{3}{5} e^{-3 t}(1,-4). And Ax(t)A x(t) equals the same thing because Av1=2v1A v_1 = 2 v_1 and Av2=3v2A v_2 = -3 v_2 by construction. ✓

Step 9 — Long-time behaviour.

As tt \to \infty the e2te^{2 t} term blows up while e3t0e^{-3 t} \to 0. So every trajectory eventually lines up with the unstable direction v1=(1,1)v_1 = (1, 1). As tt \to -\infty the opposite happens — trajectories swing toward the stable direction v2=(1,4)v_2 = (1, -4). This is the signature of a saddle: one direction repels, the other attracts.


Interactive Eigenvalue-Method Explorer

Tweak the four entries of AA. The panel recomputes λ1,λ2,v1,v2\lambda_1, \lambda_2, v_1, v_2, decomposes the initial condition x(0)=c1v1+c2v2x(0) = c_1 v_1 + c_2 v_2, prints the symbolic solution x(t)=c1eλ1tv1+c2eλ2tv2x(t) = c_1 e^{\lambda_1 t} v_1 + c_2 e^{\lambda_2 t} v_2, and draws the trajectory in the phase plane together with the two component plots x1(t)x_1(t) and x2(t)x_2(t).

Loading eigenvalue-method explorer…

Three experiments worth doing

  • Saddle. The default settings reproduce the worked example. Drag x(0)x(0) onto the dashed  v2\;v_2 line — the trajectory now collapses straight into the origin along that one direction (the unstable mode has been zeroed out).
  • Stable node. Click the "Stable node" preset. Both eigenvalues are negative. Watch both component plots decay monotonically.
  • Spiral. Click "Stable spiral." The eigenvalues become complex — the symbolic display switches to the real form eαt(A1cosβt+A2sinβt)e^{\alpha t}(A_1 \cos\beta t + A_2 \sin\beta t), the phase trajectory winds in, and the component plots become damped sinusoids.

Plain Python with NumPy

The mechanical pipeline of the eigenvalue method translates into about ten lines of NumPy. Read each card carefully — the same five steps you did by hand appear verbatim in the code.

Eigenvalue method with NumPy — the closed-form solver
🐍eigen_method_numpy.py
1Import NumPy

NumPy is the array engine. np.linalg.eig is a single call that returns eigenvalues and eigenvectors of a square matrix — exactly the two pieces we need to build x(t).

4Encode the system as a matrix

A 2×2 matrix is enough to capture the whole system x' = A x. Row 1 of A holds the coefficients of x1'; row 2 holds the coefficients of x2'. The system is now a single object that NumPy can decompose mechanically.

11One line to extract eigenstructure

np.linalg.eig returns (w, V): w is a length-n array of eigenvalues (possibly complex), and V is an n×n matrix whose i-th column is the eigenvector belonging to w[i]. The eigenvectors are normalised to unit length. For our matrix the output is w = [2, -3] and the columns of V are proportional to (1, 1) and (1, -4).

16Numerical sanity check

Reading eigenvalues out of a library is only useful if you trust it. A v_i should equal lambda_i v_i to machine precision. If this line ever prints a mismatch, you typed the matrix wrong — not NumPy's fault.

23Initial condition as a column vector

The system has two state variables, so we need two numbers to start it: x1(0) and x2(0). Here x(0) = (2, 1).

24Switch to the eigenbasis

We want c1 and c2 such that x(0) = c1 v1 + c2 v2. Rewritten with V (whose columns are the eigenvectors): V c = x(0). np.linalg.solve solves that linear system exactly. The output c = [1.8, 0.2] tells us the initial state contains 1.8 units of the growing mode and 0.2 units of the decaying mode.

28The closed-form solution

This single line is the entire content of the eigenvalue method: every solution of x' = A x is a sum c_i e^{lambda_i t} v_i. There is no derivative to integrate, no guessing — just exponentiation, multiplication, and addition. The work was paid up front in the eigen-decomposition.

32Sample the solution

Pick any t you want and call x_of(t). Because the formula is closed-form (not an integrator), the answer is exact to machine precision at every t — no step-size error accumulates. At t=0 you should recover (2, 1); at t=1 the growing mode dominates and you should see (~13.3, ~13.3) — both components hurtling along v1.

28 lines without explanation
1import numpy as np
2
3# The coefficient matrix from the worked example:
4#   x1' =  1*x1 + 1*x2
5#   x2' =  4*x1 - 2*x2
6A = np.array([[1.0, 1.0],
7              [4.0, -2.0]])
8
9# 1) Eigen-decomposition.  np.linalg.eig returns eigenvalues w
10#    and a matrix V whose COLUMNS are the corresponding eigenvectors.
11w, V = np.linalg.eig(A)
12print("eigenvalues :", w)             # -> [ 2. -3.]
13print("eigenvectors:")
14print(V)                                # columns are v1, v2
15
16# 2) Sanity check:  A v_i  must equal  lambda_i v_i  for each i.
17for i in range(2):
18    lhs = A @ V[:, i]
19    rhs = w[i] * V[:, i]
20    print(f"i={i}: A v = {lhs},  lambda v = {rhs}")
21
22# 3) Initial condition.  Decompose it in the eigenbasis:
23#    x(0) = c1 v1 + c2 v2  <=>  V @ c = x0  <=>  c = V^{-1} x0.
24x0 = np.array([2.0, 1.0])
25c  = np.linalg.solve(V, x0)
26print("c =", c)                        # weight of each eigen-mode
27
28# 4) Closed-form solution at any time t:
29#    x(t) = c1 e^{lambda1 t} v1 + c2 e^{lambda2 t} v2
30def x_of(t):
31    return c[0]*np.exp(w[0]*t)*V[:, 0] + c[1]*np.exp(w[1]*t)*V[:, 1]
32
33# 5) Evaluate on a grid and print a few samples.
34ts = np.linspace(0.0, 1.0, 6)
35for t in ts:
36    print(f"t = {t:.2f}   x(t) = {x_of(t)}")

Running the script prints, in order: the eigenvalues [2,3][2, -3], two normalised eigenvectors (proportional to (1,1)(1,1) and (1,4)(1,-4)), the coefficients c(1.8,0.2)c \approx (1.8, 0.2), and a table of sampled values for x(t)x(t). At t=1t = 1 you should see x(1)(13.31,13.26)x(1) \approx (13.31, 13.26) — both components have started to chase the unstable eigen-direction v1=(1,1)v_1 = (1, 1).

Independent verification: brute-force Euler

Symbolic and library answers can be wrong if you typed the matrix wrong. A short forward-Euler loop computes x(t)x(t) with no eigenstructure anywhere in sight — agreement to two or three decimal places means the eigen-derivation is correct.

Cross-check by forward Euler — no eigenvectors used
🐍euler_check.py
1Imports

Only NumPy this time. We deliberately avoid np.linalg.eig — the goal is to verify the eigen-formula by an independent route.

8Forward-Euler step

Replace dx/dt with the finite difference (x_{n+1} − x_n)/h, plug into x' = A x, and solve for the next state: x_{n+1} = (I + h A) x_n. This is just a matrix multiplication per step.

11Run the loop

1000 steps with h=0.001 cover the interval [0, 1]. Euler is first-order, so the error is O(h) — about 10⁻³. We expect to land within a percent of the analytic answer (13.31, 13.26), which is enough to confirm the eigen-method derivation. A 10⁻¹⁰ check would need RK4 or scipy.integrate.solve_ivp, but the message is the same.

14Print and compare

The numeric output should be very close to the analytic answer. If they agree, the eigen-formula and the brute-force loop are computing the same trajectory — strong evidence that the method works.

15 lines without explanation
1import numpy as np
2
3# Same matrix and initial condition as the analytic computation above.
4A = np.array([[1.0, 1.0],
5              [4.0, -2.0]])
6x = np.array([2.0, 1.0])
7
8# Forward Euler is the simplest integrator: x_{n+1} = x_n + h * A x_n.
9# It is NOT used for production work, but it is short enough to read
10# end-to-end and confirms the analytic solution from a totally different
11# angle (no eigenvectors are computed anywhere in this loop).
12h = 0.001
13T = 1.0
14n_steps = int(T / h)
15for _ in range(n_steps):
16    x = x + h * (A @ x)
17
18print("Euler   x(1.0) =", x)
19print("Analytic x(1.0) = (13.310, 13.260)  (rounded; see the closed form)")

The Same Idea in PyTorch

Why bother with PyTorch when NumPy works? Three reasons. First, the whole pipeline above runs on a GPU with a one-line device change. Second, every intermediate tensor flows through autograd, so you can back-propagate through the eigen-decomposition — useful if AA itself is being learned by a neural network. Third, broadcasting lets you evaluate x(t)x(t) at thousands of times in a single vectorised call.

Eigenvalue method with PyTorch — batched + differentiable
🐍eigen_method_pytorch.py
1Import torch

PyTorch gives us GPU acceleration, automatic differentiation, and broadcasting. The eigenvalue method becomes a tiny, batched, differentiable building block.

5Matrix on whatever device you want

torch.tensor builds a CPU tensor by default. Append .cuda() or .to('mps') for GPU. Everything downstream — eig, solve, exponentiation — runs on the same device.

11torch.linalg.eig (complex by default)

Unlike NumPy, PyTorch returns complex tensors even when the eigenvalues are real, because PyTorch cannot know in advance whether the matrix is symmetric. The structure of the output is identical to NumPy: w is the eigenvalue tensor, V is the matrix of eigenvectors stacked as columns.

17Drop the imaginary parts when safe

For our matrix the eigenvalues happen to be real, so we strip the imaginary parts. In the complex-eigenvalues case (a spiral) you keep them and the same formula automatically produces sin/cos via Euler's identity.

21Solve V c = x0

Exactly the same idea as before: project x(0) onto the eigenbasis. torch.linalg.solve does the equivalent of NumPy's np.linalg.solve, except the result is a differentiable tensor — useful if you want to back-prop through the initial condition later.

25A batched closed-form solution

We design x_of so it accepts either a single time or a batch of times. t.unsqueeze(-1) adds a trailing axis so that broadcasting against the eigenvalue vector w_r produces a (..., n) tensor of mode amplitudes. Multiplying by the eigenvector matrix V_r.T turns those amplitudes back into state vectors.

32Vectorised evaluation

Passing torch.linspace gives us six time points at once. The forward pass is O(n²) per time step regardless of batch — perfect for putting inside a differentiable simulation. If you wrap A in nn.Parameter and train through this code, you have a tiny linear neural ODE that learns its own dynamics.

28 lines without explanation
1import torch
2
3# Same system as above, but with a PyTorch tensor so the result lives on
4# whichever device we like (CPU/GPU) and can pass through autograd.
5A = torch.tensor([[1.0, 1.0],
6                  [4.0, -2.0]])
7
8# torch.linalg.eig works for general (not necessarily symmetric) matrices.
9# It returns complex tensors because in general eigenvalues are complex.
10w, V = torch.linalg.eig(A)
11print("eigenvalues :", w)              # tensor([ 2.+0.j, -3.+0.j])
12print("eigenvectors:")
13print(V)
14
15# Take real parts since for this matrix both eigenvalues happen to be real.
16w_r = w.real
17V_r = V.real
18
19x0 = torch.tensor([2.0, 1.0])
20c  = torch.linalg.solve(V_r, x0)
21print("c =", c)                         # tensor([1.8000, 0.2000])
22
23def x_of(t):
24    # Broadcasting: t can be a scalar OR a tensor of times.
25    t = torch.as_tensor(t, dtype=torch.float32)
26    # Each column of V_r times its mode amplitude e^{lambda t} c_i.
27    modes = (c * torch.exp(w_r * t.unsqueeze(-1)))  # shape (..., 2)
28    return modes @ V_r.T                              # shape (..., 2)
29
30# Vectorised evaluation over many t at once — the calling pattern you would
31# use inside a neural-ODE layer or a differentiable simulation.
32ts = torch.linspace(0.0, 1.0, 6)
33print("ts:", ts)
34print("xs:")
35print(x_of(ts))

From script to neural ODE

Wrap AA in nn.Parameter\texttt{nn.Parameter}, feed observed trajectories as training data, and back-propagate the squared error of x_of(t)x\_of(t) against measured points. You have just built the simplest possible linear neural ODE: the network's job is to discover the matrix whose eigenvalues and eigenvectors reproduce the data. The eigenvalue method is not a relic — it is the smallest differentiable simulation you can write.


Three Cases of Eigenvalues

The discriminant of the characteristic polynomial splits the possibilities into three cases, each with a distinct picture.

Discriminant ΔEigenvaluesSolution formGeometry
Δ > 0λ₁, λ₂ real, distinctc₁ e^(λ₁ t) v₁ + c₂ e^(λ₂ t) v₂Node (same sign) or saddle (opposite sign)
Δ < 0α ± iβ (complex conjugates)e^(α t) ( A₁ cos βt + A₂ sin βt )Spiral if α≠0, center if α=0
Δ = 0λ repeated, reale^(λ t) ( v + t (A − λ I) v )Degenerate node

Sections 23.04, 23.05, and 23.06 dig into each case with a separate worked example. The eigenvalue method itself does not change — only how you interpret the algebra changes.

Why the three pictures all come from one formula

Complex exponentials are just sums of sines and cosines via Euler. Repeated roots are the borderline where the two solutions eλ1tv1e^{\lambda_1 t} v_1 and eλ2tv2e^{\lambda_2 t} v_2 collapse onto each other, requiring a second linearly-independent solution that you can read off by differentiating with respect to λ\lambda. Both moves come straight out of the same scalar formula x=eλtx = e^{\lambda t} — only the eigenvalue changes character.


Common Pitfalls

Forgetting to scale the eigenvector consistently

Eigenvectors are only defined up to a scalar. If you change v1v_1 from (1,1)(1, 1) to (2,2)(2, 2), the constant c1c_1 will change by a factor of 1/21/2 — that is fine. What is not fine is mixing scalings between the' general-solution' expression and the' initial-condition' step. Pick a scaling and use it everywhere.

Using det(A − λI) = 0 with a sign error

Always write the diagonal entries as aiiλa_{ii} - \lambda, not λaii\lambda - a_{ii}. A sign mistake here flips the trace and produces eigenvalues with the wrong sign — your "stable spiral" becomes an unstable spiral.

Stopping at eigenvalues — forgetting eigenvectors

The eigenvalues by themselves tell you the qualitative picture (stable, unstable, oscillatory). To write down a specific solution you must also find v1v_1 and v2v_2. A common mistake on exams is writing x(t)=c1eλ1t+c2eλ2tx(t) = c_1 e^{\lambda_1 t} + c_2 e^{\lambda_2 t} (scalars), which is dimensionally wrong — the answer must be a vector.

Complex eigenvalues but real solutions

If the matrix has real entries, complex eigenvalues come in conjugate pairs and so do their eigenvectors. Take the real and imaginary parts of one complex solution to get two real, linearly independent solutions eαtcos(βt)peαtsin(βt)qe^{\alpha t} \cos(\beta t) p - e^{\alpha t}\sin(\beta t) q and eαtsin(βt)p+eαtcos(βt)qe^{\alpha t} \sin(\beta t) p + e^{\alpha t}\cos(\beta t) q. Do not try to fit the initial condition with complex constants — you will produce algebra noise that almost cancels.


Summary

The eigenvalue method converts a coupled linear ODE system into pure algebra. The whole pipeline is six bullet points long:

  1. Guess x(t)=eλtvx(t) = e^{\lambda t} v.
  2. Substitute into x=Axx' = A x. The factor eλte^{\lambda t} cancels and you are left with Av=λvA v = \lambda v.
  3. Solve det(AλI)=0\det(A - \lambda I) = 0 for the eigenvalues. For 2×22 \times 2: roots of λ2(trA)λ+detA=0\lambda^2 - (\operatorname{tr} A)\lambda + \det A = 0.
  4. Find an eigenvector viv_i for each λi\lambda_i from the null space of AλiIA - \lambda_i I.
  5. Assemble the general solution x(t)=icieλitvix(t) = \sum_i c_i e^{\lambda_i t} v_i.
  6. Fit the constants by solving icivi=x(0)\sum_i c_i v_i = x(0).
The slogan
"Diagonalise the matrix, exponentiate the eigenvalues, sum the modes. Every linear constant-coefficient system answers to this recipe."
Coming up next: Section 23.03 turns the algebra into pictures — the phase portrait. We will see how the sign and type of the eigenvalues paint the entire global geometry of trajectories: where they come from, where they go, and how to read stability off a single sketch.
Loading comments...