Why QR Decomposition Matters
Suppose three measurements describe a model with only two unknown coefficients. Noise can make the equations incompatible: there may be no vector $x$ for which $Ax=b$ exactly. We still want the coefficients that minimize the Euclidean error $\lVert Ax-b\rVert_2$. QR decomposition separates the geometry of the model's column space from a triangular system that is easy to solve.
The columns of $Q$ form perpendicular unit directions. The upper triangular matrix $R$ describes the original columns in those directions. This lesson connects three ways to obtain them: Gram–Schmidt removes projections, Householder reflects whole column tails, and Givens rotates two coordinates at a time. Our complete implementations use Householder reflections with explicit column pivoting, then solve a least-squares problem whose optimal residual is deliberately nonzero.
You will learn to distinguish a valid nonzero fitting error from an implementation error, trace the transformations, undo a column permutation correctly, and check both reconstruction and orthogonality. QR also appears in polynomial fitting, numerical optimization, orthogonal basis construction, and eigenvalue algorithms.
QR decomposition expresses a matrix using orthonormal directions and triangular coordinates. For a full-column-rank matrix, it turns least squares into projecting $b$ onto those directions and solving one triangular system, without forming $A^TA$.
Core Concepts and Terminology
For a real matrix $A\in\mathbb{R}^{m\times n}$ with $m\ge n$ and independent columns, the thin QR factorization is $A=QR$, where $Q\in\mathbb{R}^{m\times n}$, $Q^TQ=I_n$, and $R\in\mathbb{R}^{n\times n}$ is upper triangular. Thin $Q$ has orthonormal columns but is usually not square, so $QQ^T$ is a projection rather than the identity.
Column pivoting reorders the variables: write $AP=QR$. Our array $p$ means column $j$ of $AP$ is original column $p_j$ of $A$, or $P_{p_j,j}=1$. To solve, set $z=P^Tx$, calculate $c=Q^Tb$, solve $Rz=c$, and scatter the result back with $x_{p_j}=z_j$. Pivoting swaps columns, not equations or entries of $b$.
A full QR factorization instead uses an $m\times m$ orthogonal matrix $Q_{\mathrm{full}}$ and an $m\times n$ upper trapezoidal factor, whose last $m-n$ rows are zero. Its first $n$ columns are thin $Q$. The remaining columns span the perpendicular complement of the model's column space.
In exact arithmetic, the least-squares solution satisfies
$$ Ax=QQ^Tb, \qquad A^T(Ax-b)=0. $$
Thus the residual is perpendicular to every column of $A$. Its norm need not be zero. Full column rank makes the minimizer unique; this lesson's code rejects a column tail that is too small at the chosen tolerance instead of returning a rank-deficient or minimum-norm solution.
The LAPACK DGEQP3 reference documents the same $AP=QR$ convention and a representation of $Q$ as stored reflectors. Our teaching implementation explicitly constructs thin $Q$ and uses a simple rank-rejection policy.
| Term | Definition | Role or example |
|---|---|---|
| Orthonormal columns | $q_i^Tq_j=0$ when $i\ne j$, and $q_i^Tq_i=1$ | Coordinates along these directions do not mix |
| Thin $Q$ | An $m\times n$ matrix satisfying $Q^TQ=I_n$ | The sample has three rows and two columns |
| Upper triangular $R$ | $R_{ij}=0$ for $i\gt j$ | Back substitution solves $Rz=Q^Tb$ |
| Column ordering $p$ | Column $j$ of $AP$ equals column $p_j$ of $A$ | Sample $p=(1,0)$ moves the longer column first |
| Gram–Schmidt | Remove earlier projections from each new column, then normalize | Builds an orthonormal basis directly |
| Householder reflector | $H=I-2vv^T$ with $\lVert v\rVert_2=1$ | Orthogonal reflection clearing a whole column tail |
| Givens rotation | A two-coordinate orthogonal transformation | Clears one chosen entry while touching only two rows |
| Column pivot | Largest remaining active column-tail norm | Chooses a promising independent direction next |
| Least-squares residual | $e=Ax-b$ | The sample has $e=(-1,1,1)^T$ and $\lVert e\rVert_2=\sqrt3$ |
| Numerical rejection | Active norm divided by a fixed reference is at most $\tau$ | A policy decision, not an exact rank certificate |
How QR Decomposition Works, Step by Step
Gram–Schmidt: build directions from projections. For column $a_j$, classical Gram–Schmidt computes $r_{ij}=q_i^Ta_j$ for each earlier $i$, subtracts their combined projections, and normalizes the remainder:
$$ w=a_j-\sum_{i=0}^{j-1}r_{ij}q_i, \qquad r_{jj}=\lVert w\rVert_2, \qquad q_j=w/r_{jj}. $$
Modified Gram–Schmidt starts with $w=a_j$ and repeatedly computes $r_{ij}=q_i^Tw$ from the current remainder, followed immediately by $w\leftarrow w-r_{ij}q_i$. They agree in exact arithmetic but differ in rounding behavior; modified Gram–Schmidt usually preserves orthogonality better. Reorthogonalization makes an additional projection pass when needed. A zero or rejected remainder must be handled before normalization.
For the sample's original columns $(1,1,0)^T$ and $(2,0,2)^T$, unpivoted Gram–Schmidt gives $q_0=(1,1,0)^T/\sqrt2$, $r_{01}=\sqrt2$, remainder $(1,-1,2)^T$, and $q_1=(1,-1,2)^T/\sqrt6$, with $r_{11}=\sqrt6$. This is a valid unpivoted factorization; the pivoted Householder factors below use a different column order and signs.
Householder: reflect tails into coordinate axes. This is the algorithm implemented in the three panels:
- Validate a finite rectangular input with $m\ge n\ge1$. Set $s=\max_{i,j}\lvert A_{ij}\rvert$ and reject $s=0$. Work on $W=A/s$, preserving the input. Let $\rho$ be the largest initial column norm of $W$; keep it fixed.
- At stage $k$, recompute $\lVert W_{k:m-1,j}\rVert_2$ for each remaining column $j\ge k$. Select the largest, retaining the first on an exact tie. Reject if the largest norm divided by $\rho$ is at most $\tau$.
- Swap the chosen entire columns of $W$, including entries above row $k$, and update $p$. Previously computed rows of $R$ must follow their variables too.
- Extract the active column tail $w$. Choose $\alpha=-\lVert w\rVert_2$ if $w_0\ge0$, otherwise $\alpha=\lVert w\rVert_2$. Set $u=w-\alpha e_0$ and $v=u/\lVert u\rVert_2$. The opposite sign avoids cancellation in the first component of $u$.
- Apply $H_k=I-2vv^T$ to the active rows and remaining columns: for each column tail $t$, calculate $d=v^Tt$ and replace $t$ by $t-2vd$. Store the reflector, explicitly set the eliminated entries to zero, and set the pivot to $\alpha$.
- After all stages, extract the first $n$ rows and rescale them by $s$ to obtain $R$. Form thin $Q=H_0H_1\cdots H_{n-1}E$, where $E$ is the first $n$ columns of $I_m$. Applying the stored reflectors to $E$ requires processing them in reverse order, since each update multiplies on the left.
- For each right-hand side, calculate $c=Q^Tb$, back-substitute for $z$, then assign $x_{p_j}=z_j$. Verify the fit using the original $A$ and $b$.
Givens: rotate two coordinates. For entries $a$ and $b$ in a target column, set $r=\operatorname{hypot}(a,b)$, $c=a/r$, and $s_g=b/r$. When $r\ne0$,
$$ \begin{pmatrix}c&s_g\\-s_g&c\end{pmatrix} \begin{pmatrix}a\\b\end{pmatrix} =\begin{pmatrix}r\\0\end{pmatrix}. $$
For $(a,b)=(3,4)$, take $r=5$, $c=3/5$, and $s_g=4/5$. Apply that rotation to the same two rows of all affected columns, then continue clearing entries below the diagonal. If both entries are zero, use the identity rotation. The product of the transposed rotations builds $Q$. Givens is useful when changes or existing zeros make localized two-row work attractive.
flowchart TD
A["Validate A; scale; initialize column order"] --> B{"More columns?"}
B -->|Yes| C["Recompute active norms; choose column pivot"]
C --> D{"Norm passes tolerance?"}
D -->|No| E["Report rejected column rank"]
D -->|Yes| F["Swap whole columns and order entries"]
F --> G["Build stable reflector; clear tail; store reflector"]
G --> H["Advance column"]
H --> B
B -->|No| I["Rescale R; form thin Q using reverse reflector order"]
I --> J["Compute Q transpose b; back solve R z"]
J --> K["Scatter z into original variable order"]
K --> L["Check reconstruction, orthogonality, and fit stationarity"]
Worked Example with a Full Trace
Use
$$ A=\begin{pmatrix}1&2\\1&0\\0&2\end{pmatrix}, \qquad b=\begin{pmatrix}6\\0\\3\end{pmatrix}. $$
We show matrix states in original units for readability. The code scales by $s=2$ internally, so its working entries and pivot values are half of those displayed; normalized reflectors and pivot ratios are identical. Its fixed reference norm is $\rho=\sqrt2$, corresponding to the original maximum column norm $2\sqrt2$.
Stage $k=0$: choose a column. Original column norms are $\sqrt2$ and $2\sqrt2$. Choose column $1$ and swap it with column $0$, giving $p=(1,0)$ and
$$ AP=\begin{pmatrix}2&1\\0&1\\2&0\end{pmatrix}. $$
The active tail is $w=(2,0,2)^T$, with norm $2\sqrt2$. Its pivot ratio is $1$. Choose $\alpha_0=-2\sqrt2$. A normalized reflector vector and the resulting full reflector are
$$ v_0=\frac{(1+\sqrt2,0,1)^T}{\sqrt{4+2\sqrt2}}, \qquad H_0=\begin{pmatrix} -1/\sqrt2&0&-1/\sqrt2\\ 0&1&0\\ -1/\sqrt2&0&1/\sqrt2 \end{pmatrix}. $$
Applying it to both columns clears the first tail:
$$ W_1=H_0AP= \begin{pmatrix}-2\sqrt2&-1/\sqrt2\\0&1\\0&-1/\sqrt2\end{pmatrix}. $$
Stage $k=1$: clear the remaining tail. Only one column remains. Its active tail is $(1,-1/\sqrt2)^T$, with norm $\sqrt{3/2}=\sqrt6/2$. Relative to $2\sqrt2$, its pivot ratio is $\sqrt3/4\gt10^{-12}$. No further column swap occurs. Choose $\alpha_1=-\sqrt{3/2}$ and normalized active reflector vector
$$ v_1=\frac{(1+\sqrt{3/2},-1/\sqrt2)^T}{\sqrt{3+\sqrt6}}. $$
Embedded in three dimensions, its reflector is
$$ H_1=\begin{pmatrix} 1&0&0\\ 0&-\sqrt{2/3}&1/\sqrt3\\ 0&1/\sqrt3&\sqrt{2/3} \end{pmatrix}, \qquad W_2=H_1W_1= \begin{pmatrix}-2\sqrt2&-1/\sqrt2\\0&-\sqrt6/2\\0&0\end{pmatrix}. $$
Recover the thin factors. The top two rows of $W_2$ are $R$. The first two columns of $H_0H_1$ are $Q$:
$$ Q=\begin{pmatrix} -1/\sqrt2&-1/\sqrt6\\ 0&-2/\sqrt6\\ -1/\sqrt2&1/\sqrt6 \end{pmatrix}, \qquad R=\begin{pmatrix}-2\sqrt2&-1/\sqrt2\\0&-\sqrt6/2\end{pmatrix}. $$
The column norms of $Q$ are $1$, and their dot product is $1/\sqrt{12}-1/\sqrt{12}=0$. Multiplication gives $QR=AP$, including the original column $(1,1,0)^T$ as the second product column.
Transform the measurements. The first reflection gives $H_0b=(-9/\sqrt2,0,-3/\sqrt2)^T$. The second gives $H_1H_0b=(-9/\sqrt2,-3/\sqrt6,-\sqrt3)^T$. Its first two entries are $c=Q^Tb$; the last entry is the irreducible perpendicular component.
Back-substitute and restore variables. Solve $Rz=c$. The last equation gives $z_1=1$. The first then gives $z_0=2$. These are in pivoted variable order: $x_{p_0}=x_1=z_0=2$ and $x_{p_1}=x_0=z_1=1$. Thus $x=(1,2)^T$.
Check the fit. We obtain $Ax=(5,1,4)^T$ and $e=Ax-b=(-1,1,1)^T$. Therefore $\lVert e\rVert_2=\sqrt3$, and $A^Te=(0,0)^T$. The nonzero residual is correct: it is perpendicular to the model's column space and cannot be reduced by changing $x$.
Trace It by Hand
After back substitution, you have $z=(2,1)^T$ and $p=(1,0)$. Predict $x$ before reading on. Reveal: scatter by $x_{p_j}=z_j$, giving $x=(1,2)^T$, rather than leaving the values in pivoted order.
Now replace the measurements by $b^{(2)}=(5,1,4)^T$ while keeping $A$ fixed. Predict the solution and residual without refactoring. Check: the same $x=(1,2)^T$ now fits exactly, so the residual norm is $0$. The difference between the two measurement vectors, $(1,-1,-1)^T$, is perpendicular to both columns of $A$ and therefore changes the fitting error without changing the minimizer.
Finally, verify a Givens step: applying the rotation with $c=3/5$ and $s_g=4/5$ to $(3,4)^T$ must give $(5,0)^T$. Compute both coordinates, including the minus sign in the lower row.
Implementation in Python, C++, and Java
Each panel is a complete program with the same Householder and column-pivoting logic. Use Python 3, C++14 or newer, or save the Java program as Main.java. Inputs are nested real arrays with $m\ge n\ge1$; the code preserves $A$ and $b$. The factorization returns explicit thin $Q$, square $R$, and a zero-based final column order $p$. The solve routine expects an unchanged successful factorization and can be reused for another $b$.
All norms use repeated hypot calls rather than a direct sum of squares. Uniform scaling keeps reflector construction in a manageable range; the final $R$ is rescaled and checked for finite entries and representable nonzero pivots. The default $\tau=10^{-12}$ compares each active norm with the largest initial normalized column norm. This rejects deficient or numerically small directions, but it is not a singular-value calculation or a guarantee of accurate rank estimation. Extreme dynamic range can still cause underflow or loss of information; this educational code does not replace a carefully scaled numerical library.
The diagnostics check three different properties. The relative reconstruction error is $\max_{i,j}\lvert(QR)_{ij}-A_{i,p_j}\rvert/s$. Orthogonality error is the largest entry magnitude of $Q^TQ-I$. The stationarity error is $\lVert A^T(Ax-b)\rVert_\infty$; it should be small even though the fit residual norm is $\sqrt3$. The sample's absolute stationarity threshold of $10^{-10}$ assumes these modest data scales. Diagnostic dot products are simple floating-point sums intended for moderate inputs, not overflow-safe error estimators for every finite input.
All three programs print the same seven lines. Six decimal places hide the small nonzero roundoff in the diagnostic errors:
column order = [1, 0]x = [1.000000, 2.000000]fit residual norm = 1.732051relative factor error = 0.000000orthogonality error = 0.000000stationarity error = 0.000000check = PASS
Complexity, Correctness, and Invariants
For a successful dense $m\times n$ input with $m\ge n$, the implementations take $\Theta(mn^2)$ time in the best, average, and worst cases. They recompute the remaining column norms, update the trailing matrix, and explicitly form thin $Q$ using the same loop structure regardless of existing zeros. A rejected direction can terminate early. Unpivoted Householder reduction alone has a leading arithmetic count near $2mn^2-2n^3/3$; column-norm recomputation and explicit construction of $Q$ add work, so that expression is not the total count for these programs.
The working matrix, reflector vectors, thin $Q$, and $R$ together use $\Theta(mn)$ storage because $n\le m$. A later solve needs $\Theta(mn+n^2)=\Theta(mn)$ time and $\Theta(n)$ additional workspace, excluding its input vector. Reusing the factors for $t$ right-hand sides costs $\Theta(mn^2+tmn)$ overall. Forming a full $m\times m$ orthogonal matrix would require more storage and is deliberately avoided.
| Method | Dense factorization time | Main storage | Strength and limitation |
|---|---|---|---|
| Classical Gram–Schmidt | $\Theta(mn^2)$ | $\Theta(mn)$ | Direct geometric interpretation; can lose substantial orthogonality for nearly dependent columns |
| Modified Gram–Schmidt | $\Theta(mn^2)$ | $\Theta(mn)$ | Better rounding behavior; reorthogonalization may be needed |
| Householder with thin $Q$, as here | $\Theta(mn^2)$ | $\Theta(mn)$ | Orthogonal transformations and explicit column pivoting; rejects small directions |
| Dense Givens QR | $\Theta(mn^2)$ | $O(mn)$ with a compact transformation representation | Local two-row changes suit updates and selected sparsity patterns |
| Normal equations plus Cholesky | $\Theta(mn^2+n^3)$ | $O(n^2)$ beyond stored input | Can process rows incrementally; $\kappa_2(A^TA)=\kappa_2(A)^2$ for full-column-rank $A$ |
| Dense SVD | $O(mn^2)$ for standard dense algorithms | $O(mn)$ when singular vectors are kept | Stronger tools for rank and minimum-norm solutions; usually more expensive constants |
In exact arithmetic, each normalized reflector satisfies $H_k^T=H_k$ and $H_k^TH_k=I$. After $k$ stages, the evolving relation is
$$ W_k=H_{k-1}\cdots H_0(A/s)P_k. $$
The first $k$ columns have zero entries below their diagonals. A new pivot swaps only remaining columns, preserving those zeros. The next reflector acts only on rows $k,\ldots,m-1$, clearing its chosen column tail without disturbing previously completed columns. After the final stage, the transformed matrix is a top triangular block followed by zero rows.
Reversing the orthogonal transformations gives $AP=Q_{\mathrm{full}}\begin{pmatrix}R\\0\end{pmatrix}=QR$. Because thin $Q$ consists of columns of an orthogonal matrix, $Q^TQ=I_n$. The code's reverse loop forms this product by left-multiplying the rectangular identity $E$ in the required order.
For any candidate $x$, write $x=Pz$. Orthogonal projection splits the squared error into two perpendicular parts:
$$ \lVert Ax-b\rVert_2^2 =\lVert Rz-Q^Tb\rVert_2^2 +\lVert(I_m-QQ^T)b\rVert_2^2. $$
The second term is independent of $z$. Nonzero accepted diagonal entries make $R$ invertible, so solving $Rz=Q^Tb$ uniquely makes the first term zero. Scattering $z$ back through $P$ yields the unique minimizer in original variable order. Floating-point arithmetic makes these identities approximate; reconstruction, orthogonality, and stationarity checks test different parts of that argument.
Each step preserves the factorization relation through an orthogonal reflection and a recorded column permutation while extending the triangular zero pattern. Orthogonality preserves Euclidean distances, so the triangular solve minimizes the adjustable part of the fitting error; the perpendicular residual remains.
Variants, Pitfalls, and Optimizations
Do not lose the variable order. For this sample, the triangular result is $z=(2,1)^T$, but the answer in original coordinates is $x=(1,2)^T$. If you incorrectly return $x=z$, the fitted vector becomes $(4,2,2)^T$, its residual norm is $3$ instead of $\sqrt3$, and its stationarity error is $6$. For a general permutation, assign x[p[j]] = z[j]; guessing the inverse direction can silently fail. At later pivots, swap complete working columns so earlier rows of $R$ move with their variables.
Preserve orthogonality and use the stable sign. Nearly dependent columns produce a small remainder in Gram–Schmidt, where cancellation can magnify rounding error. Modified Gram–Schmidt and a second projection pass improve this behavior, while Householder is a usual dense choice. In a reflector, choosing the same sign as the first component can subtract nearly equal values: for $w=(1,\varepsilon)^T$ with tiny $\varepsilon$, $1-\lVert w\rVert_2$ can round to zero. The opposite sign uses a first component near $2$ instead.
Factor signs are not unique. Flipping one column of $Q$ and the corresponding row of $R$ leaves the product unchanged. Negative diagonal entries of $R$ in our example are valid. Comparing different implementations requires allowing for sign conventions, and pivot ties can also produce different valid column orders. Check mathematical identities before comparing raw factor entries.
Rank deficiency needs a separate solution policy. A tiny diagonal suggests a weak direction but does not by itself give an accurate condition number or a universal numerical rank. The LAPACK discussion of pivoted QR distinguishes factorization from rank determination. For rank-deficient or underdetermined problems, a basic solution is not necessarily the minimum-norm solution; use an appropriate complete orthogonal factorization or SVD when that is the requirement. Reducing $\tau$ blindly can amplify a poorly determined coefficient.
Choose the transformation for the workload. Givens rotations can update a triangular factor after adding a row, making them useful in online least squares. Sparse QR needs fill-reducing ordering because localized operations can still create nonzeros. Modified Gram–Schmidt appears naturally when building Krylov bases, often with reorthogonalization. Tall-skinny QR reduces communication on distributed tall matrices. Production dense implementations block reflectors into matrix operations, as described in the LAPACK Householder QR discussion.
Avoid unnecessary matrices. A solver can store reflectors and apply them directly to $b$ without explicitly forming $Q$ or $QQ^T$. Our explicit thin $Q$ makes the geometry and orthogonality check visible. Forming normal equations can square the condition number, so their lower storage footprint comes with a numerical tradeoff. Column rescaling can improve badly scaled data but changes coefficient coordinates and must be undone; row weighting changes the least-squares objective itself.
Check $AP\approx QR$ and $Q^TQ\approx I$, then restore the original variable order before evaluating the fit. A correct least-squares residual can be nonzero; test its stationarity as well as its size, and do not treat a tolerance-based rank rejection as an exact algebraic conclusion.
Practice Problems and Real-World Use
Begin by computing two-column orthogonalizations by hand, then compare methods on the same data. A full-rank random test can start from a known $x$ and set $b=Ax$. To test a nonzero least-squares residual independently, choose a vector $e$ perpendicular to the columns and use $b=Ax+e$; the same $x$ should remain the minimizer. Include column permutations with three or more entries so an incorrect scatter direction cannot hide behind a single swap.
For numerical tests, check reconstruction relative to matrix scale, orthogonality separately, and a scale-aware stationarity measure when data magnitudes change. Reuse a factorization for several right-hand sides, verify the original arrays remain intact, and include dependent columns, near-dependence, a zero leading component, and a one-column input.
| Exercise or application | Concrete task or checkpoint | Main idea | Difficulty |
|---|---|---|---|
| Two-column Gram–Schmidt | Obtain $(1,1,0)^T/\sqrt2$ and $(1,-1,2)^T/\sqrt6$ for the sample in original order | Projections and normalization | Beginner |
| Householder sign test | Reflect $(1,\varepsilon)^T$ using the stable opposite-sign choice | Avoid cancellation in the reflector vector | Intermediate |
| Pivoted least squares | Reproduce $p=(1,0)$, $x=(1,2)^T$, and residual norm $\sqrt3$ | Column pivots and a nonzero optimal residual | Intermediate |
| Givens rotation | Map $(3,4)^T$ to $(5,0)^T$ using $c=3/5$, $s_g=4/5$ | Two-coordinate elimination | Beginner |
| Polynomial fitting | Fit coefficients from more measurements than coefficients; compare centered and uncentered inputs | Rectangular models and conditioning | Applied |
| Online least squares | Append a measurement row and update $R$ with rotations | Reuse structure rather than refactor every observation | Advanced |
| Krylov basis construction | Orthogonalize newly generated vectors and monitor $Q^TQ-I$ | Modified Gram–Schmidt and reorthogonalization | Advanced |
| QR eigenvalue iteration | For square $A_k$, factor unpivoted $A_k=Q_kR_k$, then form $A_{k+1}=R_kQ_k$ | Orthogonal similarity; convergence needs further machinery | Advanced |