Matrix Decompositions: LU, QR, Cholesky
Splitting one matrix into two triangular pieces turns slow elimination into fast substitution, and the three standard splits cover general, rectangular and symmetric matrices.
Why Does This Exist?
Fitting a regression, updating a Kalman filter, taking a Newton step and evaluating a Gaussian likelihood all end in the same place: solve for , given a square matrix and a vector . Inverting directly costs about flops and squares the condition number, so the computed inverse is less accurate than the factors it came from. Nobody inverts.
Factoring once and substituting twice is the replacement. Elimination on costs about flops, and each later solve against a new costs only about . Ten right-hand sides therefore cost little more than one. This page covers the three factorizations and when each applies; linear systems and least squares covers how the system is set up, and determinants covers what the pivots say about it in determinants, inverses and trace.
The one idea you need first
A triangular system needs no elimination. An upper triangular solves bottom-up: , then each row above uses the values already found. A lower triangular solves top-down the same way. Each pass costs about flops. The whole point of every decomposition below is to convert the hard problem into two easy triangular ones.
Think of It Like This
Disassembling a wardrobe into labelled stacks
You need to move a built wardrobe through a narrow door, so you take it apart. Two stacks result. One stack holds every panel in the order it came off, each labelled with which screws held it and where: that is , the record of the elimination steps as multipliers. The other stack is the bare frame with a staircase profile, widest at the bottom step: that is , the staircase shape elimination leaves behind. The screws themselves sit on the diagonal of each stack, one per panel: those are the pivots. If a screw strips (a zero pivot), you reorder the panels before continuing: that is row pivoting. Reassembling in reverse order solves the system.
Where it stops: wardrobe panels are exact while matrix factors round every arithmetic step, and rounding piles up exactly where the condition number says it will. The analogy also hides the rectangular case: QR handles wardrobes with more panels than frame slots, which has no furniture equivalent.
How It Actually Works
LU records elimination. Eliminating from rows through uses multipliers , stored below the diagonal of with ones on its diagonal. Repeating down the columns gives : unit lower triangular holding every multiplier, upper triangular holding the staircase. Solving becomes (forward substitution) then (backward substitution). The determinant falls out free: is the product of the diagonal of , up to the sign of the row swaps.
Pivoting fixes zero and tiny pivots. If is zero, the multiplier divides by zero and elimination halts even when is perfectly invertible. Partial pivoting swaps in the largest entry below before each column, recorded in a permutation with . The symptom that you skipped it is a division by zero on a matrix you know is nonsingular, or garbage answers from a tiny pivot amplifying rounding. Always pivot; unpivoted LU is a teaching tool, not a solver.
QR handles rectangles and least squares
When is with , there is no inverse and LU does not apply. QR writes with holding orthonormal columns () and upper triangular. Least squares becomes , one triangular solve, with no squaring of the condition number the way forming would. Householder reflections build it in about flops. Cost rule of thumb: QR costs roughly twice LU on a square matrix, and buys stability for exactly the problems LU cannot touch.
Cholesky halves the work for symmetric positive definite matrices
If is symmetric () with all eigenvalues positive, it factors as : one lower triangular with positive diagonal, times its own transpose. Derivation per entry: , and each row below uses only values already computed. Cost is about flops, half of LU, with half the storage. Covariance matrices and the of ridge regression are the textbook cases. If is not positive definite, some radicand goes negative and the algorithm stops: that failure is diagnostic, not a bug to patch over.
Worked example
Take with .
Eliminate with multiplier . Row 2 becomes . So:
Direct check: . Forward substitution : , then gives . Backward substitution : gives , then gives . Verify: and .
The companions on small numbers: has orthonormal columns up to , so and with . And gives , , . Check the product: and .
Code
The point is the check: multiply the factors back and confirm the solve leaves no residual. Every # -> value below is the printed result.
import numpy as np
A = np.array([[4.0, 3.0], [6.0, 3.0]])L = np.array([[1.0, 0.0], [1.5, 1.0]])U = np.array([[4.0, 3.0], [0.0, -1.5]])b = np.array([7.0, 9.0])
print(np.allclose(L @ U, A)) # -> Trueprint(round(np.linalg.det(A), 6)) # -> -6.0
y = np.linalg.solve(L, b) # forward leg, L has unit diagonalx = np.linalg.solve(U, y) # backward legprint(np.round(x, 6)) # -> [1. 1.]print(np.round(A @ x - b, 10)) # -> [0. 0.]
C = np.array([[4.0, 2.0], [2.0, 3.0]])Lc = np.linalg.cholesky(C)print(np.round(Lc, 4)) # -> [[2. 0. ] [1. 1.4142]]print(np.allclose(Lc @ Lc.T, C)) # -> TrueReplacing the hand-derived and with scipy.linalg.lu returns the same factors up to the row-swap convention; the residual stays zero either way.
Watch Out For
Running LU with no pivoting on a matrix with a zero leading entry
The matrix above with its rows swapped, , is perfectly invertible, yet unpivoted elimination divides by on the first step and halts. The more insidious version is a tiny but nonzero pivot: no crash, just answers wrong by orders of magnitude because one huge multiplier amplifies every rounding error below it.
The fix is partial pivoting (), which every serious routine does by default: scipy.linalg.lu returns the permutation, and numpy.linalg.solve pivots internally. If your hand-rolled elimination hits a zero pivot, swap in the largest entry below before dividing, and record the swap. Never special-case the zero away.
Calling Cholesky on a matrix that is not positive definite
Pass a merely symmetric matrix such as (eigenvalues and ) and the algorithm reaches and raises LinAlgError. The usual cause is forming a covariance from uncentered or rank-deficient data, or rounding that pushed a barely positive eigenvalue negative.
Check before factoring: symmetry up to tolerance, then either an eigenvalue test or simply attempt the factorization and treat failure as the diagnosis. If it fails, the matrix was never Cholesky-eligible: fall back to LU with pivoting for square systems or QR for least squares, and fix the modelling (add the ridge diagonal , center the data) rather than clipping the negative radicand to zero.
The Quick Version
- Factor once at flops, then solve each new right-hand side at : that is the whole economy.
- stores elimination multipliers in unit and the staircase in ; pivoting () is mandatory in practice.
- The worked gives with , with , determinant , and solution against .
- Determinant is the product of 's diagonal up to row-swap signs; triangular solves run forward then backward.
- covers rectangular and least squares without squaring the condition number, at about twice the cost of LU.
- halves cost and storage for symmetric positive definite ; the worked holds , and .
- Never invert explicitly, and never run unpivoted LU or Cholesky outside its eligibility.