Monocurl Essays
Series Download

Eigenvectors: the directions a transformation leaves alone

Most arrows are knocked off their line by a matrix. The few that are only stretched are its eigenvectors, and they turn matrix powers into powers of numbers.

23 min·Early college·

AssumesA matrix is a transformation of spaceThe determinant is how much area gets scaled

Five arrows under the matrix with columns $(3, 0)$ and $(1, 2)$. Each dashed line is the line an arrow started on. Three of the arrows end up on their own line, only stretched, and turn green; the other two are pushed off theirs and turn red.
Monocurl source
let Go = |t| [
    Fan(Fd, blend(IDENT, A6, t), FAN),
    MatBox([RX, 1.0, 0], blend(IDENT, A6, t), 1)
]
mesh ground = Ground(Fd)
mesh go = Go(t: 0)
mesh legend = FanLegend

slide "apply"
    play Wait(0.9)
    go.t = 1
    play Lerp(4, [&go])
    play Wait(2)

Uses the shared definitions for this lesson's figures.

Apply the matrix

$$ A = \begin{pmatrix} 3 & 1 \\ 0 & 2 \end{pmatrix} $$

to an arrow, and the arrow usually changes direction. The arrow $(0, 1)$ goes to $(1, 2)$, which points somewhere else entirely. The arrow $(1, 1)$ goes to $(4, 2)$, again off its original line. You would expect this of every arrow: a matrix stretches and slants the plane, so why would any direction survive? Yet in the animation the arrow $(-1, 1)$ goes to $(-2, 2)$, exactly twice itself, and $(1, 0)$ goes to $(3, 0)$, three times itself. These arrows are stretched and nothing else happens to them.

Arrows like that are rare, and they are the most useful thing to know about a matrix. Along such a direction the matrix acts like a plain number, and questions that are hard for matrices, such as “what does applying $A$ fifty times do?”, become questions about powers of numbers. We will find out how to locate these directions, when they exist, and how to use them.

Here is what I use from earlier. A $2 \times 2$ matrix is a transformation of the plane: $\begin{pmatrix} a & b \\ c & d \end{pmatrix}$ sends $(x, y)$ to $(ax + by,\ cx + dy)$, and the map is linear, so $A(\mathbf{u} + \mathbf{v}) = A\mathbf{u} + A\mathbf{v}$ and $A(k\mathbf{v}) = k\,A\mathbf{v}$. The determinant $ad - bc$ is the factor by which the matrix scales areas, and it is zero exactly when the two columns are parallel and the plane is flattened onto a line or a point. $I$ is the identity matrix, which leaves every vector alone.

Hunting for the special directions

Let us look for the special arrows of $A$ systematically. Take an arrow $\mathbf{v}$ of fixed length, swing it around the origin, and at each position draw $A\mathbf{v}$ next to it. The quantity to watch is the angle between $\mathbf{v}$ and $A\mathbf{v}$. When it is zero, $A\mathbf{v}$ lies along $\mathbf{v}$.

Pause and try

Before watching: compute $A\mathbf{v}$ for $\mathbf{v} = (1, 0)$, $(2, 1)$, $(1, -1)$ and $(-3, 3)$. For which of these is $A\mathbf{v}$ a multiple of $\mathbf{v}$, and what is the multiple?

Show answerHide answer

$A(1, 0) = (3, 0) = 3\,(1, 0)$. $A(2, 1) = (7, 2)$, which is not a multiple of $(2, 1)$ since $7/2 \neq 2/1$. $A(1, -1) = (3 - 1, -2) = (2, -2) = 2\,(1, -1)$. $A(-3, 3) = (-9 + 3, 6) = (-6, 6) = 2\,(-3, 3)$. So three of the four are only stretched, and the last two, which lie on the same line through the origin, are stretched by the same factor 2.

The blue arrow $\mathbf{v}$ swings around the origin and the orange arrow is $A\mathbf{v}$. The number is the angle between them. It drops to 0 when $\mathbf{v}$ reaches the direction of $(-1, 1)$ and again at the direction of $(-1, 0)$; those two lines are marked in green as they are found.
Monocurl source
let Probe = |deg| {
    let v = rot2([1.2, 0], deg)
    let w = apply(A6, v)
    let c = clamp(-1, (v[0] * w[0] + v[1] * w[1]) / (mag(v) * mag(w)), 1)
    let ang = arccos(c) * 180 / PI
    var col = EMBER
    var ncol = INK
    if (ang < 0.6) {
        col = LEAF
        ncol = LEAF
    }
    return [
        dashed{[0.1, 0.08]} Cut(Cd, times(-9, v), times(9, v), SLATE, 2),
        Vec(Cd, [0, 0], w, col),
        Vec(Cd, [0, 0], v, AZURE),
        center{[RX, -0.35, 0]} scale{1.5} color{ncol} Number(ang, 1)
    ]
}
mesh ground = Ground(Cd)
mesh found = []
mesh probe = Probe(deg: 60)
mesh label = [
    center{[RX, 1.05, 0]} Note("angle between", 0.74),
    center{[RX, 0.6, 0]} Math("\mathbf{v} \text{ and } A\mathbf{v}", 0.95, INK),
    center{[RX, -0.95, 0]} Note("degrees", 0.72)
]

slide "swing"
    play Wait(0.6)
    probe.deg = 135
    play Lerp(2.6, [&probe])
    found = [Cut(Cd, [4, -4], [-4, 4], LEAF, 4)]
    play Fade(0.4, [&found])
    play Wait(0.8)
    probe.deg = 180
    play Lerp(2, [&probe])
    found .= Cut(Cd, [-4, 0], [4, 0], LEAF, 4)
    play Fade(0.4, [&found])
    play Wait(0.8)
    probe.deg = 245
    play Lerp(2.2, [&probe])
    play Wait(1.4)

Uses the shared definitions for this lesson's figures.

In half a turn the angle reaches zero exactly twice: on the line through $(-1, 1)$, where $A$ doubles every arrow, and on the $x$ axis, where $A$ triples every arrow. (The other half turn passes through the same two lines.) Everywhere else $A\mathbf{v}$ leaves the line of $\mathbf{v}$.

The clause $\mathbf{v} \neq \mathbf{0}$ is there because $A\mathbf{0} = \lambda\mathbf{0}$ for every number $\lambda$, so the zero vector would tell us nothing. The eigenvalue, on the other hand, may be any number. If $\lambda$ is negative the arrow is flipped to the other side of the origin, still on its line. If $\lambda = 0$ the arrow is sent to $\mathbf{0}$.

Eigenvectors come in whole lines. If $A\mathbf{v} = \lambda\mathbf{v}$ and $k \neq 0$, then $A(k\mathbf{v}) = k\,A\mathbf{v} = k\lambda\mathbf{v} = \lambda(k\mathbf{v})$, so $k\mathbf{v}$ is an eigenvector for the same $\lambda$. That is why $(1, -1)$ and $(-3, 3)$ shared the factor 2 above. It is the line that matters, and I will call it an eigenline. (“Eigen” is German for “own”: these are the matrix’s own directions.) For our matrix we have found the eigenvalue 3 with eigenline the $x$ axis, and the eigenvalue 2 with eigenline along $(-1, 1)$.

Finding eigenvalues without searching

Swinging an arrow around is not a method. For a matrix whose eigenlines are at awkward angles we would never hit them exactly, and we could not be sure we had found them all. We need an equation.

The trouble with $A\mathbf{v} = \lambda\mathbf{v}$ is that it has two unknowns of different kinds, the number $\lambda$ and the vector $\mathbf{v}$. Move everything to one side. Since $\lambda\mathbf{v} = \lambda I\mathbf{v}$, the equation says

$$ (A - \lambda I)\,\mathbf{v} = \mathbf{0}. $$

Read this as a statement about the matrix $A - \lambda I$, which is $A$ with $\lambda$ subtracted from both diagonal entries. It says that this matrix sends some nonzero vector to the origin. Most matrices do not do that. A matrix that sends a nonzero vector to $\mathbf{0}$ has crushed a whole line onto a point, so it flattens the plane, and flattening is what a zero determinant detects. So we can look for $\lambda$ first, with no $\mathbf{v}$ in sight: find the values of $\lambda$ for which $A - \lambda I$ flattens the plane.

The transformation $A - \lambda I$ as $\lambda$ rises from 1 to 3.6, with the image of the unit square shaded and its signed area, the determinant, shown on the right. The grid collapses onto a line twice, at $\lambda = 2$ and at $\lambda = 3$, and those are the eigenvalues of $A$.
Monocurl source
let Squash = |lam| {
    let m = [A6[0] - lam, A6[1], A6[2], A6[3] - lam]
    let d = clean(det(m))
    var col = AZURE
    if (abs(d) < 0.015) {
        col = LEAF
    }
    return [
        Lines(B2, m),
        Para(B2, m, col),
        Vec(B2, [0, 0], [m[1], m[3]], EMBER),
        Vec(B2, [0, 0], [m[0], m[2]], AZURE),
        center{[RX, 1.15, 0]} scale{1.2} color{INK} Number(lam, 2),
        center{[RX, -0.1, 0]} scale{1.2} color{col} Number(d, 2)
    ]
}
mesh ground = Ground(B2)
mesh squash = Squash(lam: 1)
mesh label = [
    center{[RX, 1.65, 0]} Math("\lambda", 1.0, INK),
    center{[RX, 0.45, 0]} Math("\det(A - \lambda I)", 0.9, INK)
]
mesh found = []

slide "slide"
    play Wait(0.7)
    squash.lam = 2
    play Lerp(2.4, [&squash])
    found = [center{[RX, -0.95, 0]} Math("\lambda = 2", 0.95, LEAF)]
    play Fade(0.4, [&found])
    play Wait(0.7)
    squash.lam = 3
    play Lerp(2.4, [&squash])
    found .= center{[RX, -1.5, 0]} Math("\lambda = 3", 0.95, LEAF)
    play Fade(0.4, [&found])
    play Wait(0.7)
    squash.lam = 3.6
    play Lerp(1.6, [&squash])
    play Wait(1.4)

Uses the shared definitions for this lesson's figures.

For our matrix, $A - \lambda I = \begin{pmatrix} 3 - \lambda & 1 \\ 0 & 2 - \lambda \end{pmatrix}$ has determinant $(3 - \lambda)(2 - \lambda) - 1 \cdot 0$, which is zero exactly for $\lambda = 3$ and $\lambda = 2$. The search found both eigenvalues, and now we know there are no others. Here is the general statement, with the step “flattens the plane” made exact.

The quadratic $\lambda^2 - (a + d)\lambda + (ad - bc)$ is called the characteristic polynomial of $A$. Its coefficients are familiar: $ad - bc$ is the determinant, and $a + d$, the sum of the diagonal entries, is called the trace. Because a quadratic with roots $\lambda_1, \lambda_2$ factors as $(\lambda - \lambda_1)(\lambda - \lambda_2)$, the eigenvalues add up to the trace and multiply to the determinant. For our $A$: $3 + 2 = 5$ and $3 \cdot 2 = 6$. The second fact makes sense as geometry, since stretching by 3 along one line and by 2 along another multiplies areas by 6.

Once an eigenvalue is known, its eigenvectors are the nonzero solutions of $(A - \lambda I)\mathbf{v} = \mathbf{0}$, a pair of linear equations of which one is guaranteed to be a multiple of the other.

Three kinds of matrix

A quadratic equation with real coefficients has two different real roots, one repeated root, or no real roots. All three happen for matrices, and each has a clear geometric meaning. The quantity that decides is the discriminant of the characteristic polynomial, $(a + d)^2 - 4(ad - bc)$.

Two different real eigenvalues. This is the case of $A$ and $B$ above: two eigenlines with different stretch factors.

A repeated eigenvalue. Take the shear $S = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix}$, which slides each point sideways by its height.

Pause and try

Picture the shear $S$, which sends $(x, y)$ to $(x + y, y)$. Which arrows stay on their own line? Then confirm with the characteristic equation.

Show answerHide answer

An arrow on the $x$ axis has height 0 and does not move at all, so it is an eigenvector with eigenvalue 1. Any arrow with $y \neq 0$ keeps its height while its tip slides sideways by $y$, so its direction changes. Equation: the trace is 2 and the determinant is 1, so $\lambda^2 - 2\lambda + 1 = (\lambda - 1)^2 = 0$ and the only eigenvalue is 1. Then $S - I = \begin{pmatrix} 0 & 1 \\ 0 & 0 \end{pmatrix}$, and $(S - I)(x, y) = (y, 0) = \mathbf{0}$ forces $y = 0$: only the $x$ axis.

Five arrows under the shear with columns $(1, 0)$ and $(1, 1)$, each with its own dashed line. Only the arrow on the $x$ axis stays on its line; the other four are tipped off theirs. A shear has a single eigenline.
Monocurl source
let S = [1, 1, 0, 1]
# directions chosen so that no image lands on another arrow's dashed line
let SFAN = [[1, 0], [0, 1.4], [-1.4, 0.7], [0.7, -1.4], [1.4, 0.7]]
let Go = |t| [
    Fan(Fd, blend(IDENT, S, t), SFAN),
    MatBox([RX, 1.0, 0], blend(IDENT, S, t), 1)
]
mesh ground = Ground(Fd)
mesh go = Go(t: 0)
mesh legend = FanLegend

slide "shear"
    play Wait(0.9)
    go.t = 1
    play Lerp(3.5, [&go])
    play Wait(2)

Uses the shared definitions for this lesson's figures.

So a repeated eigenvalue can come with only one eigenline. It does not have to: the matrix $2I = \begin{pmatrix} 2 & 0 \\ 0 & 2 \end{pmatrix}$ has characteristic polynomial $(\lambda - 2)^2$ as well, and it doubles every vector, so every nonzero vector is an eigenvector. A repeated root tells you to look at $A - \lambda I$ directly.

No real eigenvalues. Take the quarter turn $R = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix}$, which sends $(x, y)$ to $(-y, x)$. A rotation by an angle other than $0^\circ$ or $180^\circ$ turns every arrow off its line, so it should have no eigenvectors, and the algebra agrees: the trace is 0 and the determinant is 1, so the characteristic equation is $\lambda^2 + 1 = 0$, which no real number satisfies.

The five arrows of the opening figure under a rotation that grows from $0^\circ$ to $60^\circ$. Every arrow leaves its line at once. A rotation of the plane has no real eigenvectors.
Monocurl source
let Go = |deg| {
    let c = cos(deg * PI / 180)
    let s = sin(deg * PI / 180)
    return [
        Fan(Fd, [c, -s, s, c], FAN),
        MatBox([RX, 1.0, 0], [c, -s, s, c], 2, 0.82)
    ]
}
mesh ground = Ground(Fd)
mesh go = Go(deg: 0)
mesh legend = FanLegend

slide "turn"
    play Wait(0.9)
    go.deg = 60
    play Lerp(3.5, [&go])
    play Wait(2)

Uses the shared definitions for this lesson's figures.

The equation $\lambda^2 + 1 = 0$ does have solutions among the complex numbers, $\lambda = \pm i$, and in a fuller treatment these complex eigenvalues encode the rotation. In the real plane there is no arrow that a quarter turn leaves on its line, and that is the statement we need here.

Eigenvectors for different eigenvalues point in different directions

In the first case we found two eigenlines, and they were different lines. That was no accident, and the consequence is what makes eigenvectors usable.

Two vectors of which neither is a multiple of the other are called linearly independent, and the theorem says they form a basis of the plane: a pair of arrows out of which everything else can be mixed, just like $\mathbf{i}$ and $\mathbf{j}$. A basis made of eigenvectors is an eigenbasis. The shear shows that the hypothesis $\lambda_1 \neq \lambda_2$ cannot be dropped, since all its eigenvectors lie on one line and cannot reach a point off the $x$ axis.

Why eigenvectors make powers easy

Here is the payoff. Suppose $\mathbf{x} = \alpha\mathbf{v}_1 + \beta\mathbf{v}_2$ with $\mathbf{v}_1, \mathbf{v}_2$ eigenvectors. Applying $A$ once, by linearity,

$$ A\mathbf{x} = \alpha\,A\mathbf{v}_1 + \beta\,A\mathbf{v}_2 = \alpha\lambda_1\mathbf{v}_1 + \beta\lambda_2\mathbf{v}_2 . $$

The result has the same form with new coefficients, so applying $A$ again multiplies them by $\lambda_1$ and $\lambda_2$ once more.

Now a concrete model. A region has 900 thousand people, some in the city and some in the suburbs. Each year 10% of the city’s residents move to the suburbs and 20% of the suburbs’ residents move to the city. If $c$ and $s$ are this year’s populations in thousands, next year’s are $0.9c + 0.2s$ and $0.1c + 0.8s$:

$$ \begin{pmatrix} c \\ s \end{pmatrix} \longmapsto M\begin{pmatrix} c \\ s \end{pmatrix}, \qquad M = \begin{pmatrix} 0.9 & 0.2 \\ 0.1 & 0.8 \end{pmatrix}. $$

Start with 300 thousand in the city and 600 thousand in the suburbs. Where do things stand after 15 years? Done directly, that is fifteen matrix multiplications.

  1. Start at (300, 600). It is 300 (2, 1), the green arrow, minus 300 (1, -1), the purple arrow.
  2. One year multiplies the green piece by 1 and the purple piece by 0.7. The point moves to (390, 510).
  3. A second year shrinks the purple piece by 0.7 again: (453, 447).
  4. After 5 years the purple piece is 0.7 to the fifth power, about a sixth, of its first size.
  5. After 15 years less than half a percent of it is left. The populations are within 2 thousand of the eigenvector (600, 300).
The populations (city, suburbs) in thousands, year by year. The starting point is split into a green piece along the eigenvector $(2, 1)$ and a purple piece along the eigenvector $(1, -1)$. Each year the green piece is multiplied by 1 and the purple piece by 0.7, so the point slides toward the tip of the green arrow at $(600, 300)$.
Monocurl source
let Md = Board([-3.15, -1.42, 0], 0.44, 0, 9, 0, 7)
let pos = |n| [6 - 3 * 0.7 ^ n, 3 + 3 * 0.7 ^ n]
# whole numbers print without a decimal
let fmt = |v| {
    if (abs(v - round(v)) < 0.001) {
        return to_string(round(v), 0)
    }
    return to_string(v, 1)
}
let State = |n| [
    Vec(Md, [6, 3], pos(n), PLUM, 4.5, 0.17),
    Pt(P(Md, pos(n)), INK, 0.1)
]
let Line2 = |y, tex, col, s = 0.9| center{[2.45, y, 0]} Math(tex, s, col)
let Read = |n| {
    # 0.7^n is exact to three places only up to n = 3
    var rel = " = "
    if (n > 3) {
        rel = " \approx "
    }
    return [
    Line2(1.2, str_join(["\text{year } ", to_string(n)]), INK),
    Line2(0.55, str_join(["(", fmt(100 * pos(n)[0]), ",\ ", fmt(100 * pos(n)[1]), ")"]), INK),
    Line2(-0.35, str_join(["0.7^{", to_string(n), "}", rel, to_string(0.7 ^ n, 3)]), PLUM)
    ]
}
mesh ground = Ground(Md, 3, 100)
mesh names = [
    center{[0.2, -2.02, 0]} Note("city", 0.72),
    center{[-2.75, 1.92, 0]} Note("suburbs", 0.72)
]
mesh total = dashed{[0.1, 0.08]} Cut(Md, [9, 0], [2, 7], SLATE, 2)
mesh eigenline = Cut(Md, [0, 0], [18, 9], tint(LEAF, 0.5), 3)
mesh trail = []
mesh fixed = Vec(Md, [0, 0], [6, 3], LEAF, 4.5, 0.17)
mesh state = State(n: 0)
mesh read = Read(0)

slide "Start at (300, 600). It is 300 (2, 1), the green arrow, minus 300 (1, -1), the purple arrow."
    play Fade(0.5)

slide "One year multiplies the green piece by 1 and the purple piece by 0.7. The point moves to (390, 510)."
    read = []
    play Fade(0.3, [&read])
    trail .= Pt(P(Md, pos(0)), SLATE, 0.06)
    state.n = 1
    play Lerp(1.5, [&state])
    read = Read(1)
    play Fade(0.4, [&read, &trail])

slide "A second year shrinks the purple piece by 0.7 again: (453, 447)."
    read = []
    play Fade(0.3, [&read])
    trail .= Pt(P(Md, pos(1)), SLATE, 0.06)
    state.n = 2
    play Lerp(1.5, [&state])
    read = Read(2)
    play Fade(0.4, [&read, &trail])

slide "After 5 years the purple piece is 0.7 to the fifth power, about a sixth, of its first size."
    read = []
    play Fade(0.3, [&read])
    trail .= Pt(P(Md, pos(2)), SLATE, 0.06)
    state.n = 5
    play Lerp(1.8, [&state])
    read = Read(5)
    play Fade(0.4, [&read, &trail])

slide "After 15 years less than half a percent of it is left. The populations are within 2 thousand of the eigenvector (600, 300)."
    read = []
    play Fade(0.3, [&read])
    trail .= Pt(P(Md, pos(5)), SLATE, 0.06)
    state.n = 15
    play Lerp(1.8, [&state])
    read = Read(15)
    play Fade(0.4, [&read, &trail])

Uses the shared definitions for this lesson's figures.

The formula says more than the answer for year 15. Because $(0.7)^n$ shrinks toward 0, the second piece dies away and the population approaches $300\,(2, 1) = (600, 300)$: twice as many people in the city as in the suburbs. And that limit depends on the start only through the total.

Pause and try

Suppose instead that all 900 thousand people start in the suburbs. Without multiplying any matrices, find the populations after $n$ years and the long-run split.

Show answerHide answer

Write $(0, 900) = \alpha\,(2, 1) + \beta\,(1, -1)$: $2\alpha + \beta = 0$ and $\alpha - \beta = 900$, so $3\alpha = 900$, $\alpha = 300$, $\beta = -600$. Then $M^n(0, 900) = (600 - 600\,(0.7)^n,\ 300 + 600\,(0.7)^n)$. The long-run split is $(600, 300)$ again. The reason is that $\mathbf{v}_2 = (1, -1)$ has coordinates adding to 0, so the total of 900 has to be carried entirely by $\alpha\,(2, 1)$, which forces $\alpha = 300$ for every start with that total.

Nine different starting populations followed for 14 years under the migration matrix. Each point slides along a line of constant total toward the green eigenline along $(2, 1)$, where the city holds twice as many people as the suburbs.
Monocurl source
let Md = Board([-3.15, -1.42, 0], 0.44, 0, 9, 0, 7)
let STARTS = [[0.5, 2.5], [1, 5], [2, 7], [0.5, 6.5], [5, 0.5], [8, 1], [8.5, 3.5], [3, 0], [6, 6]]
let Flow = |n| block {
    let k = 0.7 ^ n
    for (q in STARTS) {
        let al = (q[0] + q[1]) / 3
        let be = (q[0] - 2 * q[1]) / 3
        let now = [2 * al + k * be, al - k * be]
        if (n > 0.02) {
            . Seg(P(Md, q), P(Md, now), tint(AZURE, 0.4), 2.5)
        }
        . Pt(P(Md, now), AZURE, 0.09)
    }
    . center{[2.45, 0.35, 0]} scale{1.5} color{INK} Number(floor(n + 0.001), 0)
}
mesh ground = Ground(Md, 3, 100)
mesh names = [
    center{[0.2, -2.02, 0]} Note("city", 0.72),
    center{[-2.75, 1.92, 0]} Note("suburbs", 0.72)
]
mesh eigenline = Cut(Md, [0, 0], [18, 9], LEAF, 3.5)
mesh flow = Flow(n: 0)
mesh label = [
    center{[2.45, 1.0, 0]} Note("years", 0.76),
    center{[2.45, -0.75, 0]} Math("\text{city} : \text{suburbs}", 0.85, LEAF),
    center{[2.45, -1.25, 0]} Math("\to 2 : 1", 0.95, LEAF)
]

slide "flow"
    play Wait(1)
    flow.n = 14
    play Lerp(6, [&flow], linear)
    play Wait(1.8)

Uses the shared definitions for this lesson's figures.

The same reasoning explains the long-run behavior of any matrix with an eigenbasis. In $\alpha\lambda_1^n\mathbf{v}_1 + \beta\lambda_2^n\mathbf{v}_2$, the term whose eigenvalue is larger in absolute value outgrows the other, so after many steps the result points almost along that eigenvector (provided its coefficient is not zero). For our first matrix $A$, with eigenvalues 3 and 2, the ratio of the two terms shrinks like $(2/3)^n$, and repeated application lines every arrow up with the $x$ axis, unless it started on the other eigenline. The Fibonacci numbers obey the same law: each step is one multiplication by a fixed matrix, and the ratio of consecutive terms settles at the larger eigenvalue of that matrix, the golden ratio. One of the problems below works a similar sequence with whole-number eigenvalues.

Two mistakes to avoid

What was hard here was that the defining equation $A\mathbf{v} = \lambda\mathbf{v}$ hides two unknowns in one line. The step that untangles them is to ask a question about $\lambda$ alone: for which numbers does $A - \lambda I$ flatten the plane? The determinant answers that, and the eigenvectors then come from a small linear system. The reusable idea is the change of viewpoint that follows. Described in the basis of its eigenvectors, a matrix only multiplies each coordinate by a number, and anything done repeatedly with the matrix becomes arithmetic with powers.

Check yourself

The matrix $C$ has characteristic equation $\lambda^2 - 4\lambda + 4 = 0$. What can you say about its eigenvectors?

Problems

The first three are computations, the next three use the eigenbasis, and the last two are proofs.

Problem 1

Let $D = \begin{pmatrix} 1 & 2 \\ 2 & -2 \end{pmatrix}$. Without solving any equation, decide which of $(2, 1)$, $(1, 1)$ and $(1, -2)$ are eigenvectors of $D$, and give the eigenvalues. Check your eigenvalues against the trace and determinant.

Show solutionHide solution

$D(2, 1) = (2 + 2, 4 - 2) = (4, 2) = 2\,(2, 1)$: an eigenvector with eigenvalue 2. $D(1, 1) = (3, 0)$, which is not a multiple of $(1, 1)$. $D(1, -2) = (1 - 4, 2 + 4) = (-3, 6) = -3\,(1, -2)$: an eigenvector with eigenvalue $-3$. Check: $2 + (-3) = -1$ is the trace $1 + (-2)$, and $2 \cdot (-3) = -6$ is the determinant $-2 - 4$.

Problem 2

Find the eigenvalues and eigenvectors of $\begin{pmatrix} 5 & -2 \\ 4 & -1 \end{pmatrix}$, and verify them.

Show solutionHide solution

Trace $4$, determinant $-5 + 8 = 3$: $\lambda^2 - 4\lambda + 3 = (\lambda - 1)(\lambda - 3) = 0$, so $\lambda = 1$ or $3$. For $\lambda = 1$: the matrix minus $I$ is $\begin{pmatrix} 4 & -2 \\ 4 & -2 \end{pmatrix}$, giving $4x - 2y = 0$, so $y = 2x$: multiples of $(1, 2)$. For $\lambda = 3$: $\begin{pmatrix} 2 & -2 \\ 4 & -4 \end{pmatrix}$ gives $x = y$: multiples of $(1, 1)$. Verify: the matrix sends $(1, 2)$ to $(5 - 4, 4 - 2) = (1, 2)$ and $(1, 1)$ to $(3, 3)$.

Problem 3

For each matrix, say how many real eigenvalues it has and how many eigenlines: (a) $\begin{pmatrix} 2 & 1 \\ 0 & 2 \end{pmatrix}$, (b) $\begin{pmatrix} 1 & -2 \\ 1 & 3 \end{pmatrix}$, (c) $\begin{pmatrix} 1 & 4 \\ 1 & 1 \end{pmatrix}$.

Show solutionHide solution

(a) Trace 4, determinant 4: $(\lambda - 2)^2 = 0$, one eigenvalue, 2. The matrix minus $2I$ is $\begin{pmatrix} 0 & 1 \\ 0 & 0 \end{pmatrix}$, which is not zero and sends $(x, y)$ to $(y, 0)$, so the eigenvectors have $y = 0$: one eigenline, the $x$ axis. (b) Trace 4, determinant $3 + 2 = 5$: $\lambda^2 - 4\lambda + 5 = 0$ has discriminant $16 - 20 = -4 < 0$. No real eigenvalues and no eigenlines. (c) Trace 2, determinant $1 - 4 = -3$: $\lambda^2 - 2\lambda - 3 = (\lambda - 3)(\lambda + 1) = 0$. Two eigenvalues, $3$ and $-1$, and two eigenlines: for $3$, $-2x + 4y = 0$ gives multiples of $(2, 1)$; for $-1$, $2x + 4y = 0$ gives multiples of $(2, -1)$.

Problem 4

The matrix $F = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}$ reflects the plane across the line $y = x$. Predict its eigenvalues and eigenlines from the geometry, then confirm with the characteristic equation.

Show solutionHide solution

A reflection leaves every point of its mirror line where it is, so the line $y = x$ is an eigenline with eigenvalue 1. An arrow perpendicular to the mirror is sent to its opposite, so the line along $(1, -1)$ is an eigenline with eigenvalue $-1$. Every other arrow lands on the other side of the mirror, off its line. Algebra: trace 0, determinant $-1$, so $\lambda^2 - 1 = 0$ and $\lambda = \pm 1$. Check: $F(1, 1) = (1, 1)$ and $F(1, -1) = (-1, 1) = -(1, -1)$.

Problem 5

For $A = \begin{pmatrix} 3 & 1 \\ 0 & 2 \end{pmatrix}$ and $\mathbf{x} = (1, 1)$, compute $A^5\mathbf{x}$ using the eigenvectors $(1, 0)$ and $(-1, 1)$. Then check by multiplying five times.

Show solutionHide solution

Write $(1, 1) = \alpha\,(1, 0) + \beta\,(-1, 1)$: the second coordinate gives $\beta = 1$, and then $\alpha - 1 = 1$ gives $\alpha = 2$. So $A^5\mathbf{x} = 2 \cdot 3^5\,(1, 0) + 1 \cdot 2^5\,(-1, 1) = (486 - 32,\ 32) = (454, 32)$. Directly, each step sends $(x, y)$ to $(3x + y, 2y)$: $(1, 1) \to (4, 2) \to (14, 4) \to (46, 8) \to (146, 16) \to (454, 32)$.

Problem 6

A sequence starts $x_0 = 1$, $x_1 = 1$ and continues by $x_{n+1} = x_n + 2x_{n-1}$, so it runs $1, 1, 3, 5, 11, 21, \dots$ (a) Check that the pair $(x_{n+1}, x_n)$ is obtained from $(x_n, x_{n-1})$ by the matrix $G = \begin{pmatrix} 1 & 2 \\ 1 & 0 \end{pmatrix}$. (b) Find the eigenvalues and eigenvectors of $G$. (c) Find a formula for $x_n$, and say what $x_{n+1}/x_n$ approaches.

Show solutionHide solution

(a) $G(x_n, x_{n-1}) = (x_n + 2x_{n-1},\ x_n) = (x_{n+1}, x_n)$. (b) Trace 1, determinant $-2$: $\lambda^2 - \lambda - 2 = (\lambda - 2)(\lambda + 1) = 0$. For $\lambda = 2$: $G - 2I = \begin{pmatrix} -1 & 2 \\ 1 & -2 \end{pmatrix}$ gives $x = 2y$, eigenvector $(2, 1)$. For $\lambda = -1$: $G + I = \begin{pmatrix} 2 & 2 \\ 1 & 1 \end{pmatrix}$ gives $x = -y$, eigenvector $(1, -1)$. (c) The pair $(x_{n+1}, x_n)$ equals $G^n(x_1, x_0) = G^n(1, 1)$. Write $(1, 1) = \alpha\,(2, 1) + \beta\,(1, -1)$: $2\alpha + \beta = 1$ and $\alpha - \beta = 1$ give $\alpha = \frac23$, $\beta = -\frac13$. So

$$ (x_{n+1}, x_n) = \tfrac23 \cdot 2^n\,(2, 1) - \tfrac13\,(-1)^n\,(1, -1), $$

and reading the second coordinate, $x_n = \dfrac{2^{n+1} + (-1)^n}{3}$. Check: $x_5 = \frac{64 - 1}{3} = 21$. The term with $2^n$ dominates, so $x_{n+1}/x_n$ approaches the larger eigenvalue, 2. (For example $21/11 \approx 1.91$.)

Problem 7

Let $\mathbf{v}$ be an eigenvector of $A$ with eigenvalue $\lambda$. Prove: (a) $\mathbf{v}$ is an eigenvector of $A^2$ with eigenvalue $\lambda^2$; (b) if $A$ is invertible, then $\lambda \neq 0$ and $\mathbf{v}$ is an eigenvector of $A^{-1}$ with eigenvalue $1/\lambda$.

Show solutionHide solution

(a) $A^2\mathbf{v} = A(A\mathbf{v}) = A(\lambda\mathbf{v}) = \lambda\,A\mathbf{v} = \lambda^2\mathbf{v}$, and $\mathbf{v} \neq \mathbf{0}$. (b) If $\lambda$ were 0 we would have $A\mathbf{v} = \mathbf{0}$, and applying $A^{-1}$ would give $\mathbf{v} = A^{-1}\mathbf{0} = \mathbf{0}$, which is impossible for an eigenvector. So $\lambda \neq 0$. Now apply $A^{-1}$ to both sides of $A\mathbf{v} = \lambda\mathbf{v}$: $\mathbf{v} = \lambda\,A^{-1}\mathbf{v}$, and dividing by $\lambda$ gives $A^{-1}\mathbf{v} = \frac{1}{\lambda}\mathbf{v}$. Undoing a stretch by $\lambda$ along a line is a stretch by $1/\lambda$ along the same line.

Problem 8

A matrix is symmetric if it has the form $\begin{pmatrix} a & b \\ b & d \end{pmatrix}$. (a) Prove that a symmetric $2 \times 2$ matrix always has real eigenvalues. (b) The dot product of $(v_1, v_2)$ and $(w_1, w_2)$ is $v_1w_1 + v_2w_2$, and two nonzero vectors are perpendicular exactly when it is 0. Prove that for a symmetric matrix $A$, $(A\mathbf{v}) \cdot \mathbf{w} = \mathbf{v} \cdot (A\mathbf{w})$ for all $\mathbf{v}, \mathbf{w}$, and deduce that eigenvectors for two different eigenvalues are perpendicular.

Show solutionHide solution

(a) The discriminant of $\lambda^2 - (a + d)\lambda + (ad - b^2)$ is $(a + d)^2 - 4ad + 4b^2 = (a - d)^2 + 4b^2$, a sum of squares, so it is never negative and the roots are real. (b) $(A\mathbf{v}) \cdot \mathbf{w} = (av_1 + bv_2)w_1 + (bv_1 + dv_2)w_2 = a\,v_1w_1 + b\,(v_2w_1 + v_1w_2) + d\,v_2w_2$. This expression is unchanged when $\mathbf{v}$ and $\mathbf{w}$ are exchanged, so it also equals $(A\mathbf{w}) \cdot \mathbf{v} = \mathbf{v} \cdot (A\mathbf{w})$. Now let $A\mathbf{v}_1 = \lambda_1\mathbf{v}_1$ and $A\mathbf{v}_2 = \lambda_2\mathbf{v}_2$ with $\lambda_1 \neq \lambda_2$. Then $\lambda_1\,(\mathbf{v}_1 \cdot \mathbf{v}_2) = (A\mathbf{v}_1) \cdot \mathbf{v}_2 = \mathbf{v}_1 \cdot (A\mathbf{v}_2) = \lambda_2\,(\mathbf{v}_1 \cdot \mathbf{v}_2)$, so $(\lambda_1 - \lambda_2)(\mathbf{v}_1 \cdot \mathbf{v}_2) = 0$ and $\mathbf{v}_1 \cdot \mathbf{v}_2 = 0$. Problem 4 is an instance: $F$ is symmetric, and its eigenvectors $(1, 1)$ and $(1, -1)$ are perpendicular.

Shared definitions used by this lesson's figures
# house kit: prepended to every essay figure after the std imports

# palette. AZURE and EMBER are the two actors, LEAF marks an answer, ROSE a problem
let PAPER = [1, 1, 1, 1]
let INK = [0.08, 0.09, 0.11, 1]
let SLATE = [0.40, 0.44, 0.50, 1]
let MIST = [0.85, 0.86, 0.88, 1]
let AZURE = [0.13, 0.42, 0.68, 1]
let EMBER = [0.89, 0.42, 0.12, 1]
let LEAF = [0.13, 0.55, 0.36, 1]
let ROSE = [0.78, 0.22, 0.24, 1]
let PLUM = [0.47, 0.30, 0.66, 1]
let GOLD = [0.85, 0.62, 0.08, 1]

background = PAPER

# an opaque pastel of col; use for fills so overlaps do not darken
let tint = |col, amount = 0.18| [
    1 - (1 - col[0]) * amount,
    1 - (1 - col[1]) * amount,
    1 - (1 - col[2]) * amount,
    1
]

# text: Cap for labels, Note for quiet labels, Math for typeset maths
let Cap = |txt, size = 0.75, col = INK| color{col} Text(txt, size, "Charter")
let Note = |txt, size = 0.65| color{SLATE} Text(txt, size, "Charter")
let Math = |tex, size = 0.9, col = INK| color{col} Tex(tex, size)

# a paper rectangle to sit behind text that crosses lines
let Plate = |pos, size| center{pos} fill{PAPER} stroke{alpha{0} PAPER, 0} Rect(size)

# shapes with fill and stroke both set, so no stray black appears
let Pt = |pos, col = INK, r = 0.075| center{pos} fill{col} stroke{col, 1} Circle(r, 48)
let Ring = |pos, col = INK, r = 0.1, w = 3| center{pos} fill{PAPER} stroke{col, w} Circle(r, 48)
let Seg = |a, b, col = INK, w = 3| stroke{col, w} Line(a, b)
let Path = |pts, col = INK, w = 3| stroke{col, w} Polyline(pts)
let Region = |pts, col| fill{col} stroke{alpha{0} col, 0} Polygon(pts)
let Outline = |pts, col = INK, w = 3| fill{alpha{0} PAPER} stroke{col, w} Polygon(pts)
let Shape = |pts, col, amount = 0.18, w = 3| fill{tint(col, amount)} stroke{col, w} Polygon(pts)
let Disc = |pos, r, col, amount = 0.18, w = 3| center{pos} fill{tint(col, amount)} stroke{col, w} Circle(r, 96)
let Box = |pos, size, col, amount = 0.18, w = 3| center{pos} fill{tint(col, amount)} stroke{col, w} Rect(size)
# a line from a to b ending in a small solid arrowhead
let Tip = |a, b, col = INK, w = 3, head = 0.17| {
    let d = normalize([b[0] - a[0], b[1] - a[1], 0])
    let n = [-d[1], d[0], 0]
    let base = [b[0] - d[0] * head, b[1] - d[1] * head, 0]
    return [
        stroke{col, w} Line(a, base),
        fill{col} stroke{col, 1} Polygon([
            b,
            [base[0] + n[0] * head * 0.42, base[1] + n[1] * head * 0.42, 0],
            [base[0] - n[0] * head * 0.42, base[1] - n[1] * head * 0.42, 0]
        ])
    ]
}

# plots. a frame is [x0, x1, y0, y1, centre, width, height]: a data window and the scene box it fills
let Frame = |x0, x1, y0, y1, c = [0, 0, 0], w = 6.4, h = 3.6| [x0, x1, y0, y1, c, w, h]
let fx = |fr, x| fr[4][0] + (x - (fr[0] + fr[1]) / 2) * fr[5] / (fr[1] - fr[0])
let fy = |fr, y| fr[4][1] + (y - (fr[2] + fr[3]) / 2) * fr[6] / (fr[3] - fr[2])
let at = |fr, x, y| [fx(fr, x), fy(fr, y), 0]

# axes through the data origin (or the nearest edge of the window), with optional names
let Axes = |fr, x_name = "", y_name = "", col = SLATE| {
    let ox = clamp(fr[0], 0, fr[1])
    let oy = clamp(fr[2], 0, fr[3])
    var parts = [
        Tip(at(fr, fr[0], oy), [fx(fr, fr[1]) + 0.25, fy(fr, oy), 0], col, 2.5, 0.15),
        Tip(at(fr, ox, fr[2]), [fx(fr, ox), fy(fr, fr[3]) + 0.25, 0], col, 2.5, 0.15)
    ]
    if (x_name != "") {
        parts .= center{[fx(fr, fr[1]) + 0.5, fy(fr, oy), 0]} Math(x_name, 0.8, col)
    }
    if (y_name != "") {
        parts .= center{[fx(fr, ox), fy(fr, fr[3]) + 0.5, 0]} Math(y_name, 0.8, col)
    }
    return parts
}

# tick marks with numbers at the listed data values
let XTicks = |fr, xs, col = SLATE, places = 0| block {
    let oy = fy(fr, clamp(fr[2], 0, fr[3]))
    for (x in xs) {
        . Seg([fx(fr, x), oy - 0.07, 0], [fx(fr, x), oy + 0.07, 0], col, 2)
        . center{[fx(fr, x), oy - 0.33, 0]} Math(to_string(x, places), 0.72, col)
    }
}
let YTicks = |fr, ys, col = SLATE, places = 0| block {
    let ox = fx(fr, clamp(fr[0], 0, fr[1]))
    for (y in ys) {
        . Seg([ox - 0.07, fy(fr, y), 0], [ox + 0.07, fy(fr, y), 0], col, 2)
        . center{[ox - 0.36, fy(fr, y), 0]} Math(to_string(y, places), 0.72, col)
    }
}

# faint grid lines at the listed data values
let GridLines = |fr, xs, ys, col = MIST| block {
    for (x in xs) {
        . Seg(at(fr, x, fr[2]), at(fr, x, fr[3]), col, 1.5)
    }
    for (y in ys) {
        . Seg(at(fr, fr[0], y), at(fr, fr[1], y), col, 1.5)
    }
}

# the graph of f over [a, b], drawn in the frame. values are clamped to the window
let Graph = |fr, f, a, b, col = AZURE, w = 4, n = 160|
    stroke{col, w} Polyline(map(sample(a, b, n), |x| at(fr, x, clamp(fr[2], f(x), fr[3]))))

# the region between the graph of f and the x axis over [a, b]
let AreaUnder = |fr, f, a, b, col = AZURE, amount = 0.2, n = 80| {
    var pts = [at(fr, a, 0)]
    for (x in sample(a, b, n)) {
        pts .= at(fr, x, clamp(fr[2], f(x), fr[3]))
    }
    pts .= at(fr, b, 0)
    return fill{tint(col, amount)} stroke{alpha{0} col, 0} Polygon(pts)
}

# vectors are pairs [x, y]. a board is [o, u, x0, x1, y0, y1]: the scene point of the origin,
# the unit length, and the window of whole numbers that is drawn
let Board = |o, u, x0, x1, y0, y1| [o, u, x0, x1, y0, y1]
let P = |pl, v| [pl[0][0] + pl[1] * v[0], pl[0][1] + pl[1] * v[1], 0]
let add = |v, w| [v[0] + w[0], v[1] + w[1]]
let times = |c, v| [c * v[0], c * v[1]]
# matrices are [a, b, c, d] with rows (a b) and (c d), so the columns are (a, c) and (b, d)
let IDENT = [1, 0, 0, 1]
let apply = |m, v| [m[0] * v[0] + m[1] * v[1], m[2] * v[0] + m[3] * v[1]]
let blend = |m, n, t| [
    m[0] + (n[0] - m[0]) * t,
    m[1] + (n[1] - m[1]) * t,
    m[2] + (n[2] - m[2]) * t,
    m[3] + (n[3] - m[3]) * t
]
let GRIDC = tint(AZURE, 0.42)
# faint gridlines, both axes, and numbers along the bottom and left edges every `step`
let Ground = |pl, step = 1, mult = 1| {
    var out = []
    let lo = P(pl, [pl[2], pl[4]])
    let hi = P(pl, [pl[3], pl[5]])
    for (x in range(pl[2], pl[3] + 1)) {
        let sx = P(pl, [x, 0])[0]
        out .= Seg([sx, lo[1], 0], [sx, hi[1], 0], MIST, 1.5)
        if (mod(x, step) == 0) {
            out .= center{[sx, lo[1] - 0.27, 0]} Math(to_string(x * mult), 0.72, SLATE)
        }
    }
    for (y in range(pl[4], pl[5] + 1)) {
        let sy = P(pl, [0, y])[1]
        out .= Seg([lo[0], sy, 0], [hi[0], sy, 0], MIST, 1.5)
        if (mod(y, step) == 0) {
            out .= center{[lo[0] - 0.38, sy, 0]} Math(to_string(y * mult), 0.72, SLATE)
        }
    }
    if (pl[4] <= 0 and pl[5] >= 0) {
        out .= Seg(P(pl, [pl[2], 0]), P(pl, [pl[3], 0]), SLATE, 2.5)
    }
    if (pl[2] <= 0 and pl[3] >= 0) {
        out .= Seg(P(pl, [0, pl[4]]), P(pl, [0, pl[5]]), SLATE, 2.5)
    }
    return out
}
# the part of the segment from scene point p to q that lies in the box lo..hi
let ClipSeg = |p, q, lo, hi, col, w| {
    let d = [q[0] - p[0], q[1] - p[1]]
    var t0 = 0
    var t1 = 1
    for (k in [0, 1]) {
        if (abs(d[k]) < 0.00001) {
            if (p[k] < lo[k] - 0.0001 or p[k] > hi[k] + 0.0001) {
                t1 = -1
            }
        }
        if (abs(d[k]) >= 0.00001) {
            let ta = (lo[k] - p[k]) / d[k]
            let tb = (hi[k] - p[k]) / d[k]
            t0 = max(t0, min(ta, tb))
            t1 = min(t1, max(ta, tb))
        }
    }
    if (t1 - t0 < 0.002) {
        return []
    }
    return stroke{col, w} Line(
        [p[0] + t0 * d[0], p[1] + t0 * d[1], 0],
        [p[0] + t1 * d[0], p[1] + t1 * d[1], 0]
    )
}
# a segment between two points of the board, cut off at the window
let Cut = |pl, a, b, col = SLATE, w = 2|
    ClipSeg(P(pl, a), P(pl, b), P(pl, [pl[2], pl[4]]), P(pl, [pl[3], pl[5]]), col, w)
# the whole-number grid after the matrix m: lines along the first column in col_a, along the second in col_b
let Lines = |pl, m, col_a = GRIDC, col_b = GRIDC, n = 8| block {
    for (k in range(-n, n + 1)) {
        var w = 1.8
        if (k == 0) {
            w = 3
        }
        . Cut(pl, apply(m, [-n, k]), apply(m, [n, k]), col_a, w)
        . Cut(pl, apply(m, [k, -n]), apply(m, [k, n]), col_b, w)
    }
}
# an arrow between two points of the board
let Vec = |pl, a, b, col = INK, w = 4.5, head = 0.2| {
    let p = P(pl, a)
    let q = P(pl, b)
    if (sqrt((q[0] - p[0]) ^ 2 + (q[1] - p[1]) ^ 2) < head * 1.3) {
        return Seg(p, q, col, w)
    }
    return Tip(p, q, col, w, head)
}
# transform the grid: the matrix is t of the way from m0 to m1; the first basis arrow is blue, the second orange
let Warp = |pl, m0, m1, t| {
    let m = blend(m0, m1, t)
    return [
        Lines(pl, m),
        Vec(pl, [0, 0], [m[0], m[2]], AZURE),
        Vec(pl, [0, 0], [m[1], m[3]], EMBER)
    ]
}
let Dash = |a, b, col = SLATE, w = 2.5| stroke{col, w} dashed{[0.1, 0.08]} Line(a, b)
let clean = |x| {
    if (abs(x) < 0.005) {
        return 0
    }
    return x
}
# a 2x2 matrix readout with brackets, first column blue and second orange
let MatBox = |c, m, places = 1, s = 0.9| {
    let dx = 0.5
    let dy = 0.3
    let bx = 1.0
    let by = 0.62
    let lip = 0.13
    let q = |x, y| [c[0] + x, c[1] + y, 0]
    return [
        stroke{INK, 2.5} Polyline([q(-bx + lip, by), q(-bx, by), q(-bx, -by), q(-bx + lip, -by)]),
        stroke{INK, 2.5} Polyline([q(bx - lip, by), q(bx, by), q(bx, -by), q(bx - lip, -by)]),
        center{q(-dx, dy)} scale{s} color{AZURE} Number(clean(m[0]), places),
        center{q(dx, dy)} scale{s} color{EMBER} Number(clean(m[1]), places),
        center{q(-dx, -dy)} scale{s} color{AZURE} Number(clean(m[2]), places),
        center{q(dx, -dy)} scale{s} color{EMBER} Number(clean(m[3]), places)
    ]
}
let matmul = |m, n| [
    m[0] * n[0] + m[1] * n[2],
    m[0] * n[1] + m[1] * n[3],
    m[2] * n[0] + m[3] * n[2],
    m[2] * n[1] + m[3] * n[3]
]
# an F, so that turning and mirroring can be told apart
let FPTS = [[0.4, 0.3], [0.8, 0.3], [0.8, 1.1], [1.3, 1.1], [1.3, 1.5], [0.8, 1.5], [0.8, 1.9], [1.6, 1.9], [1.6, 2.3], [0.4, 2.3]]
let FShape = |pl, m, col = LEAF| Shape(map(FPTS, |p| P(pl, apply(m, p))), col, 0.22, 2.5)
let det = |m| m[0] * m[3] - m[1] * m[2]
# lesson 6: eigenvectors
let cross2 = |v, w| v[0] * w[1] - v[1] * w[0]
let mag = |v| sqrt(v[0] * v[0] + v[1] * v[1])
let rot2 = |v, deg| {
    let t = deg * PI / 180
    return [v[0] * cos(t) - v[1] * sin(t), v[0] * sin(t) + v[1] * cos(t)]
}
let A6 = [3, 1, 0, 2]
let FAN = [[1, 0], [1, 1], [0, 1], [-1, 1], [1, -1]]
let Fd = Board([-1.54, -0.36, 0], 0.62, -3, 5, -2, 3)
let Cd = Board([-1.15, 0, 0], 0.55, -4, 4, -3, 3)
let B2 = Board([-1.94, -0.24, 0], 0.68, -2, 5, -2, 3)
let RX = 2.68
# the grid after m, the original line of each arrow (dashed), and the image of each arrow:
# green if it is still on its own line, red if it has left it
let Fan = |pl, m, vs| block {
    . Lines(pl, m)
    for (v in vs) {
        . dashed{[0.1, 0.08]} Cut(pl, times(-9, v), times(9, v), INK, 2)
    }
    for (v in vs) {
        let w = apply(m, v)
        var col = LEAF
        if (abs(cross2(v, w)) > 0.03) {
            col = ROSE
        }
        . Vec(pl, [0, 0], w, col)
    }
}
let FanLegend = [
    center{[RX, -0.55, 0]} Cap("stays on its line", 0.74, LEAF),
    center{[RX, -1.05, 0]} Cap("leaves its line", 0.74, ROSE)
]
let Para = |pl, m, col = LEAF| Shape([
    P(pl, [0, 0]),
    P(pl, [m[0], m[2]]),
    P(pl, [m[0] + m[1], m[2] + m[3]]),
    P(pl, [m[1], m[3]])
], col, 0.3, 2.5)