Volume 2: Continuous Mathematics · Part VII: Linear Algebra

Chapter 28. Eigenvectors, Bases, and Applications

A matrix moves almost every vector off its original line: it turns it as well as stretching it. But most matrices have a few special directions that they only stretch. Along those directions, a complicated transformation acts like plain multiplication by a number. Those directions are eigenvectors, and the numbers are eigenvalues. Find them, and hard questions become easy: what happens if you apply a matrix a thousand times? Where does a random process settle down? Which web page matters most? Which direction in a cloud of data carries the most information? This chapter builds eigenvectors from that one geometric question, computes them by hand for 2×2 matrices, and then follows them into Fibonacci numbers, Markov chains, PageRank, principal component analysis, and the singular value decomposition that compresses images and finds hidden topics in text.

Where this chapter sits

You need: matrices as transformations, matrix multiplication, determinants and inverses (Chapter 27); vectors, dot products and projections (Chapter 26); solving quadratic equations (Chapter 8); and for two applications, recurrences (Chapter 14) and directed graphs (Chapter 25). Complex numbers (Chapter 15) appear briefly. No calculus.

This chapter unlocks: the statistics of correlated data and regression (Chapter 31), stability of numerical methods (Chapter 32), the second-derivative test in several variables (Chapter 20), and the machine-learning and information-retrieval material in Chapter 33.

Why this chapter matters. Many real questions are about what happens when the same step is repeated: next year’s population from this year’s, tomorrow’s weather from today’s, the next vibration of a bridge from the last, the next click of a web surfer. Eigenvectors answer "where does this end up, and how fast?" without simulating every step. The same idea, aimed at a cloud of data instead of a process, finds its most informative directions, which is how faces, genomes and documents get compressed into a few meaningful numbers.

28.1. Directions that only stretch

28.1.1. The problem

Take the matrix

\[A = \begin{bmatrix} 4 & 1 \\ 2 & 3 \end{bmatrix}.\]

Apply it to \((1, 0)\) and you get \((4, 2)\): the vector has been stretched and turned. Apply it to \((0, 1)\): \((1, 3)\), also turned. But apply it to \((1, 1)\):

\[\begin{bmatrix} 4 & 1 \\ 2 & 3 \end{bmatrix}\begin{bmatrix} 1 \\ 1 \end{bmatrix} = \begin{bmatrix} 5 \\ 5 \end{bmatrix} = 5\begin{bmatrix} 1 \\ 1 \end{bmatrix}.\]

No turning at all. Along the line through \((1, 1)\), this matrix does nothing except multiply by 5. Try \((1, -2)\): \(A(1, -2) = (4 - 2, 2 - 6) = (2, -4) = 2(1, -2)\). Another unturned direction, stretched by 2.

Now here’s why this matters. Suppose you want \(A^{100}\mathbf{x}\), the result of applying \(A\) a hundred times. Multiplying out 100 matrices is tedious. But if \(\mathbf{x} = (1, 1)\), the answer is instant: each application multiplies by 5, so \(A^{100}(1, 1) = 5^{100}(1, 1)\). And since \((1, 1)\) and \((1, -2)\) point in different directions, every vector is a combination of them, and we can handle every vector this way. A complicated matrix has become two independent multiplications by numbers.

The questions for this chapter: how do you find such directions systematically, what do they mean, and where does this trick pay off?

28.1.2. Intuition: watch which arrows stay on their lines

Fan of unit vectors before and after applying A, with the two eigenvector directions highlighted and staying on their dashed lines
Figure 249. Unit vectors in every direction (left) and their images under \(A\) (right). Almost every arrow is turned. Only the two eigen-directions, through \((1, 1)\) (blue) and \((1, -2)\) (orange), stay on their own lines: they are stretched by 5 and 2.

Picture the transformation acting on the whole plane. Most directions get dragged around. But a few lines through the origin are mapped onto themselves: vectors on those lines stay on them, just longer, shorter, or flipped. Those are the transformation’s natural axes, its "grain". A shear, a stretch, a projection: each has its own.

Eigenvector and eigenvalue

A nonzero vector \(\mathbf{v}\) is an eigenvector of a square matrix \(A\) if

\[A\mathbf{v} = \lambda\mathbf{v}\]

for some scalar \(\lambda\) (Greek "lambda"), called the eigenvalue belonging to \(\mathbf{v}\). In words: \(A\) maps \(\mathbf{v}\) to a multiple of itself. \(\lambda > 1\) stretches, \(0 < \lambda < 1\) shrinks, \(\lambda < 0\) flips (and scales by \(|\lambda|\)), \(\lambda = 0\) crushes \(\mathbf{v}\) to zero. Any nonzero multiple of an eigenvector is also an eigenvector with the same eigenvalue, so really an eigenvector names a direction (a line through the origin). The zero vector is never counted as an eigenvector, since \(A\mathbf{0} = \lambda\mathbf{0}\) for every \(\lambda\) and tells you nothing.

Why "eigen"?

Eigen is German for "own" or "characteristic", as in mein eigenes Auto ("my own car"). An eigenvector is the matrix’s own vector, a direction that belongs to it. David Hilbert popularised the term around 1904 in his work on integral equations. English briefly tried "proper value" and "characteristic value" (the latter survives in "characteristic equation"), but the half-German hybrid won. Physicists use "eigenstate", "eigenfrequency" and "eigenmode"; quantum mechanics is, in a sense, applied eigenvector theory.

You can often spot eigenvectors from the geometry alone.

  • Scaling \(\begin{bmatrix} 3 & 0 \\ 0 & 2 \end{bmatrix}\): the axes. \(\mathbf{e}_1\) with \(\lambda = 3\), \(\mathbf{e}_2\) with \(\lambda = 2\). A diagonal matrix’s eigenvectors are the coordinate axes and its eigenvalues are the diagonal entries.

  • Reflection in the line \(y = x\): vectors on that line are unchanged (\(\lambda = 1\)); vectors perpendicular to it, along \((1, -1)\), are flipped (\(\lambda = -1\)).

  • Projection onto the x-axis: \(\mathbf{e}_1\) is kept (\(\lambda = 1\)); \(\mathbf{e}_2\) is crushed (\(\lambda = 0\)). An eigenvalue of 0 means the matrix is singular.

  • Rotation by \(90^\circ\): every vector is turned, so there are no real eigenvectors at all. (The eigenvalues turn out to be the complex numbers \(\pm i\); see the failure modes below.)

  • Shear \(\begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix}\): the x-axis slides along itself unchanged (\(\lambda = 1\)), and nothing else stays on its line. Only one eigen-direction.

28.1.4. Where you’d use this

  • Everyday: why things shake at particular speeds. A washing machine judders at one spin speed and runs smoothly above it; a car’s mirror buzzes at one engine speed; a wine glass rings at one note. Each structure has natural modes of vibration, which are the eigenvectors of its stiffness-and-mass equations, and the eigenvalues give the natural frequencies. Push at one of those frequencies and the motion builds up (resonance).

  • Science and engineering: London’s Millennium Bridge swayed alarmingly on its opening day in 2000 because the footfall of crowds matched a sideways natural mode near 1 Hz; it was closed for nearly two years while dampers were fitted. Structural engineers compute the lowest eigenvalues of a building model to keep its natural frequencies away from earthquake and wind frequencies. In quantum mechanics the allowed energies of an atom are eigenvalues, and the spectral lines astronomers use to identify elements in distant stars are differences between them. A tumbling object (throw a phone or a tennis racket in the air) spins steadily only about the eigenvectors of its inertia matrix, its principal axes.

  • Computing: stability analysis. A numerical simulation, a control system or a recurrent neural network repeatedly multiplies by some matrix; if any eigenvalue has size greater than 1, errors grow without limit. Checking the largest eigenvalue is how engineers tell whether an autopilot, a physics time-step or a training run will settle or blow up (Chapter 32).

28.2. Finding eigenvalues: the characteristic equation

28.2.1. Why it works

We want nonzero \(\mathbf{v}\) with \(A\mathbf{v} = \lambda\mathbf{v}\). Rewrite that with everything on one side. Since \(\lambda\mathbf{v} = \lambda I\mathbf{v}\),

\[\begin{aligned} A\mathbf{v} - \lambda I\mathbf{v} &= \mathbf{0} \\ (A - \lambda I)\,\mathbf{v} &= \mathbf{0}. &&\quad\text{(factor out } \mathbf{v}\text{)} \end{aligned}\]

Read it as a transformation: the matrix \(A - \lambda I\) sends the nonzero vector \(\mathbf{v}\) to zero. An invertible matrix can’t do that (it only sends \(\mathbf{0}\) to \(\mathbf{0}\); see Chapter 27). So \(A - \lambda I\) must be singular, which means its determinant is zero:

\[\boxed{\ \det(A - \lambda I) = 0\ }\]

This is the characteristic equation. It contains only \(\lambda\), not \(\mathbf{v}\), so you can solve it for the eigenvalues first and find the eigenvectors afterwards. For a \(2\times 2\) matrix \(\begin{bmatrix} a & b \\ c & d \end{bmatrix}\),

\[\begin{aligned} \det\begin{bmatrix} a - \lambda & b \\ c & d - \lambda \end{bmatrix} &= (a - \lambda)(d - \lambda) - bc \\ &= \lambda^2 - (a + d)\lambda + (ad - bc) = 0. \end{aligned}\]

The coefficients are two old friends: \(a + d\) is the trace (sum of the diagonal) and \(ad - bc\) is the determinant. So for any \(2\times 2\) matrix,

\[\lambda^2 - (\operatorname{tr}A)\,\lambda + \det A = 0,\]

a quadratic, solved with the quadratic formula (Chapter 8). By the sum and product of roots, \(\lambda_1 + \lambda_2 = \operatorname{tr}A\) and \(\lambda_1\lambda_2 = \det A\), two handy checks. The second also makes geometric sense: if \(A\) stretches one direction by \(\lambda_1\) and another by \(\lambda_2\), areas scale by \(\lambda_1\lambda_2\).

Characteristic polynomial, trace
  • The characteristic polynomial of an \(n\times n\) matrix is \(\det(A - \lambda I)\), a polynomial of degree \(n\) in \(\lambda\). Its roots are the eigenvalues; setting it to zero gives the characteristic equation.

  • The trace \(\operatorname{tr}A\) is the sum of the diagonal entries. It equals the sum of the eigenvalues; the determinant equals their product.

  • The eigenspace of \(\lambda\) is the set of all solutions of \((A - \lambda I)\mathbf{v} = \mathbf{0}\): all eigenvectors for \(\lambda\) together with \(\mathbf{0}\).

  • The set of all eigenvalues is the spectrum of \(A\).

Example 151. Procedure — eigenvalues and eigenvectors of a 2×2 matrix

Input: \(A = \begin{bmatrix} a & b \\ c & d \end{bmatrix}\).

Steps:

  1. Form the characteristic equation \(\lambda^2 - (a + d)\lambda + (ad - bc) = 0\).

  2. Solve it for \(\lambda_1, \lambda_2\) (factor, or use the quadratic formula). Check: \(\lambda_1 + \lambda_2 = a + d\) and \(\lambda_1\lambda_2 = ad - bc\).

  3. For each eigenvalue \(\lambda\), write down \(A - \lambda I\). Its two rows are multiples of each other (it’s singular), so they give a single equation in \((x, y)\).

  4. Solve that one equation for a nonzero vector. Shortcut: for a row \((p, q)\) that isn’t all zero, \((-q, p)\) (or \((q, -p)\)) solves \(px + qy = 0\).

  5. Check \(A\mathbf{v} = \lambda\mathbf{v}\).

Output: eigenvalues \(\lambda_1, \lambda_2\) and eigenvectors \(\mathbf{v}_1, \mathbf{v}_2\) (each determined up to a nonzero multiple).

28.2.2. Worked example: the opening matrix

Find the eigenvalues and eigenvectors of \(A = \begin{bmatrix} 4 & 1 \\ 2 & 3 \end{bmatrix}\). Try it: Watch the eigenvectors stay put; Step through this example.

Step 1: characteristic equation. Trace \(= 4 + 3 = 7\), determinant \(= 4\cdot 3 - 1\cdot 2 = 10\).

\[\begin{aligned} \det\begin{bmatrix} 4 - \lambda & 1 \\ 2 & 3 - \lambda \end{bmatrix} &= (4 - \lambda)(3 - \lambda) - 1\cdot 2 \\ &= 12 - 7\lambda + \lambda^2 - 2 &&\quad\text{(expand)} \\ &= \lambda^2 - 7\lambda + 10 = 0. \end{aligned}\]

Step 2: solve. We need two numbers with product 10 and sum 7: 5 and 2. So \((\lambda - 5)(\lambda - 2) = 0\), giving \(\lambda_1 = 5\) and \(\lambda_2 = 2\). Checks: \(5 + 2 = 7\) ✓, \(5 \times 2 = 10\) ✓.

Step 3–4: eigenvector for \(\lambda_1 = 5\).

\[A - 5I = \begin{bmatrix} -1 & 1 \\ 2 & -2 \end{bmatrix}.\]

The second row is \(-2\) times the first, as promised. The first row says \(-x + y = 0\), i.e. \(y = x\). Take \(\mathbf{v}_1 = (1, 1)\).

Eigenvector for \(\lambda_2 = 2\).

\[A - 2I = \begin{bmatrix} 2 & 1 \\ 2 & 1 \end{bmatrix}.\]

Both rows say \(2x + y = 0\), i.e. \(y = -2x\). Take \(\mathbf{v}_2 = (1, -2)\).

Step 5: check. \(A(1, 1) = (5, 5) = 5(1, 1)\) ✓ and \(A(1, -2) = (2, -4) = 2(1, -2)\) ✓. These are exactly the directions highlighted in the diagram.

28.2.3. Alternative approaches, and choosing between them

  • By hand, 2×2: the characteristic equation, as above. It’s quick and shows the structure.

  • By hand, 3×3: the characteristic polynomial is a cubic, which is unpleasant unless you can spot a root (try small integers, and remember the trace and determinant checks). Exam questions are designed so you can; real matrices aren’t.

  • By computer, any size: never through the characteristic polynomial. Finding polynomial roots is numerically ill-conditioned: tiny rounding errors in the coefficients move the roots a lot. Libraries use the QR algorithm, an iterative process of repeated orthogonal transformations, which is stable and \(O(n^3)\). In NumPy: np.linalg.eig for general matrices, np.linalg.eigh for symmetric ones (faster, more accurate, and returns real, sorted, orthonormal results).

  • Only the largest eigenvector of a huge sparse matrix: power iteration (Section 28.7), which needs nothing but matrix–vector products.

When eigenvectors go missing
  • Complex eigenvalues. For the 90° rotation \(\begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix}\), trace 0 and determinant 1 give \(\lambda^2 + 1 = 0\), so \(\lambda = \pm i\) (Chapter 15). No real direction is left unturned. Complex eigenvalues always signal rotation: their angle in the complex plane is the rotation angle, their size the scaling. In dynamical systems they mean oscillation.

  • Too few eigenvectors. The shear \(\begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix}\) has characteristic equation \((1 - \lambda)^2 = 0\): a repeated eigenvalue 1. But \(A - I = \begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix}\) gives only \(y = 0\): a single eigen-direction. Such a matrix is called defective, and it cannot be diagonalised (next section).

  • Repeated eigenvalues aren’t always a problem. \(2I\) has eigenvalue 2 twice and every vector is an eigenvector.

  • NumPy normalises and may flip signs. np.linalg.eig returns unit-length eigenvectors as columns, and the sign is arbitrary: \((-0.707, -0.707)\) is just as correct as \((0.707, 0.707)\). Never compare eigenvectors from two runs or two libraries without allowing for sign.

Experiment: Eigenvectors using Python

Verify the worked example, then probe the failure modes.

import numpy as np

A = np.array([[4.0, 1.0],
              [2.0, 3.0]])
vals, vecs = np.linalg.eig(A)
print(vals)
print(vecs)                     # eigenvectors are the COLUMNS, scaled to length 1

v = vecs[:, 0]
print(A @ v, vals[0] * v)       # A v equals lambda v
[5. 2.]
[[ 0.70710678 -0.4472136 ]
 [ 0.70710678  0.89442719]]
[3.53553391 3.53553391] [3.53553391 3.53553391]

The columns are \((1, 1)/\sqrt{2}\) and \((-1, 2)/\sqrt{5}\), which is \(-(1, -2)/\sqrt{5}\): the same direction as our hand answer, normalised and flipped. Now try these and explain each result geometrically:

  1. np.linalg.eig(np.array([[0.0, -1.0], [1.0, 0.0]])) (a rotation: expect complex output).

  2. np.linalg.eig(np.array([[1.0, 1.0], [0.0, 1.0]])) (a shear: look at the two eigenvector columns; are they really different?).

  3. A random symmetric matrix M = R + R.T for a random R: compute vecs.T @ vecs and see what you get (preview of Section 28.9).

  4. Pick any \(2\times 2\) matrix and any starting vector, apply the matrix 30 times (normalising each time), and compare the result with the eigenvector of the largest eigenvalue.

US curriculum

Eigenvalues and eigenvectors are not part of the standard US high-school curriculum. They’re a core topic of a first college Linear Algebra course (typically first or second year for STEM majors) and reappear in Differential Equations. Some advanced high schools offer post-AP linear algebra courses that include them.

28.3. Exercises: finding eigenvectors

  1. (Recognition) Without calculating, give the eigenvalues and eigenvectors of \(\begin{bmatrix} -1 & 0 \\ 0 & 7 \end{bmatrix}\) and of the reflection in the x-axis. Try it: Check exercise [1 2; 3 2); Check exercise [2 1; 1 2).

  2. (Application) Find the eigenvalues and eigenvectors of \(\begin{bmatrix} 2 & 1 \\ 1 & 2 \end{bmatrix}\).

  3. (Application) Find the eigenvalues of \(\begin{bmatrix} 1 & 2 \\ 3 & 2 \end{bmatrix}\) and one eigenvector for each.

  4. (Problem solving) A \(2\times 2\) matrix has trace 6 and determinant 9. What can you say about its eigenvalues? Give two different matrices with these properties, one with a single eigen-direction and one where every vector is an eigenvector.

Answers
  1. Diagonal: eigenvalues \(-1\) (along \(\mathbf{e}_1\)) and 7 (along \(\mathbf{e}_2\)). Reflection in the x-axis: \(\mathbf{e}_1\) with \(\lambda = 1\), \(\mathbf{e}_2\) with \(\lambda = -1\).

  2. Trace 4, determinant 3: \(\lambda^2 - 4\lambda + 3 = (\lambda - 3)(\lambda - 1) = 0\), so \(\lambda = 3, 1\). For 3: \(A - 3I = \begin{bmatrix} -1 & 1 \\ 1 & -1 \end{bmatrix}\), \(y = x\), \(\mathbf{v} = (1, 1)\). For 1: \(A - I = \begin{bmatrix} 1 & 1 \\ 1 & 1 \end{bmatrix}\), \(y = -x\), \(\mathbf{v} = (1, -1)\). (Note: perpendicular.)

  3. Trace 3, determinant \(2 - 6 = -4\): \(\lambda^2 - 3\lambda - 4 = (\lambda - 4)(\lambda + 1) = 0\). For 4: \(A - 4I = \begin{bmatrix} -3 & 2 \\ 3 & -2 \end{bmatrix}\), \(-3x + 2y = 0\), \(\mathbf{v} = (2, 3)\). Check: \(A(2, 3) = (2 + 6, 6 + 6) = (8, 12) = 4(2, 3)\) ✓. For \(-1\): \(A + I = \begin{bmatrix} 2 & 2 \\ 3 & 3 \end{bmatrix}\), \(x + y = 0\), \(\mathbf{v} = (1, -1)\). Check: \(A(1, -1) = (-1, 1)\) ✓.

  4. \(\lambda^2 - 6\lambda + 9 = (\lambda - 3)^2\): a repeated eigenvalue 3. \(3I\) has every vector as an eigenvector. \(\begin{bmatrix} 3 & 1 \\ 0 & 3 \end{bmatrix}\) has trace 6, determinant 9, and only the eigen-direction \(\mathbf{e}_1\) (since \(A - 3I = \begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix}\) forces \(y = 0\)).

28.4. Change of basis and diagonalisation

28.4.1. The problem

We found that \(A = \begin{bmatrix} 4 & 1 \\ 2 & 3 \end{bmatrix}\) simply multiplies by 5 along \((1, 1)\) and by 2 along \((1, -2)\). That suggests a strategy: describe vectors in terms of those two directions instead of \(\mathbf{e}_1, \mathbf{e}_2\). In those coordinates, \(A\) should look like the diagonal matrix \(\begin{bmatrix} 5 & 0 \\ 0 & 2 \end{bmatrix}\). How do we switch coordinates, and what does it buy us?

28.4.2. Change of basis

Any two independent vectors form a basis of the plane (Chapter 26). Put the new basis vectors as the columns of a matrix \(P\):

\[P = \begin{bmatrix} 1 & 1 \\ 1 & -2 \end{bmatrix}.\]

If a vector has coordinates \(\mathbf{c} = (c_1, c_2)\) in the new basis, meaning it equals \(c_1(1, 1) + c_2(1, -2)\), then in standard coordinates it’s \(\mathbf{x} = P\mathbf{c}\) (that’s the column view of matrix–vector multiplication). Going the other way, \(\mathbf{c} = P^{-1}\mathbf{x}\). So:

  • \(P\) translates from new coordinates to standard coordinates.

  • \(P^{-1}\) translates from standard coordinates to new coordinates.

Using the \(2\times 2\) inverse formula, \(\det P = -2 - 1 = -3\) and

\[P^{-1} = \frac{1}{-3}\begin{bmatrix} -2 & -1 \\ -1 & 1 \end{bmatrix} = \begin{bmatrix} 2/3 & 1/3 \\ 1/3 & -1/3 \end{bmatrix}.\]

Example. The vector \(\mathbf{x} = (3, 0)\) has new coordinates \(P^{-1}(3, 0) = (2, 1)\). Check: \(2(1, 1) + 1(1, -2) = (3, 0)\) ✓.

28.4.3. Diagonalisation: three moves

Now compute \(A\mathbf{x}\) by a detour through eigen-coordinates:

  1. Translate into eigen-coordinates: \(\mathbf{c} = P^{-1}\mathbf{x}\).

  2. In those coordinates \(A\) just scales each coordinate by its eigenvalue: multiply by \(D = \begin{bmatrix} 5 & 0 \\ 0 & 2 \end{bmatrix}\).

  3. Translate back: multiply by \(P\).

So \(A\mathbf{x} = PDP^{-1}\mathbf{x}\) for every \(\mathbf{x}\), which means

\[\boxed{\ A = PDP^{-1}\ }\]

where the columns of \(P\) are eigenvectors and \(D\) is diagonal with the matching eigenvalues. Reading right to left: change to the matrix’s own coordinates, stretch along the axes, change back. A complicated-looking transformation is a diagonal one viewed in a tilted coordinate system.

Check with \(\mathbf{x} = (3, 0)\). Coordinates \((2, 1)\); scale to \((10, 2)\); back to standard: \(10(1, 1) + 2(1, -2) = (12, 6)\). Directly: \(A(3, 0) = (12, 6)\) ✓.

Diagonalisable, similar, change of basis
  • A square matrix is diagonalisable if \(A = PDP^{-1}\) with \(D\) diagonal. This works exactly when \(A\) has \(n\) linearly independent eigenvectors (the columns of \(P\)); the eigenvalues fill the diagonal of \(D\) in the same order.

  • A change-of-basis matrix \(P\) has the new basis vectors as columns; \(P\mathbf{c}\) converts new coordinates to standard ones, \(P^{-1}\mathbf{x}\) converts back.

  • Matrices \(A\) and \(B = P^{-1}AP\) are similar: the same transformation described in different coordinates. Similar matrices share eigenvalues, trace and determinant.

  • \(n\) distinct eigenvalues guarantee diagonalisability. Repeated eigenvalues may or may not allow it (\(2I\) yes, the shear no).

28.4.4. Why powers become easy

Here’s the payoff. Apply \(A\) twice:

\[A^2 = (PDP^{-1})(PDP^{-1}) = PD(P^{-1}P)DP^{-1} = PD\,I\,DP^{-1} = PD^2P^{-1}.\]

The inner \(P^{-1}P\) cancels: there’s no point changing back to standard coordinates between applications. The same happens for any power:

\[A^n = PD^nP^{-1}, \qquad D^n = \begin{bmatrix} 5^n & 0 \\ 0 & 2^n \end{bmatrix}.\]

Powers of a diagonal matrix are trivial: raise each diagonal entry to the power. So the cost of \(A^n\) no longer depends on \(n\) (beyond computing two numerical powers).

Example 152. Procedure — computing \(A^n\) by diagonalisation

Input: a diagonalisable \(n\times n\) matrix \(A\) and a power \(k\).

Steps:

  1. Find the eigenvalues \(\lambda_1, \ldots, \lambda_n\) and eigenvectors \(\mathbf{v}_1, \ldots, \mathbf{v}_n\).

  2. Form \(P = [\mathbf{v}_1 \mid \cdots \mid \mathbf{v}_n]\) and \(D = \operatorname{diag}(\lambda_1, \ldots, \lambda_n)\). Compute \(P^{-1}\).

  3. Form \(D^k = \operatorname{diag}(\lambda_1^k, \ldots, \lambda_n^k)\).

  4. Multiply \(A^k = PD^kP^{-1}\).

Output: \(A^k\), often as a closed-form formula in \(k\). If you only need \(A^k\mathbf{x}\): write \(\mathbf{x} = \sum c_i\mathbf{v}_i\) (i.e. \(\mathbf{c} = P^{-1}\mathbf{x}\)), and then \(A^k\mathbf{x} = \sum c_i\lambda_i^k\mathbf{v}_i\).

28.4.5. Worked example: a formula for \(A^n\)

\[\begin{aligned} PD^n &= \begin{bmatrix} 1 & 1 \\ 1 & -2 \end{bmatrix}\begin{bmatrix} 5^n & 0 \\ 0 & 2^n \end{bmatrix} = \begin{bmatrix} 5^n & 2^n \\ 5^n & -2\cdot 2^n \end{bmatrix} &&\quad\text{(scale the columns)} \\[4pt] A^n = PD^nP^{-1} &= \begin{bmatrix} 5^n & 2^n \\ 5^n & -2\cdot 2^n \end{bmatrix}\begin{bmatrix} 2/3 & 1/3 \\ 1/3 & -1/3 \end{bmatrix} \\[4pt] &= \frac{1}{3}\begin{bmatrix} 2\cdot 5^n + 2^n & 5^n - 2^n \\ 2\cdot 5^n - 2\cdot 2^n & 5^n + 2\cdot 2^n \end{bmatrix}. \end{aligned}\]

Check at \(n = 1\): \(\tfrac{1}{3}(12, 3; 6, 9) = (4, 1; 2, 3) = A\) ✓. At \(n = 3\): top-left \((250 + 8)/3 = 86\), top-right \((125 - 8)/3 = 39\), bottom-left \((250 - 16)/3 = 78\), bottom-right \((125 + 16)/3 = 47\). Multiplying out \(A \cdot A \cdot A\) directly gives \(\begin{bmatrix} 86 & 39 \\ 78 & 47 \end{bmatrix}\) ✓.

The formula also tells you the long-run behaviour at a glance. For large \(n\), \(5^n\) dwarfs \(2^n\), so \(A^n \approx \tfrac{5^n}{3}\begin{bmatrix} 2 & 1 \\ 2 & 1 \end{bmatrix}\): every vector gets pulled towards the \((1, 1)\) direction and grows like \(5^n\). The largest eigenvalue governs the long run. That single observation drives the next three sections.

28.4.6. Where you’d use this: a population model

Ecologists track animal populations by age class with a Leslie matrix. Suppose each adult produces on average 3 juveniles a year, half the juveniles survive to become adults, and half the adults survive each year. With \(\mathbf{x}_n = (j_n, a_n)\) (juveniles, adults) in year \(n\), Try it: Run the Leslie model; Leslie matrix eigenvalues.

\[\mathbf{x}_{n+1} = \underbrace{\begin{bmatrix} 0 & 3 \\ 0.5 & 0.5 \end{bmatrix}}_{L}\mathbf{x}_n, \qquad \lambda^2 - 0.5\lambda - 1.5 = 0 \;\Rightarrow\; \lambda = \frac{0.5 \pm \sqrt{0.25 + 6}}{2} = 1.5 \text{ or } -1.\]

(Trace 0.5, determinant \(0 - 1.5 = -1.5\).) For \(\lambda = 1.5\), \(L - 1.5I = \begin{bmatrix} -1.5 & 3 \\ 0.5 & -1 \end{bmatrix}\) gives \(j = 2a\), eigenvector \((2, 1)\). So in the long run the population grows by 50% a year with two juveniles for every adult, whatever it started as. The eigenvalue \(-1\) adds a boom-and-bust wobble that alternates sign each year, but relative to the \(1.5^n\) growth it shrinks by a factor \(1/1.5 = 2/3\) a year. Start with 10 adults and no juveniles: year by year the totals are 35, 32.5, 68.75, 83.1, 144.7, 197.0, and the juvenile-to-adult ratio goes \(6, 0.86, 3.2, 1.42, 2.48, 1.72, \ldots\), closing in on 2. Fisheries managers use exactly this calculation, with more age classes, to set catch limits: harvest so that the dominant eigenvalue stays at about 1.

Beyond ecology, the same "largest eigenvalue governs the long run" reasoning gives compound growth of interacting quantities in economics, the spread rate of an epidemic across age groups (the famous \(R_0\) is the dominant eigenvalue of a "next-generation matrix"), and the asymptotic running time of algorithms defined by recurrences, as the next section shows.

Diagonalisation fails for defective matrices

If there aren’t \(n\) independent eigenvectors (the shear, or anything with a complex eigenvalue if you insist on real numbers), \(P\) isn’t invertible and \(A = PDP^{-1}\) is impossible. The general fallback is the Jordan form (nearly diagonal, with 1s just above the diagonal), which is theoretically important but numerically fragile; in practice, people use the Schur decomposition or the SVD (Section 28.11) instead. And even when \(A\) is diagonalisable, if its eigenvectors are nearly parallel then \(P\) is nearly singular and computing with \(PDP^{-1}\) loses accuracy. Symmetric matrices never have either problem.

28.5. Fibonacci via matrix powers

28.5.1. The problem

The Fibonacci sequence (Chapter 14) is \(F_0 = 0\), \(F_1 = 1\), \(F_{n+1} = F_n + F_{n-1}\): 0, 1, 1, 2, 3, 5, 8, 13, … Is there a formula for \(F_n\) that doesn’t require computing all the earlier terms? And how fast does the sequence grow?

28.5.2. Turning a recurrence into a matrix

The recurrence needs the two most recent terms, so track them together as a state vector \((F_{n+1}, F_n)\). One step forward:

\[\begin{bmatrix} F_{n+2} \\ F_{n+1} \end{bmatrix} = \begin{bmatrix} F_{n+1} + F_n \\ F_{n+1} \end{bmatrix} = \underbrace{\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}}_{M}\begin{bmatrix} F_{n+1} \\ F_n \end{bmatrix}.\]

Starting from \((F_1, F_0) = (1, 0)\), \(n\) steps give \((F_{n+1}, F_n) = M^n(1, 0)\). So the Fibonacci numbers are the entries of the powers of \(M\). (In fact \(M^n = \begin{bmatrix} F_{n+1} & F_n \\ F_n & F_{n-1} \end{bmatrix}\).)

28.5.3. Eigenvalues of \(M\)

Trace 1, determinant \(-1\), so the characteristic equation is

\[\lambda^2 - \lambda - 1 = 0 \quad\Longrightarrow\quad \lambda = \frac{1 \pm \sqrt{5}}{2}.\]

These are the golden ratio \(\varphi = \tfrac{1 + \sqrt{5}}{2} \approx 1.618\) and its partner \(\psi = \tfrac{1 - \sqrt{5}}{2} \approx -0.618\). Eigenvectors: for either root \(\lambda\), the vector \((\lambda, 1)\) works, because

\[M\begin{bmatrix} \lambda \\ 1 \end{bmatrix} = \begin{bmatrix} \lambda + 1 \\ \lambda \end{bmatrix} = \begin{bmatrix} \lambda^2 \\ \lambda \end{bmatrix} = \lambda\begin{bmatrix} \lambda \\ 1 \end{bmatrix}, \qquad\text{using } \lambda^2 = \lambda + 1.\]

28.5.4. Deriving Binet’s formula

Write the starting vector as a combination of eigenvectors. Since \(\varphi - \psi = \sqrt{5}\),

\[\begin{bmatrix} 1 \\ 0 \end{bmatrix} = \frac{1}{\sqrt{5}}\left(\begin{bmatrix} \varphi \\ 1 \end{bmatrix} - \begin{bmatrix} \psi \\ 1 \end{bmatrix}\right).\]

(Check: top \((\varphi - \psi)/\sqrt{5} = 1\), bottom \((1 - 1)/\sqrt{5} = 0\) ✓.) Now apply \(M^n\): each eigenvector just gets multiplied by its eigenvalue to the \(n\):

\[\begin{bmatrix} F_{n+1} \\ F_n \end{bmatrix} = M^n\begin{bmatrix} 1 \\ 0 \end{bmatrix} = \frac{1}{\sqrt{5}}\left(\varphi^n\begin{bmatrix} \varphi \\ 1 \end{bmatrix} - \psi^n\begin{bmatrix} \psi \\ 1 \end{bmatrix}\right).\]

The bottom entry is

\[\boxed{\ F_n = \frac{\varphi^n - \psi^n}{\sqrt{5}}\ }\]

This is Binet’s formula. A sequence of whole numbers is given by a formula full of square roots of 5, which all cancel. Since \(|\psi| < 1\), the \(\psi^n\) term shrinks to nothing, so \(F_n\) is simply \(\varphi^n/\sqrt{5}\) rounded to the nearest integer, and the ratio of consecutive terms tends to \(\varphi\). The growth rate of the sequence is the dominant eigenvalue.

28.5.5. Implementation: exact Fibonacci in \(O(\log n)\) steps

Binet’s formula is elegant but floating-point arithmetic ruins it for large \(n\). Matrix powers computed by repeated squaring stay in exact integers and need only about \(\log_2 n\) matrix multiplications (the same trick as fast modular exponentiation in Chapter 23): Try it: Matrix powers versus Binet’s formula.

def mat_mult(X, Y):
    return [[X[0][0]*Y[0][0] + X[0][1]*Y[1][0], X[0][0]*Y[0][1] + X[0][1]*Y[1][1]],
            [X[1][0]*Y[0][0] + X[1][1]*Y[1][0], X[1][0]*Y[0][1] + X[1][1]*Y[1][1]]]

def mat_pow(M, n):
    """M**n by repeated squaring: O(log n) matrix multiplications."""
    result = [[1, 0], [0, 1]]              # identity
    while n > 0:
        if n % 2 == 1:                     # this binary digit of n is 1
            result = mat_mult(result, M)
        M = mat_mult(M, M)                 # square for the next digit
        n //= 2
    return result

def fib(n):
    return mat_pow([[1, 1], [1, 0]], n)[0][1]

print([fib(n) for n in range(1, 13)])
print(fib(100))
phi = (1 + 5 ** 0.5) / 2
print(round(phi ** 100 / 5 ** 0.5))     # Binet, dominant term only
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
354224848179261915075
354224848179263111168

The exact answer and the floating-point Binet answer agree in their first 14 digits and then diverge: a 64-bit float carries only about 16 significant digits (Chapter 32). Choose the method by the job: Binet for growth rates and estimates, exact matrix powers for exact values.

Computer-science connection: linear recurrences everywhere

Any linear recurrence, where the next value is a fixed linear combination of the previous \(k\) values, becomes a \(k\times k\) matrix power in the same way. Its long-run growth rate is the largest eigenvalue in absolute value. This is how you analyse the running time of recursive algorithms whose recurrences look like Fibonacci (the naive recursive fib makes about \(\varphi^n\) calls), count strings that avoid a forbidden pattern (Chapter 24), and compute population models such as the Leslie matrix in ecology. Linear-feedback shift registers, used for pseudo-random numbers and CRC checksums, are matrix powers over the integers mod 2.

28.6. Markov chains and steady states

28.6.1. The problem

A crude weather model: if today is sunny, tomorrow is sunny with probability 0.9 and rainy with probability 0.1. If today is rainy, tomorrow is sunny or rainy with probability 0.5 each. It’s raining today. What’s the chance of sun in a week? In the long run, what fraction of days are sunny?

Two states, Sunny and Rainy, with transition probabilities 0.9 sunny to sunny, 0.1 sunny to rainy, 0.5 rainy to sunny, 0.5 rainy to rainy
Figure 250. A two-state Markov chain. Each arrow is labelled with the probability of moving along it tomorrow; the probabilities leaving each state sum to 1.

28.6.2. Setting it up as a matrix

Let \(\mathbf{x}_n = (s_n, r_n)\) be the probabilities of sun and rain on day \(n\). Tomorrow’s chance of sun comes from two routes, sunny-then-sunny and rainy-then-sunny:

\[\begin{aligned} s_{n+1} &= 0.9\,s_n + 0.5\,r_n \\ r_{n+1} &= 0.1\,s_n + 0.5\,r_n \end{aligned} \qquad\Longleftrightarrow\qquad \mathbf{x}_{n+1} = \underbrace{\begin{bmatrix} 0.9 & 0.5 \\ 0.1 & 0.5 \end{bmatrix}}_{P}\mathbf{x}_n.\]

Column \(j\) of \(P\) lists where you go from state \(j\); each column sums to 1. After \(n\) days, \(\mathbf{x}_n = P^n\mathbf{x}_0\): a matrix power again.

Markov chain, transition matrix, steady state

A Markov chain is a random process that moves between states, where the probability of the next state depends only on the current one (it has no memory of earlier history). Its transition matrix \(P\) has entry \(p_{ij}\) = probability of moving to state \(i\) from state \(j\). With this convention (column-stochastic), columns sum to 1 and distributions update as \(\mathbf{x} \mapsto P\mathbf{x}\). Many statistics texts use the transpose (row-stochastic, rows sum to 1, \(\mathbf{x}^\mathsf{T} \mapsto \mathbf{x}^\mathsf{T}P\)); check which a source uses. A steady state (or stationary distribution) is a probability vector with \(P\mathbf{x} = \mathbf{x}\): an eigenvector with eigenvalue 1, scaled so its entries sum to 1.

28.6.3. The steady state is an eigenvector

If the distribution settles down to some \(\mathbf{x}^*\), then applying \(P\) once more leaves it unchanged: \(P\mathbf{x}^* = \mathbf{x}^*\). That’s an eigenvector with eigenvalue 1. Solve \((P - I)\mathbf{x} = \mathbf{0}\):

\[\begin{aligned} P - I &= \begin{bmatrix} -0.1 & 0.5 \\ 0.1 & -0.5 \end{bmatrix} &&\quad\text{(rows are negatives of each other)} \\ -0.1\,s + 0.5\,r &= 0 \quad\Longrightarrow\quad s = 5r, \\ s + r &= 1 \quad\Longrightarrow\quad 6r = 1, \quad r = \tfrac{1}{6}, \quad s = \tfrac{5}{6}. \end{aligned}\]

In the long run, five days in six are sunny, regardless of today’s weather.

28.6.4. How fast it gets there: the second eigenvalue

Trace \(1.4\), determinant \(0.45 - 0.05 = 0.4\), so \(\lambda^2 - 1.4\lambda + 0.4 = (\lambda - 1)(\lambda - 0.4) = 0\). One eigenvalue is 1 (the steady state); the other is 0.4. Decompose the starting distribution into the steady state plus a component along the second eigenvector; each day that second component is multiplied by 0.4. So the distance from equilibrium shrinks by a factor of 0.4 per day. Starting from rain, the chance of sun on day \(n\) works out to Try it: Explore the weather chain.

\[s_n = \tfrac{5}{6} - \tfrac{5}{6}(0.4)^n,\]

which gives \(s_1 = 0.5\), \(s_2 = 0.7\), \(s_3 = 0.78\), and by day 7, \(0.8320\): within 0.002 of \(5/6\). The code confirms it:

import numpy as np

P = np.array([[0.9, 0.5],     # column j = today's state j; rows = tomorrow
              [0.1, 0.5]])
x = np.array([0.0, 1.0])      # start: certainly rainy
for day in range(1, 7):
    x = P @ x
    print(day, x.round(4))

vals, vecs = np.linalg.eig(P)
print(vals)
k = np.argmax(np.isclose(vals, 1.0))
steady = vecs[:, k] / vecs[:, k].sum()   # rescale so entries sum to 1
print(steady)
1 [0.5 0.5]
2 [0.7 0.3]
3 [0.78 0.22]
4 [0.812 0.188]
5 [0.8248 0.1752]
6 [0.8299 0.1701]
[1.  0.4]
[0.83333333 0.16666667]
Two curves of the probability of sun against day number, one rising from 0 and one falling from 1, both levelling off at five sixths by about day 5
Figure 251. The chance of sun on day \(n\), starting from a rainy day (blue) or a sunny day (orange). Both converge to the steady state \(5/6\) (dashed), and the gap shrinks by the second eigenvalue’s factor of 0.4 each day, so after a week the starting weather is all but forgotten.

Why is 1 always an eigenvalue of a transition matrix, and why are the others no bigger than 1 in size? Because the columns sum to 1, multiplying by \(P\) preserves total probability, so no component can grow forever. The gap between 1 and the next-largest eigenvalue size (here \(1 - 0.4 = 0.6\)) is the spectral gap, and it controls the convergence speed: a big gap means the chain forgets its starting point quickly.

When there’s no single steady state

Two things can go wrong. A chain that cycles (\(P = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}\): rain always follows sun and vice versa) has a second eigenvalue of \(-1\), so the distribution oscillates forever and never settles, even though a stationary distribution \((\tfrac{1}{2}, \tfrac{1}{2})\) exists. A chain that splits into separate pieces that can’t reach each other has eigenvalue 1 more than once and several steady states, depending on where you start. A chain with neither problem (irreducible and aperiodic) always converges to a unique steady state from any starting distribution; that’s the content of the Perron–Frobenius theorem. PageRank’s "damping" trick exists precisely to force these conditions.

28.6.5. Where you’d use this

  • Everyday: brand switching and board games. Each month 20% of a phone network’s customers leave for its rival, and 10% of the rival’s customers come the other way. The transition matrix \(\begin{bmatrix} 0.8 & 0.1 \\ 0.2 & 0.9 \end{bmatrix}\) has steady state from \(0.2a = 0.1b\), so \(b = 2a\): in the long run the first network keeps only \(\tfrac{1}{3}\) of the market, wherever it starts. (Halving its churn from 20% to 10% would make the shares equal.) The same eigenvector calculation, with 40 states, shows which Monopoly squares players land on most often. Try it: Brand switching chain.

  • Science: DNA sequence evolution models, chemical reaction networks, the queue at a call centre and credit-rating changes for bonds (rated AA this year, what’s the chance of A next year?) are all Markov chains whose long-run behaviour is an eigenvector.

  • Computing: your phone keyboard’s next-word suggestions began as Markov chains on words; Markov chain Monte Carlo (MCMC) deliberately builds a chain whose steady state is a probability distribution you want to sample, and is the workhorse of Bayesian statistics (Chapter 29); and the random surfer of the next section is a Markov chain on web pages.

28.7. PageRank: the eigenvector that built Google

28.7.1. The problem

In 1998 the web had tens of millions of pages, and keyword search returned thousands of matches for any query. Which matches should come first? Larry Page and Sergey Brin’s idea: a page is important if important pages link to it. That sounds circular, and it is. Eigenvectors are how you solve circular definitions.

28.7.2. The random surfer

Imagine a surfer who starts on a random page and, at every step, clicks a random link on the current page. Over a long time, what fraction of steps does the surfer spend on each page? Pages with many incoming links from frequently visited pages will be visited often. That long-run fraction is the page’s PageRank, and it’s exactly the steady state of a Markov chain whose states are web pages.

Here’s a tiny web:

Directed graph of four pages A, B, C, D with links A to B, A to C, B to C, C to A and D to C
Figure 252. A four-page web. Page A links to B and C; B links to C; C links to A; D links to C, and nothing links to D.

The link matrix \(M\) has \(m_{ij} = 1/(\text{number of out-links of } j)\) if page \(j\) links to page \(i\), and 0 otherwise. Columns are pages being left; each column splits its page’s "vote" equally among its links (Chapter 25 calls this a weighted adjacency matrix):

\[M = \begin{array}{c} \\ A \\ B \\ C \\ D \end{array} \begin{array}{c} \begin{array}{cccc} A & \;B & \;C & \;D \end{array} \\ \begin{bmatrix} 0 & 0 & 1 & 0 \\ \tfrac{1}{2} & 0 & 0 & 0 \\ \tfrac{1}{2} & 1 & 0 & 1 \\ 0 & 0 & 0 & 0 \end{bmatrix} \end{array}.\]

28.7.3. Damping: the teleporting surfer

The pure random surfer gets stuck in pages with no out-links and in closed loops, and page D (no incoming links) would get rank zero, which breaks the uniqueness conditions above. The fix: with probability \(d\) (the damping factor, classically 0.85) the surfer follows a random link; with probability \(1 - d\) they get bored and jump to a page chosen uniformly at random. With \(n\) pages, the Google matrix is Try it: Move the damping slider.

\[G = d\,M + \frac{1 - d}{n}\,J,\]

where \(J\) is the matrix of all 1s. Every entry of \(G\) is positive, so the Perron–Frobenius theorem guarantees a unique steady state with all-positive entries, and the chain converges to it. The PageRank vector \(\mathbf{r}\) is the eigenvector of \(G\) with eigenvalue 1:

\[G\,\mathbf{r} = \mathbf{r}, \qquad \textstyle\sum_i r_i = 1.\]

28.7.4. Power iteration

For the real web, \(G\) has billions of rows. You can’t run a textbook eigenvalue algorithm on it. But you don’t need to: you know the eigenvalue you want (1), you know it’s the largest, and the diagonalisation section showed that repeatedly applying a matrix swings any vector towards its dominant eigenvector. So just keep multiplying. Try it: Watch power iteration converge.

Sequence of unit vectors v0, v1, v2 and so on rotating towards the diagonal line through (1,1)
Figure 253. Power iteration: repeatedly applying \(A = \begin{bmatrix} 4 & 1 \\ 2 & 3 \end{bmatrix}\) and renormalising pulls a starting vector \(\mathbf{v}_0\) onto the dominant eigenvector direction \((1, 1)\). The error shrinks by a factor of \(|\lambda_2/\lambda_1| = 2/5\) per step.

Why it works: write \(\mathbf{v}_0 = c_1\mathbf{v}_1 + c_2\mathbf{v}_2 + \cdots\) in eigenvectors. Then \(A^k\mathbf{v}_0 = c_1\lambda_1^k\mathbf{v}_1 + c_2\lambda_2^k\mathbf{v}_2 + \cdots = \lambda_1^k\big(c_1\mathbf{v}_1 + c_2(\lambda_2/\lambda_1)^k\mathbf{v}_2 + \cdots\big)\). If \(|\lambda_1|\) is strictly the largest, every ratio \((\lambda_i/\lambda_1)^k\) shrinks to zero, leaving only the \(\mathbf{v}_1\) direction. Renormalising each step stops the numbers overflowing.

Example 153. Procedure — power iteration (and PageRank)

Input: a square matrix \(A\) (for PageRank, the Google matrix \(G\), or just a way to compute \(G\mathbf{v}\)); a tolerance \(\varepsilon\); a maximum number of iterations.

Steps:

  1. Start with any vector \(\mathbf{v}\) having a component along the dominant eigenvector (for PageRank, the uniform vector \((1/n, \ldots, 1/n)\)).

  2. Compute \(\mathbf{w} = A\mathbf{v}\).

  3. Renormalise: \(\mathbf{w} \leftarrow \mathbf{w}/\|\mathbf{w}\|\) (for PageRank, divide by the sum of entries, which for \(G\) stays 1 automatically).

  4. If \(\|\mathbf{w} - \mathbf{v}\| < \varepsilon\), stop. Otherwise set \(\mathbf{v} \leftarrow \mathbf{w}\) and go to step 2.

  5. Estimate the eigenvalue with the Rayleigh quotient \(\lambda \approx \mathbf{v}^\mathsf{T}A\mathbf{v}\) (for a unit vector \(\mathbf{v}\)); for PageRank it’s 1 by construction.

Output: the dominant eigenvector \(\mathbf{v}\) (the PageRank vector) and its eigenvalue. Convergence is geometric with ratio \(|\lambda_2/\lambda_1|\); for the Google matrix, \(|\lambda_2| \le d\), so the error shrinks at least by \(0.85\) per iteration.

28.7.5. Worked example: ranking the tiny web

Before running code, predict. D has no incoming links, so it only gets teleport traffic: \(r_D = (1 - d)/n = 0.15/4 = 0.0375\). C receives links from three pages (including all of B’s vote and all of D’s) and should rank highest. A receives all of C’s vote, so A should be close behind. B receives only half of A’s vote. Try it: Rank the tiny web; Rank the tiny web.

import numpy as np

pages = ["A", "B", "C", "D"]
links = {"A": ["B", "C"], "B": ["C"], "C": ["A"], "D": ["C"]}
n = len(pages)
idx = {p: i for i, p in enumerate(pages)}

# Link matrix: column j spreads page j's vote equally over its out-links.
M = np.zeros((n, n))
for src, dests in links.items():
    for dst in dests:
        M[idx[dst], idx[src]] = 1 / len(dests)

d = 0.85                                   # damping factor
G = d * M + (1 - d) / n * np.ones((n, n))  # the "Google matrix"

r = np.full(n, 1 / n)                      # start: equal rank everywhere
for it in range(1, 101):
    r_new = G @ r
    change = np.abs(r_new - r).sum()
    r = r_new
    if change < 1e-10:
        break
print("iterations:", it)
for p in pages:
    print(p, round(r[idx[p]], 4))
iterations: 47
A 0.3725
B 0.1958
C 0.3941
D 0.0375

Check the steady-state equations by hand. Each page’s rank is its teleport share plus \(d\) times the votes it receives:

\[\begin{aligned} r_A &= 0.0375 + 0.85\,r_C = 0.0375 + 0.85(0.3941) \approx 0.3725 \ \checkmark \\ r_B &= 0.0375 + 0.85\cdot\tfrac{1}{2}r_A = 0.0375 + 0.425(0.3725) \approx 0.1958 \ \checkmark \\ r_D &= 0.0375 \ \checkmark \end{aligned}\]

and the four ranks sum to 1. The prediction holds: C first, A a close second (one very good inbound link can be worth as much as several mediocre ones), then B, then the unlinked D.

Computer-science connection: PageRank at web scale

The real link matrix is sparse: a page links to dozens of others, not billions, so \(M\) has a few dozen nonzeros per column. Power iteration never forms \(G\) (which would be dense); it computes \(G\mathbf{r} = dM\mathbf{r} + \frac{1 - d}{n}\mathbf{1}\) using the sparse \(M\) and the fact that \(J\mathbf{r} = \mathbf{1}\) when \(\mathbf{r}\) sums to 1. Each iteration costs \(O(\text{number of links})\) and parallelises naturally: it’s the canonical example in MapReduce and in graph-processing systems like Pregel. About 50–100 iterations suffice in practice, because the error shrinks by at least 0.85 per step (\(0.85^{100} \approx 10^{-7}\)).

Variations of the same eigenvector idea rank people in social networks (eigenvector centrality, Chapter 25), scientific papers by citations, players in tournaments, and the importance of code modules in a dependency graph. Personalised PageRank replaces the uniform teleport with a jump back to a user’s own pages, which makes it a recommendation engine.

The $25,000,000,000 eigenvector

That was the title of a 2006 paper by Kurt Bryan and Tanya Leise explaining PageRank to undergraduates, the figure being roughly Google’s market value at the time. The idea wasn’t entirely new: in 1976 Gabriel Pinski and Francis Narin ranked scientific journals by an eigenvector of the citation matrix, and in 1953 the sociologist Leo Katz proposed a similar measure of social status. What Page and Brin added was the random-surfer interpretation, the damping trick that makes it well-posed on a messy real web, and the engineering to compute it over the entire web. "PageRank" is a pun: it ranks pages, and it was invented by Larry Page.

Grow your own web

The learner draws pages as nodes and links as arrows. The app animates a crowd of random surfers hopping along links (with occasional teleports), sizes each node by the fraction of surfers on it, and shows the power-iteration vector converging alongside. Sliders: damping factor \(d\) from 0 to 1 (watch rankings flatten towards uniform as \(d \to 0\), and convergence slow as \(d \to 1\)). Challenges: add a "link farm" of pages that all point at one page and see how much rank it gains; create a dead-end page and a closed loop and observe what goes wrong with \(d = 1\).

28.8. Exercises: powers, chains, and ranking

  1. (Recognition) A Markov transition matrix has eigenvalues 1, 0.95 and 0.2. Another has 1, 0.5 and 0.1. Which converges to its steady state faster, and why?

  2. (Application) Use diagonalisation to find a formula for \(\begin{bmatrix} 2 & 1 \\ 1 & 2 \end{bmatrix}^n\) (eigenvalues and eigenvectors from the earlier exercise).

  3. (Application) A gym member who works out today works out tomorrow with probability 0.7; one who skips today works out tomorrow with probability 0.4. Find the long-run fraction of days spent working out. Try it: Check the gym chain.

  4. (Problem solving) In the tiny web, add a link from B to D. Without computing, predict how the ranks of B, C and D change. Then explain why a page with no out-links (a "dangling node") breaks the column-sum property of \(M\), and suggest a fix. Try it: Add the link B to D; Add the link B → D.

Answers
  1. The second: its second-largest eigenvalue size is 0.5, so errors shrink by half each step, versus only 5% per step (\(\times 0.95\)) for the first. Spectral gap 0.5 versus 0.05.

  2. \(P = \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix}\), \(D = \operatorname{diag}(3, 1)\), \(P^{-1} = \tfrac{1}{2}\begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix}\). \(PD^nP^{-1} = \tfrac{1}{2}\begin{bmatrix} 3^n + 1 & 3^n - 1 \\ 3^n - 1 & 3^n + 1 \end{bmatrix}\). Check \(n = 1\): \(\tfrac{1}{2}(4, 2; 2, 4) = (2, 1; 1, 2)\) ✓.

  3. States (work out, skip): \(P = \begin{bmatrix} 0.7 & 0.4 \\ 0.3 & 0.6 \end{bmatrix}\). Steady state: \(-0.3w + 0.4s = 0\), so \(w = \tfrac{4}{3}s\); with \(w + s = 1\), \(s = 3/7\), \(w = 4/7 \approx 0.571\).

  4. B’s vote is now split between C and D, so C loses rank and D gains (it finally has an inbound link). A depends entirely on C, so A falls too, and B, which depends on A, falls slightly. Running the code with the extra link confirms it: A 0.3426, B 0.1831, C 0.3590, D 0.1153. A dangling page’s column in \(M\) is all zeros, so the column sums to 0, not 1, and probability "leaks" out each step. Standard fix: treat a dangling page as linking to every page (replace its column with \(1/n\) everywhere), i.e. the surfer teleports from dead ends.

28.9. Symmetric matrices and orthogonal eigenvectors

28.9.1. The observation

In the exercise above, \(\begin{bmatrix} 2 & 1 \\ 1 & 2 \end{bmatrix}\) had eigenvectors \((1, 1)\) and \((1, -1)\), which are perpendicular. That’s no accident. The matrix is symmetric (\(A^\mathsf{T} = A\), Chapter 27), and symmetric matrices are the best-behaved matrices in linear algebra.

The spectral theorem

If \(A\) is a real symmetric \(n\times n\) matrix, then:

  1. all its eigenvalues are real (no rotation hiding inside);

  2. eigenvectors for different eigenvalues are orthogonal;

  3. it always has \(n\) orthonormal eigenvectors, so it can be diagonalised as

    \[A = Q\Lambda Q^\mathsf{T},\]

    where \(Q\) is an orthogonal matrix (orthonormal eigenvector columns, so \(Q^{-1} = Q^\mathsf{T}\)) and \(\Lambda\) (capital lambda) is diagonal.

Geometrically: every symmetric matrix is a pure stretch along some set of perpendicular axes. Rotate to those axes (\(Q^\mathsf{T}\)), stretch (\(\Lambda\)), rotate back (\(Q\)). No shear, no rotation.

28.9.2. Why the eigenvectors are perpendicular

The proof is short and uses only the transpose rule \(\mathbf{u}\cdot(A\mathbf{v}) = \mathbf{u}^\mathsf{T}A\mathbf{v} = (A^\mathsf{T}\mathbf{u})^\mathsf{T}\mathbf{v}\). Suppose \(A\mathbf{u} = \lambda\mathbf{u}\) and \(A\mathbf{v} = \mu\mathbf{v}\) with \(\lambda \neq \mu\). Compute \(\mathbf{u}^\mathsf{T}A\mathbf{v}\) two ways:

\[\begin{aligned} \mathbf{u}^\mathsf{T}(A\mathbf{v}) &= \mathbf{u}^\mathsf{T}(\mu\mathbf{v}) = \mu\,(\mathbf{u}\cdot\mathbf{v}), \\ (\mathbf{u}^\mathsf{T}A)\mathbf{v} &= (A^\mathsf{T}\mathbf{u})^\mathsf{T}\mathbf{v} = (A\mathbf{u})^\mathsf{T}\mathbf{v} = \lambda\,(\mathbf{u}\cdot\mathbf{v}). &&\quad\text{(symmetry: } A^\mathsf{T} = A\text{)} \end{aligned}\]

The two are equal, so \((\lambda - \mu)(\mathbf{u}\cdot\mathbf{v}) = 0\). Since \(\lambda \neq \mu\), we must have \(\mathbf{u}\cdot\mathbf{v} = 0\). ∎

Symmetric matrices turn up constantly: covariance matrices (next section), the \(A^\mathsf{T}A\) of least squares, graph Laplacians, the Hessian of second derivatives (Chapter 20), and the stiffness matrices of engineering. Whenever you have one, use np.linalg.eigh, which exploits the symmetry.

28.10. Principal component analysis

28.10.1. The problem

You’ve measured 500 people’s height and arm span. The two numbers are strongly correlated: tall people have long arms. Plotted, the data form an elongated, tilted cloud. Is there a single number that captures most of what distinguishes one person from another? More generally, you have data with 768 features per item (embeddings, again). Can you find a handful of directions that capture most of the variation, to visualise, compress, or denoise it?

28.10.2. Intuition: find the long axis of the cloud

Scatter plot of arm span against height forming a tilted elongated cloud, with two perpendicular arrows from the centre along its long and short axes
Figure 254. Height and arm span for 500 simulated people. The first principal component (PC1, orange) points along the cloud’s long axis; the second (PC2, green) is perpendicular to it. Arrows are drawn two standard deviations long.

The cloud is long in one direction and thin in the other. If you projected every point onto the long axis (Chapter 26), you’d keep most of the spread between people and lose only the small sideways scatter. That long axis is the direction of maximum variance, and finding it is principal component analysis.

28.10.3. Why it works: variance along a direction is a quadratic form

Centre the data (subtract the mean of each feature), so the cloud sits at the origin. Put the \(N\) centred data points as rows of a matrix \(X\) (\(N\times p\) for \(p\) features). The variance of the data projected onto a unit direction \(\mathbf{u}\) is the average of the squared projections \((\mathbf{x}_i\cdot\mathbf{u})^2\):

\[\operatorname{Var}(\mathbf{u}) = \frac{1}{N - 1}\sum_{i=1}^{N}(\mathbf{x}_i^\mathsf{T}\mathbf{u})^2 = \frac{1}{N - 1}\|X\mathbf{u}\|^2 = \mathbf{u}^\mathsf{T}\Big(\underbrace{\tfrac{1}{N - 1}X^\mathsf{T}X}_{C}\Big)\mathbf{u}.\]

The matrix \(C\) is the covariance matrix (\(p\times p\), symmetric). Its diagonal entries are the variances of each feature; its off-diagonal entries are the covariances between pairs of features (Chapter 31). Now write \(C = Q\Lambda Q^\mathsf{T}\) (spectral theorem) and let \(\mathbf{y} = Q^\mathsf{T}\mathbf{u}\), the direction expressed in eigen-coordinates (still a unit vector, since \(Q^\mathsf{T}\) is a rotation):

\[\operatorname{Var}(\mathbf{u}) = \mathbf{u}^\mathsf{T}Q\Lambda Q^\mathsf{T}\mathbf{u} = \mathbf{y}^\mathsf{T}\Lambda\mathbf{y} = \lambda_1 y_1^2 + \lambda_2 y_2^2 + \cdots + \lambda_p y_p^2.\]

With \(y_1^2 + \cdots + y_p^2 = 1\), this is a weighted average of the eigenvalues. It’s largest when all the weight goes on the largest eigenvalue: \(\mathbf{y} = (1, 0, \ldots, 0)\), i.e. \(\mathbf{u} = \) the top eigenvector. So:

  • The direction of maximum variance is the eigenvector of the covariance matrix with the largest eigenvalue, and that eigenvalue is the variance along it.

  • The next-best direction perpendicular to the first is the second eigenvector, and so on.

  • The eigenvalues add up to the total variance (the trace of \(C\)), so \(\lambda_k / \sum\lambda_i\) is the fraction of variance explained by component \(k\).

Covariance matrix, principal components
  • The covariance matrix of centred data \(X\) (rows = observations) is \(C = \frac{1}{N - 1}X^\mathsf{T}X\). Entry \(c_{jk}\) is the covariance of features \(j\) and \(k\); the diagonal holds variances. It’s symmetric with non-negative eigenvalues.

  • The principal components (PCs) are the eigenvectors of \(C\), sorted by decreasing eigenvalue: orthogonal directions of decreasing variance.

  • The scores of a data point are its coordinates along the PCs: \(X Q_k\) for the top \(k\) components. Keeping \(k < p\) of them is dimensionality reduction.

Example 154. Procedure — principal component analysis

Input: a data matrix \(X\) with \(N\) rows (observations) and \(p\) columns (features); the number \(k\) of components to keep.

Steps:

  1. Centre: subtract each column’s mean. (If features have different units, e.g. centimetres and kilograms, also divide each column by its standard deviation, i.e. standardise.)

  2. Covariance: \(C = \frac{1}{N - 1}X^\mathsf{T}X\).

  3. Eigen-decompose the symmetric \(C\) (np.linalg.eigh), and sort eigenvalues from largest to smallest, reordering the eigenvector columns to match.

  4. Choose \(k\): look at the fraction of variance explained; keep enough components to reach, say, 90–95%, or look for an "elbow" where eigenvalues drop off.

  5. Project: scores \(= X Q_k\), where \(Q_k\) holds the top \(k\) eigenvectors.

Output: the principal directions, their variances, and each observation’s \(k\)-dimensional scores.

28.10.4. Worked example: height and arm span

import numpy as np

rng = np.random.default_rng(1)
# 500 people: height (cm) and arm span (cm), strongly correlated.
height = rng.normal(170, 10, 500)
span = height + rng.normal(0, 4, 500)
X = np.column_stack([height, span])

Xc = X - X.mean(axis=0)                 # 1. centre each column
C = Xc.T @ Xc / (len(X) - 1)            # 2. covariance matrix (2 x 2)
vals, vecs = np.linalg.eigh(C)          # 3. eigen-decomposition (symmetric)
order = np.argsort(vals)[::-1]          #    sort largest first
vals, vecs = vals[order], vecs[:, order]
vecs = vecs * np.sign(vecs[0])          #    (fix the arbitrary sign of each axis)

print("covariance:\n", C.round(1))
print("variances along principal axes:", vals.round(1))
print("fraction explained:", (vals / vals.sum()).round(3))
print("first principal direction:", vecs[:, 0].round(3))

scores = Xc @ vecs[:, :1]               # 4. project onto the top axis
print(scores.shape)
covariance:
 [[ 83.5  83.5]
 [ 83.5 101.3]]
variances along principal axes: [176.4   8.4]
fraction explained: [0.954 0.046]
first principal direction: [0.669 0.744]
(500, 1)

Interpret each line. The covariance matrix says height varies with variance 83.5 (standard deviation about 9.1 cm), arm span slightly more (101.3, because it adds measurement noise), and they covary strongly (83.5: arm span is height plus independent noise, so their covariance is approximately height’s own variance). The first principal component, \((0.669, 0.744)\), points roughly along the diagonal: "overall size", with both measurements increasing together. It carries 95.4% of the variance. The second, perpendicular to it, is "arms long or short for your height", with only 4.6%. So one number per person, their score on PC1, captures almost everything: two dimensions have been reduced to one with little loss. The trace checks out too: \(83.5 + 101.3 = 184.8 \approx 176.4 + 8.4\).

28.10.5. Choosing a method, and when PCA misleads

PCA finds linear structure. It’s the right first tool for correlated numeric features, for visualising high-dimensional data in 2D, and for compressing before another algorithm. Know its limits:

PCA failure modes
  • Forgetting to centre makes the first "component" point at the mean rather than along the spread.

  • Mixed units. Measure height in millimetres instead of centimetres and its variance grows 100-fold, so PC1 becomes "height" purely because of units. Standardise features with different units.

  • Variance is not importance. The direction that separates two classes (say, healthy versus ill) may have small variance and be discarded. PCA is unsupervised: it never looks at labels.

  • Curved structure. Data on a curve or a "Swiss roll" isn’t captured by straight axes. Nonlinear methods (t-SNE, UMAP, autoencoders) exist for that, at the cost of interpretability.

  • Outliers inflate variance in their own direction and can hijack a component.

28.10.6. Where you’d use this

  • Everyday: why clothes come in S, M and L. Our height-and-arm-span example is the reason a single "size" works at all: body measurements are so strongly correlated that one number, essentially the score on PC1 ("overall size"), captures most of the variation. Garment designers run PCA on thousands of body scans to choose size charts that fit the most people with the fewest sizes.

  • Science: in a 2008 study of about 1,400 Europeans genotyped at about 200,000 genetic markers, a plot of each person’s scores on the first two principal components reproduced a recognisable map of Europe; geography was the direction of greatest genetic variation. In finance, three principal components of interest-rate movements, interpreted as "level", "slope" and "curvature" of the yield curve, typically explain well over 90% of the day-to-day variation, so banks hedge risk against those three numbers instead of dozens of rates. Climate scientists call principal components of weather maps empirical orthogonal functions; the leading one in the tropical Pacific is the El Niño pattern.

  • Computing: "eigenfaces" (1991) recognised faces by projecting photos onto the top principal components of a face dataset; PCA is also the standard first step before clustering or plotting any high-dimensional data, as the next sidebar describes.

Computer-science connection: dimensionality reduction for embeddings

Embedding vectors (Chapter 26) typically have hundreds or thousands of dimensions, but their effective dimension is often much lower: PCA on a collection of sentence embeddings usually shows the variance concentrated in a fraction of the components. That’s useful in several ways:

  • Visualisation: project to the top 2 or 3 PCs to plot clusters of documents (with the caveats from Chapter 26 about high-dimensional distances).

  • Compression: storing 128 PCA scores instead of 768 raw components cuts a vector index’s memory six-fold, usually with a modest loss in retrieval quality. Vector databases combine this with quantisation.

  • Whitening and de-biasing: subtracting the mean and the top one or two components often improves cosine-similarity search, because those components encode features shared by all texts (the narrow "cone" that makes unrelated texts score 0.6). Removing them spreads the vectors out.

  • Faster search: searching in a lower-dimensional space is cheaper per comparison.

Some modern embedding models are trained so that their first \(k\) components already form a good \(k\)-dimensional embedding ("Matryoshka" embeddings), building the PCA-like ordering into the model itself.

Rotate the cloud

A scatter plot of 2D data with a draggable line through the centre. As the learner rotates the line, the app projects every point onto it and shows the variance of the projections as a live number and a dial. The learner hunts for the maximum, then clicks "reveal" to see PC1 drawn from the eigenvector of the covariance matrix, landing exactly on their best line. Further sliders change the correlation and the noise, so the learner sees the eigenvalue ratio (variance explained) track how elongated the cloud is.

28.11. The singular value decomposition

28.11.1. The problem

Eigenvectors have a frustrating limitation: they only exist for square matrices (a \(3\times 2\) matrix takes 2D vectors to 3D vectors, so "\(A\mathbf{v}\) is a multiple of \(\mathbf{v}\)" makes no sense), and even square matrices may lack enough of them. Is there a decomposition that works for every matrix, of any shape?

28.11.2. Intuition: rotate, scale, rotate

There is, and it’s the most useful factorisation in applied mathematics. Watch what a \(2\times 2\) matrix does to the unit circle: it always produces an ellipse. An ellipse has perpendicular axes. So some pair of perpendicular input directions (on the circle) must map to the perpendicular axes of the ellipse. That gives a three-step description of any matrix:

Four panels: a unit circle with two perpendicular arrows, the circle rotated, stretched into an axis-aligned ellipse, then rotated into the final tilted ellipse
Figure 255. Any matrix maps the unit circle to an ellipse, in three steps: rotate (or reflect) the input with \(V^\mathsf{T}\), stretch along the coordinate axes by the singular values with \(\Sigma\), then rotate into the output position with \(U\). Shown for the worked example below, \(A = \begin{bmatrix} 3 & 0 \\ 4 & 5 \end{bmatrix}\), with \(\mathbf{v}_1 = (1, 1)/\sqrt{2}\) (orange) and \(\mathbf{v}_2 = (-1, 1)/\sqrt{2}\) (green; its sign is flipped from the worked example so both outer steps are rotations), stretched by \(\sigma_1 \approx 6.71\) and \(\sigma_2 \approx 2.24\). The first two panels are drawn at a larger scale than the last two.
\[\boxed{\ A = U\,\Sigma\,V^\mathsf{T}\ }\]
  • \(V\) (\(n\times n\), orthogonal): its columns \(\mathbf{v}_i\) are the right singular vectors, the perpendicular input directions.

  • \(\Sigma\) (capital sigma, \(m\times n\), diagonal): the singular values \(\sigma_1 \ge \sigma_2 \ge \cdots \ge 0\), the stretch factors.

  • \(U\) (\(m\times m\), orthogonal): its columns \(\mathbf{u}_i\) are the left singular vectors, the output directions (the ellipse’s axes).

And \(A\mathbf{v}_i = \sigma_i\mathbf{u}_i\): each input axis goes to an output axis, scaled. This is the eigenvector idea generalised: you’re allowed different directions on the input and output sides, and in exchange it works for every matrix.

Singular value decomposition

Every real \(m\times n\) matrix can be written \(A = U\Sigma V^\mathsf{T}\) with \(U\), \(V\) orthogonal and \(\Sigma\) diagonal with non-negative entries \(\sigma_1 \ge \sigma_2 \ge \cdots\) (the singular values). The singular values are the square roots of the eigenvalues of the symmetric matrix \(A^\mathsf{T}A\); the columns of \(V\) are its eigenvectors. The number of nonzero singular values is the rank of \(A\). For a symmetric matrix with non-negative eigenvalues, the SVD and the eigendecomposition coincide.

28.11.3. Worked example: a 2×2 SVD by hand

Find the singular values of \(A = \begin{bmatrix} 3 & 0 \\ 4 & 5 \end{bmatrix}\). Try it: SVD as rotate–stretch–rotate; Circle to ellipse, step by step.

\[\begin{aligned} A^\mathsf{T}A &= \begin{bmatrix} 3 & 4 \\ 0 & 5 \end{bmatrix}\begin{bmatrix} 3 & 0 \\ 4 & 5 \end{bmatrix} = \begin{bmatrix} 25 & 20 \\ 20 & 25 \end{bmatrix}, \\ \text{char. eq.: } & \lambda^2 - 50\lambda + (625 - 400) = \lambda^2 - 50\lambda + 225 = (\lambda - 45)(\lambda - 5) = 0, \\ \sigma_1 &= \sqrt{45} = 3\sqrt{5} \approx 6.708, \qquad \sigma_2 = \sqrt{5} \approx 2.236. \end{aligned}\]

Eigenvectors of \(A^\mathsf{T}A\) (same pattern as the \(\begin{bmatrix} 2 & 1 \\ 1 & 2 \end{bmatrix}\) exercise): \(\mathbf{v}_1 = (1, 1)/\sqrt{2}\), \(\mathbf{v}_2 = (1, -1)/\sqrt{2}\). The output directions are \(\mathbf{u}_i = A\mathbf{v}_i/\sigma_i\):

\[\begin{aligned} \mathbf{u}_1 &= \frac{1}{\sqrt{45}}\cdot\frac{1}{\sqrt{2}}\begin{bmatrix} 3 \\ 9 \end{bmatrix} = \frac{1}{\sqrt{10}}\begin{bmatrix} 1 \\ 3 \end{bmatrix}, & \mathbf{u}_2 &= \frac{1}{\sqrt{5}}\cdot\frac{1}{\sqrt{2}}\begin{bmatrix} 3 \\ -1 \end{bmatrix} = \frac{1}{\sqrt{10}}\begin{bmatrix} 3 \\ -1 \end{bmatrix}. \end{aligned}\]

Checks: \(\mathbf{u}_1\cdot\mathbf{u}_2 = (3 - 3)/10 = 0\), both have length 1, and \(\sigma_1\sigma_2 = \sqrt{225} = 15 = |\det A|\) (area scaling is the product of the stretches). The unit circle becomes an ellipse with semi-axes 6.71 along \((1, 3)\) and 2.24 along \((3, -1)\).

28.11.4. Low-rank approximation

Multiply out \(U\Sigma V^\mathsf{T}\) column by column and you get a sum of simple pieces:

\[A = \sigma_1\mathbf{u}_1\mathbf{v}_1^\mathsf{T} + \sigma_2\mathbf{u}_2\mathbf{v}_2^\mathsf{T} + \cdots + \sigma_r\mathbf{u}_r\mathbf{v}_r^\mathsf{T}.\]

Each term \(\mathbf{u}_i\mathbf{v}_i^\mathsf{T}\) (column times row, an outer product) is a rank-1 matrix, and the terms are ordered by importance \(\sigma_i\). Keeping only the first \(k\) terms gives a rank-\(k\) matrix \(A_k\). The Eckart–Young theorem says this truncation is the best possible rank-\(k\) approximation to \(A\): no other rank-\(k\) matrix is closer (in the sense of summed squared entry differences). And storing it takes only \(k(m + n + 1)\) numbers instead of \(mn\).

Example 155. Procedure — low-rank approximation by SVD

Input: an \(m\times n\) matrix \(A\) (an image, a ratings table, a term–document matrix) and a target rank \(k\).

Steps:

  1. Compute the SVD: U, s, Vt = np.linalg.svd(A, full_matrices=False).

  2. Inspect the singular values s (plot them on a log scale) to choose \(k\): look for where they drop off.

  3. Keep the first \(k\) columns of \(U\), the first \(k\) singular values and the first \(k\) rows of \(V^\mathsf{T}\).

  4. Reconstruct \(A_k = U_k\,\operatorname{diag}(s_k)\,V_k^\mathsf{T}\) if you need the matrix itself, or use the factors directly as compact representations.

Output: \(A_k\), the best rank-\(k\) approximation, with relative error \(\sqrt{\sum_{i>k}\sigma_i^2}\big/\sqrt{\sum_i\sigma_i^2}\).

28.11.5. Worked example: compressing an image

A greyscale image is just a matrix of brightness values. Here’s a synthetic 200 × 200 image (smooth waves plus a bright disc plus noise) and its low-rank approximations: Try it: Compress the image yourself.

import numpy as np

# A synthetic 200 x 200 greyscale "image": smooth shapes plus a little noise.
y, x = np.mgrid[0:200, 0:200] / 200.0
img = (np.sin(6 * x) * np.cos(4 * y)
       + ((x - 0.6) ** 2 + (y - 0.4) ** 2 < 0.05)
       + 0.05 * np.random.default_rng(0).standard_normal((200, 200)))

U, s, Vt = np.linalg.svd(img)
print("largest singular values:", s[:6].round(1))

for k in [1, 5, 20, 50]:
    approx = U[:, :k] @ np.diag(s[:k]) @ Vt[:k, :]       # rank-k approximation
    err = np.linalg.norm(img - approx) / np.linalg.norm(img)
    stored = k * (200 + 200 + 1)
    print(f"rank {k:2d}: relative error {err:.3f}, numbers stored {stored:6d} of 40000")
largest singular values: [113.5  68.4  18.1  10.5   7.3   5.7]
rank  1: relative error 0.544, numbers stored    401 of 40000
rank  5: relative error 0.114, numbers stored   2005 of 40000
rank 20: relative error 0.071, numbers stored   8020 of 40000
rank 50: relative error 0.050, numbers stored  20050 of 40000
Four greyscale images: rank 1 blocky approximation, rank 5 recognisable, rank 20 nearly identical to the original, and the original
Figure 256. Rank-\(k\) approximations of a 200 × 200 image. Rank 5 already captures the waves and the disc; rank 20 is hard to tell from the original; the rest is mostly noise.
Plot of singular values against index, dropping steeply for the first few then flattening
Figure 257. Singular values of the image on a log scale. A few large values carry the structure; the long, flat tail is mostly noise.

The wave part of the image, \(\sin(6x)\cos(4y)\), is a product of a function of \(x\) and a function of \(y\): exactly rank 1. That’s why the first singular value is so dominant. The disc needs a few more terms (its edges are curved, not aligned with rows and columns). Beyond about rank 20 the remaining singular values are noise, and dropping them improves the image by denoising it. Real photographs aren’t this cooperative, which is why JPEG uses a different transform (the discrete cosine transform), but the idea of "keep the big coefficients, drop the small ones" is the same.

28.11.6. Latent semantic indexing: SVD in information retrieval

Recall the blind spot of bag-of-words search (Chapter 26): a query for "car" scores zero against a document that only says "automobile". Latent semantic indexing (LSI, also called latent semantic analysis), introduced by Deerwester, Dumais and colleagues in 1990, fixes this with the SVD. Try it: LSI concept space.

Build the term–document matrix \(A\): one row per term, one column per document. Its rank-\(k\) approximation forces the documents into a \(k\)-dimensional "concept" space. Terms that co-occur across documents ("car", "automobile" and "engine" all appear alongside each other) end up sharing a concept direction, so documents using different words for the same thing get similar concept vectors.

import numpy as np

# Term-document matrix: rows are terms, columns are documents.
terms = ["car", "automobile", "engine", "flower", "petal"]
#              d1  d2  d3  d4  d5
A = np.array([[1,  0,  1,  0,  0],    # car
              [0,  1,  1,  0,  0],    # automobile
              [1,  1,  1,  0,  0],    # engine
              [0,  0,  0,  1,  1],    # flower
              [0,  0,  0,  1,  0]],   # petal
             dtype=float)
q = np.array([1, 0, 0, 0, 0], dtype=float)     # query: "car"

U, s, Vt = np.linalg.svd(A)
k = 2                                           # keep two "concepts"
Uk, sk = U[:, :k], s[:k]
docs_k = Vt[:k, :].T * sk                       # documents in concept space
q_k = q @ Uk                                    # query in concept space

def cos(a, b):
    return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))

for j in range(5):
    print(f"d{j+1}: raw {cos(q, A[:, j]):.2f}   latent {cos(q_k, docs_k[j]):.2f}")
d1: raw 0.71   latent 1.00
d2: raw 0.00   latent 1.00
d3: raw 0.58   latent 1.00
d4: raw 0.00   latent 0.00
d5: raw 0.00   latent 0.00

Document d2 never mentions "car" and scores 0.00 on raw cosine similarity. In the 2-concept space it scores as high as the documents that do, because "automobile" and "engine" share the vehicle concept with "car". The flower documents stay at zero. (With only two clear topics, rank 2 sorts everything perfectly; real collections use \(k\) of a few hundred.)

28.11.7. Where you’d use this

  • Everyday: the worked example above is the arithmetic of image compression: the rank-20 picture keeps 8020 of 40,000 numbers, 20% of the storage, with about 7% error. Removing hiss from an old recording or noise from a low-light photo uses the same move: keep the large singular values (the signal) and drop the flat tail (mostly noise).

  • Science and engineering: to line up two 3D scans of the same object, or compare two shapes of the same protein, you need the best rotation taking one set of points onto the other. The standard answer (the Kabsch algorithm) takes the SVD of a \(3\times 3\) matrix built from the points and reads the rotation off \(U\) and \(V\). Medical imaging, seismology and climate science all use the SVD to separate a few strong patterns from noisy measurements, and the ratio \(\sigma_1/\sigma_n\) (the condition number) tells an engineer whether a measurement system can be trusted to invert (Chapter 32).

  • Computing: recommendation, search, model compression and least squares, collected in the next sidebar.

Computer-science connection: the SVD in the wild
  • Recommender systems. A users × items ratings matrix is mostly empty. Approximating it with a low-rank product \(UV^\mathsf{T}\) (each user and each item gets a short "taste" vector) predicts the missing ratings; this matrix factorisation approach was central to the winning entries of the 2006–2009 Netflix Prize.

  • Embeddings. LSI was an early ancestor of today’s embeddings. Word2vec-style word vectors were later shown to be closely related to factorising a word co-occurrence matrix.

  • Model compression. Large neural-network weight matrices are often approximately low-rank. LoRA (low-rank adaptation) fine-tunes a language model by learning a small rank-\(k\) update \(BA\) to each weight matrix instead of changing all its entries, cutting the trainable parameters by orders of magnitude.

  • PCA via SVD. In practice PCA is computed from the SVD of the centred data matrix, never by forming \(X^\mathsf{T}X\): the right singular vectors of \(X\) are the principal directions and \(\sigma_i^2/(N - 1)\) are the variances, and skipping \(X^\mathsf{T}X\) avoids squaring the condition number (Chapter 32).

  • Least squares and pseudo-inverses. The SVD gives the most stable way to solve over-determined and rank-deficient systems (np.linalg.lstsq uses it).

Choosing between eigendecomposition, PCA and SVD
  • Square matrix applied repeatedly (dynamics, Markov chains, recurrences, PageRank): eigenvalues and eigenvectors. The largest eigenvalue governs the long run.

  • Symmetric matrix (covariance, Laplacian, Hessian): eigendecomposition with eigh; orthogonal eigenvectors guaranteed.

  • Data cloud, directions of most variation: PCA, computed via the SVD of the centred data.

  • Any rectangular matrix, compression, denoising, rank, least squares: the SVD.

Compress it yourself

The learner uploads a photo (or picks one). The app converts it to greyscale, computes its SVD and shows a slider for the rank \(k\), redrawing the rank-\(k\) image live alongside a plot of the singular values with the cutoff marked, the storage cost, and the relative error. A second panel shows the individual rank-1 "layers" \(\sigma_i\mathbf{u}_i\mathbf{v}_i^\mathsf{T}\) for \(i = 1, 2, 3, \ldots\), so the learner sees the image being built from stripes. A third shows the unit circle turning into an ellipse for a user-chosen \(2\times 2\) matrix, step by step through \(V^\mathsf{T}\), \(\Sigma\) and \(U\).

28.12. Exercises: symmetric matrices, PCA, and SVD

  1. (Recognition) A covariance matrix for three features has eigenvalues 9, 0.9 and 0.1. What fraction of the variance does the first principal component explain? How many components would you keep?

  2. (Application) Find the principal directions of data with covariance matrix \(C = \begin{bmatrix} 5 & 2 \\ 2 & 2 \end{bmatrix}\) and the variance along each.

  3. (Application) Find the singular values of \(\begin{bmatrix} 2 & 0 \\ 0 & -3 \end{bmatrix}\). Why aren’t they just the eigenvalues?

  4. (Problem solving) A 1000 × 800 matrix has singular values that fall from 500 to about 1 by index 30 and stay near 1 after that. Estimate the storage saving of a rank-30 approximation and the relative error, and say what the flat tail probably represents.

Answers
  1. Total variance \(9 + 0.9 + 0.1 = 10\), so PC1 explains \(9/10 = 90\%\). PC1 and PC2 together explain 99%. Keep one component if 90% suffices, two for 99%; the third (1%) is probably noise.

  2. Trace 7, determinant \(10 - 4 = 6\): \(\lambda^2 - 7\lambda + 6 = (\lambda - 6)(\lambda - 1) = 0\). For 6: \(C - 6I = \begin{bmatrix} -1 & 2 \\ 2 & -4 \end{bmatrix}\), \(x = 2y\), direction \((2, 1)/\sqrt{5}\). For 1: \(C - I = \begin{bmatrix} 4 & 2 \\ 2 & 1 \end{bmatrix}\), \(y = -2x\), direction \((1, -2)/\sqrt{5}\). Perpendicular ✓ (\(2 - 2 = 0\)). Variances 6 and 1; PC1 explains \(6/7 \approx 86\%\).

  3. \(A^\mathsf{T}A = \operatorname{diag}(4, 9)\), so \(\sigma = 3, 2\) (sorted). Eigenvalues are 2 and \(-3\); singular values are always non-negative stretch factors, and the sign (a flip) is absorbed into \(U\) or \(V\). Singular values are the absolute values of the eigenvalues here because the matrix is symmetric.

  4. Full storage: \(800\,000\) numbers. Rank 30: \(30(1000 + 800 + 1) = 54\,030\), about 6.8% of the original. Relative error: \(\sqrt{\sum_{i>30}\sigma_i^2}/\sqrt{\sum\sigma_i^2}\); the tail has about 770 values near 1, contributing roughly \(770\), while the top 30 contribute at least \(500^2 = 250\,000\), so the error is roughly \(\sqrt{770/250\,000} \approx 0.055\) or less, about 5%. The flat tail is most likely noise: structureless variation spread evenly across all directions.

28.13. Summary

  • An eigenvector of \(A\) is a direction \(A\) only stretches: \(A\mathbf{v} = \lambda\mathbf{v}\). The eigenvalue \(\lambda\) is the stretch factor. Find eigenvalues from \(\det(A - \lambda I) = 0\) (for \(2\times 2\): \(\lambda^2 - (\operatorname{tr}A)\lambda + \det A = 0\)), then eigenvectors from \((A - \lambda I)\mathbf{v} = \mathbf{0}\).

  • Eigenvectors can be complex (rotations) or too few (shears). In code, use eig, or eigh for symmetric matrices, never polynomial roots.

  • Change of basis: \(P\) converts eigen-coordinates to standard ones. Diagonalisation \(A = PDP^{-1}\) makes powers easy: \(A^n = PD^nP^{-1}\). The largest eigenvalue governs the long run.

  • Fibonacci: the matrix \(\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}\) has eigenvalues \(\varphi, \psi\), giving Binet’s formula; repeated squaring gives exact values in \(O(\log n)\) steps.

  • A Markov chain's steady state is the eigenvector with eigenvalue 1; the second eigenvalue sets the convergence speed. PageRank is the steady state of a damped random surfer, computed by power iteration.

  • Symmetric matrices have real eigenvalues and orthonormal eigenvectors: \(A = Q\Lambda Q^\mathsf{T}\), a pure stretch along perpendicular axes.

  • PCA: the eigenvectors of the covariance matrix are the directions of maximum variance; eigenvalues are the variances.

  • The SVD \(A = U\Sigma V^\mathsf{T}\) (rotate, scale, rotate) works for every matrix. Truncating it gives the best low-rank approximation, used for image compression, latent semantic indexing, recommenders and model compression.

28.14. Interactive tools

Browser tools on the companion website that go with this chapter. Each example link in the chapter opens one of them with that example already set up.

28.15. Mastery check

  1. Show that \((1, 2)\) is an eigenvector of \(\begin{bmatrix} 2 & 1 \\ 2 & 3 \end{bmatrix}\) and state its eigenvalue. Then find the other eigenvalue using the trace, without solving a quadratic, and confirm it with the determinant.

  2. Find all eigenvalues and eigenvectors of \(\begin{bmatrix} 5 & -2 \\ 1 & 2 \end{bmatrix}\).

  3. For the matrix in question 2, write \(A = PDP^{-1}\) and use it to express \(A^{10}(3, 2)\) in terms of \(4^{10}\) and \(3^{10}\). What is \(A^{10}(1, 1)\)?

  4. Explain in two or three sentences why a 2D rotation by \(30^\circ\) has no real eigenvectors, and what its eigenvalues are.

  5. A Markov chain on two states has transition matrix \(\begin{bmatrix} 0.8 & 0.3 \\ 0.2 & 0.7 \end{bmatrix}\) (column-stochastic). Find its steady state and its second eigenvalue, and say how quickly it converges.

  6. In power iteration, why does the starting vector not matter (almost), and what makes convergence slow?

  7. Data have covariance matrix \(\begin{bmatrix} 4 & 0 \\ 0 & 1 \end{bmatrix}\). What are the principal components and the fraction of variance explained by the first? Describe the shape of the data cloud.

  8. State what each factor in \(A = U\Sigma V^\mathsf{T}\) does geometrically, and explain how truncating it compresses a matrix.

Decision rule: All correct without hesitation → you’ve completed Part VII; move on to Chapter 29 (or Chapter 31, where covariance and regression use this chapter directly). Stuck on 1–2 or 4 → reread Section 28.1 and Section 28.2. Stuck on 3 → reread Section 28.4. Stuck on 5–6 → reread Section 28.6 and Section 28.7. Stuck on 7–8 → reread Section 28.9, Section 28.10 and Section 28.11.

Mastery check answers
  1. \(A(1, 2) = (2 + 2,\ 2 + 6) = (4, 8) = 4(1, 2)\), so \((1, 2)\) is an eigenvector with \(\lambda = 4\). The trace is \(2 + 3 = 5 = \lambda_1 + \lambda_2\), so the other eigenvalue is \(5 - 4 = 1\). Determinant check: \(2\cdot 3 - 1\cdot 2 = 4 = 4 \times 1\) ✓. (Its eigenvector: \(A - I = \begin{bmatrix} 1 & 1 \\ 2 & 2 \end{bmatrix}\) gives \((1, -1)\).)

  2. Trace 7, determinant \(10 + 2 = 12\): \(\lambda^2 - 7\lambda + 12 = (\lambda - 4)(\lambda - 3) = 0\). For 4: \(A - 4I = \begin{bmatrix} 1 & -2 \\ 1 & -2 \end{bmatrix}\), \(x = 2y\), \(\mathbf{v}_1 = (2, 1)\). Check: \(A(2, 1) = (10 - 2, 2 + 2) = (8, 4)\) ✓. For 3: \(A - 3I = \begin{bmatrix} 2 & -2 \\ 1 & -1 \end{bmatrix}\), \(x = y\), \(\mathbf{v}_2 = (1, 1)\). Check: \(A(1, 1) = (3, 3)\) ✓.

  3. \(P = \begin{bmatrix} 2 & 1 \\ 1 & 1 \end{bmatrix}\), \(D = \operatorname{diag}(4, 3)\), \(\det P = 1\), \(P^{-1} = \begin{bmatrix} 1 & -1 \\ -1 & 2 \end{bmatrix}\). Eigen-coordinates of \((3, 2)\): \(P^{-1}(3, 2) = (3 - 2,\ -3 + 4) = (1, 1)\), i.e. \((3, 2) = 1\cdot(2, 1) + 1\cdot(1, 1)\). So \(A^{10}(3, 2) = 4^{10}(2, 1) + 3^{10}(1, 1) = (2\,097\,152 + 59\,049,\ 1\,048\,576 + 59\,049) = (2\,156\,201,\ 1\,107\,625)\). And \((1, 1)\) is itself the eigenvector \(\mathbf{v}_2\), so \(A^{10}(1, 1) = 3^{10}(1, 1) = (59\,049,\ 59\,049)\).

  4. A rotation by \(30^\circ\) turns every nonzero vector by \(30^\circ\), so no vector stays on its own line, and there is no real eigenvector. Algebraically, trace \(2\cos 30^\circ = \sqrt{3}\) and determinant 1 give \(\lambda^2 - \sqrt{3}\lambda + 1 = 0\), with discriminant \(3 - 4 < 0\). The eigenvalues are \(\cos 30^\circ \pm i\sin 30^\circ = e^{\pm i\pi/6}\): complex numbers of size 1 at angle \(\pm 30^\circ\), encoding "rotate by 30°, no scaling".

  5. Steady state: \(-0.2x + 0.3y = 0\), so \(x = 1.5y\); with \(x + y = 1\), \(y = 0.4\), \(x = 0.6\). Steady state \((0.6, 0.4)\). Trace 1.5, so the second eigenvalue is \(1.5 - 1 = 0.5\) (check: determinant \(0.56 - 0.06 = 0.5 = 1 \times 0.5\) ✓). The distance from equilibrium halves every step.

  6. Write the start as a combination of eigenvectors; after \(k\) steps each component is multiplied by \(\lambda_i^k\), and the dominant one outgrows the rest, so any start with a nonzero component along the dominant eigenvector converges to it (a random start almost surely has one). Convergence is slow when \(|\lambda_2/\lambda_1|\) is close to 1, i.e. when the two largest eigenvalues are nearly equal in size; it fails if they’re equal in size.

  7. The covariance is already diagonal, so the principal components are the axes: PC1 = \(\mathbf{e}_1\) (variance 4, sd 2), PC2 = \(\mathbf{e}_2\) (variance 1, sd 1). PC1 explains \(4/5 = 80\%\). The cloud is an axis-aligned ellipse, twice as wide (horizontally) as it is tall, with the two features uncorrelated.

  8. \(V^\mathsf{T}\) rotates (or reflects) the input so the special perpendicular input directions line up with the axes; \(\Sigma\) stretches each axis by a singular value (and may change dimension); \(U\) rotates the result into place. The matrix equals \(\sum\sigma_i\mathbf{u}_i\mathbf{v}_i^\mathsf{T}\) with terms in decreasing importance; keeping the top \(k\) gives the best rank-\(k\) approximation, stored in \(k(m + n + 1)\) numbers instead of \(mn\), with error determined by the discarded singular values.