This site is version 0.0.1. Most of the code on this site is drafted by an agent and needs review. I apologize for the AI-speak, we are working on rewriting it!
viv-lang.org

Notebooks · posts/jacobi-eigenvalue-sweeps

Jacobi's eigenvalue method

Jacobi's method diagonalizes a symmetric matrix by plane rotations, each of which zeroes one off-diagonal pair. On an 8 by 8 matrix the off-diagonal norm goes 1.33, 0.453, 0.0299, , over four sweeps, and after the fifth it is under , rounding error. The diagonal is then the spectrum, and it matches numpy.linalg.eigh to .

A symmetric matrix has an orthogonal matrix of eigenvectors with diagonal. Jacobi's method (1846) builds out of plane rotations. A rotation in the plane, through an angle set by , and (derived under "One rotation"), makes the entry zero; doing that for every pair once is a sweep. A later rotation can make an earlier zero nonzero again, so one sweep does not finish the job, but the size of the off-diagonal part, , falls with every rotation.

The cells below run it on a fixed 8 by 8 matrix and measure after each sweep. The reference is numpy: the same sweeps in Python and numpy.linalg.eigh on the same matrix (posts/refs/jacobi-eigenvalue-sweeps.py, output beside it).

Look inside this program

  • Atlas

    The program’s map: every function and which calls which.

  • There’s no recorded run of this program yet.

Shift+Enter in a cell runs it and moves to the next. Running a cell runs every cell above it too, as one program.

[1]lines 27–55

One rotation

Rotating the and rows and columns through changes only those two rows and columns. The new entry is

which is zero when . atan2 picks the angle with when , the smaller rotation, which disturbs the rest of the matrix least.

A rotation leaves the sum of squares of all entries unchanged, and it moves exactly of it from off the diagonal onto it. That is why can only fall. The cell checks both facts on the first rotation, .

[2]lines 75–119

Sweeps

A sweep rotates every pair in order, : 28 rotations for . The cell runs six sweeps and records after each.

While the off-diagonal part is large, a sweep removes a fixed fraction of it: the first two take it from 1.33 to 0.453 to 0.0299. Once it is small next to the gaps between the eigenvalues (here the smallest gap is 0.44), each sweep roughly squares it: and the next value is ; and the next is . The number of correct digits doubles each sweep, as it does for Newton's method, and the fifth sweep hits rounding: under , about times the size of the matrix itself. Through the fourth sweep every value agrees with the numpy twin, which does the same rotations in the same order, to ; after that both are rounding noise, here and in numpy.

[3]lines 141–216

The answer

After the sweeps the diagonal of holds the eigenvalues and the columns of the eigenvectors: is diagonal. The eigenvalues are checked against numpy.linalg.eigh, which uses a different algorithm (LAPACK's tridiagonal reduction), and the product is checked against the matrix we started from.

[4]lines 228–253