39. Schur decomposition

📖 Reading · 9 min
💡 Every code box below is live — edit it and hit Run.

§19.4 handled defective matrices with Jordan form — powerful, but numerically fragile (computing generalized eigenvectors is ill-conditioned, §22.2 explains why). This lesson gives the decomposition numerical software actually uses instead: every square matrix, defective or not, can be triangularized by an orthogonal change of basis — no exceptions, and no ill-conditioned steps required.

The theorem

Theorem (Schur). Every square matrix A (real or complex) can be written A=QTQ^*, where Q is unitary and T is upper triangular, with A's eigenvalues appearing on T's diagonal.

For real A with all-real eigenvalues, Q can be taken real orthogonal. (When some eigenvalues are complex, real Schur form uses 2\times2 blocks on the diagonal instead of single complex entries — not needed in detail here.)

This always exists — unlike diagonalization. §19.2's defective matrices have no full eigenbasis, so A=PDP^{-1} can fail; Schur only asks for triangular, not diagonal, which is a strictly weaker demand and holds unconditionally. §21.0's Spectral Theorem is the special case A symmetric: then T turns out diagonal too (a symmetric matrix similar to a triangular one is forced to be diagonal — check T=T^* from A=A^* transferring through Q, and a triangular matrix equal to its own conjugate transpose can only have entries on the diagonal), recovering A=Q\Lambda Q^* exactly.

Why triangular is exactly enough

Everything §19.3's similarity invariants care about reads directly off a triangular matrix: \operatorname{tr}A=\operatorname{tr}T=\sum T_{ii}, \det A=\det T=\prod T_{ii} (§16.4's triangular-determinant shortcut), and the diagonal entries are the eigenvalues with multiplicity — all without ever needing T to be fully diagonal. This is why Schur is the workhorse inside eigenvalue-finding algorithms themselves: the QR algorithm (used by numpy.linalg.eig internally) iterates toward a Schur form, then reads eigenvalues off its diagonal — it never attempts full diagonalization, sidestepping the defective-matrix problem entirely.

Doing it in Python

import numpy as np
import scipy.linalg as la

A = np.array([[3., 1.], [0., 3.]])   # defective (Section 19.4) -- no eigenbasis exists

T, Q = la.schur(A)
print("T =")
for row in T.tolist():
    print([round(v, 4) for v in row])
print("Q =")
for row in Q.tolist():
    print([round(v, 4) for v in row])

print("\nQ Q^T == I:", bool(np.allclose(Q @ Q.T, np.eye(2))))
print("Q T Q^T == A:", bool(np.allclose(Q @ T @ Q.T, A)))
print("diagonal of T (eigenvalues):", [round(v, 4) for v in np.diag(T).tolist()])
T =
[3.0, 1.0]
[0.0, 3.0]
Q =
[1.0, 0.0]
[0.0, 1.0]

Q Q^T == I: True
Q T Q^T == A: True
diagonal of T (eigenvalues): [3.0, 3.0]

Confirming Schur exists for a genuinely non-diagonalizable 3\times3 example, and reading trace/determinant off T:

import numpy as np
import scipy.linalg as la

A = np.array([[2., 1., 0.], [0., 2., 0.], [0., 0., 5.]])   # defective at eigenvalue 2

T, Q = la.schur(A)
diag = np.diag(T)
print("eigenvalues from T's diagonal:", [round(v, 4) for v in diag.tolist()])
print("sum == trace(A):", np.isclose(diag.sum(), np.trace(A)))
print("product == det(A):", np.isclose(np.prod(diag), np.linalg.det(A)))
eigenvalues from T's diagonal: [2.0, 2.0, 5.0]
sum == trace(A): True
product == det(A): True

Worked example

A=\begin{pmatrix}4&1\\0&4\end{pmatrix} is already upper triangular. Write down its Schur decomposition directly.

A is already triangular, so no rotation is needed at all: T=A, Q=I.

\boxed{A=IAI^T,\quad T=\begin{pmatrix}4&1\\0&4\end{pmatrix},\ Q=I}

Sanity check. Eigenvalues from T's diagonal: 4,4 (a repeated eigenvalue). This matches §19.4's exact example structurally: A-4I= \begin{pmatrix}0&1\\0&0\end{pmatrix} has rank 1, so \operatorname{gm}(4)=1<2=\operatorname{am}(4)defective, with no diagonalization possible — yet the Schur form exists trivially anyway, confirming the theorem's promise that triangularization never requires more than a matrix already provides.

Your turn

1. For a triangular matrix already, what are Q and T in its own Schur decomposition?

2. A matrix has Schur form with diagonal entries 2,-1,3. What are its trace and determinant?

3. True or false: the Schur form T of a non-symmetric matrix is generally not unique (different valid Q's can give different T's).

Solutions

1. Q=I, T=A — exactly the worked example's reasoning, general to any already-triangular matrix.

2. \operatorname{tr}A=2+(-1)+3=4. \det A=2(-1)(3)=-6 — both read directly off the diagonal, no further computation needed, per this lesson's "why triangular is exactly enough" section.

3. True. Unlike RREF (§16.1, always unique) or Cholesky (§22.0, unique with the positive-diagonal convention), Schur form is not unique in general — the order eigenvalues appear along the diagonal can be permuted by choosing a different Q, and for repeated eigenvalues there can be genuinely different valid upper-triangular forms. What is invariant is the multiset of diagonal entries (the eigenvalues themselves, by §19.3's similarity-invariance of the characteristic polynomial) — just not their arrangement or the specific off-diagonal values.

Check yourself in code

Compute the Schur decomposition of A=\begin{pmatrix}5&2\\0&1\end{pmatrix} (already triangular) and confirm QTQ^T=A.

Print exactly this:

T diagonal (eigenvalues): [5.0, 1.0]
Q T Q^T == A: True
import numpy as np
import scipy.linalg as la

A = np.array([[5., 2.], [0., 1.]])
T, Q = la.schur(A)
print("T diagonal (eigenvalues):", [round(v, 4) for v in np.diag(T).tolist()])
# print whether Q @ T @ Q.T reconstructs A
import numpy as np
import scipy.linalg as la

A = np.array([[5., 2.], [0., 1.]])
T, Q = la.schur(A)
print("T diagonal (eigenvalues):", [round(v, 4) for v in np.diag(T).tolist()])
print("Q T Q^T == A:", bool(np.allclose(Q @ T @ Q.T, A)))

Schur decomposition, A=QTQ^* with Q unitary and T upper triangular, exists for every square matrix without exception — strictly weaker than diagonalization but strong enough to read off trace, determinant, and every eigenvalue directly from T's diagonal. It's the Spectral Theorem's non-symmetric generalization, and the actual mechanism inside real eigenvalue-finding software.

Next: a single number attached to a matrix that measures how much it distorts space — matrix norms, and the condition number that decides whether a computation like Schur or LU can be trusted numerically at all.