AI Knowledge Tree

Krylov subspaces and GMRES

Extend Krylov-subspace solving to nonsymmetric systems with GMRES.

⏱ 32 minPrerequisites:The conjugate gradient method

Conjugate gradient relies on AA being symmetric positive definite. GMRES (Generalized Minimal Residual) drops both assumptions: it works for any invertible AA. The price is that GMRES has to store the full basis of the Krylov subspace, giving a O(km)O(km) memory cost at iteration kk for an mm-dimensional problem, which is why it is restarted in practice[1].

Krylov subspaces

The Krylov subspace of order kk for AA and starting vector r0r_0 is

Kk(A,r0)=span⁡{r0, Ar0, A2r0, …, Ak−1r0}.\mathcal{K}_k(A, r_0) = \operatorname{span}\{r_0,\ A r_0,\ A^2 r_0,\ \dots,\ A^{k-1} r_0\}.

Both CG and GMRES find their iterates in x0+Kkx_0 + \mathcal{K}_k. The subspace is generated by repeated multiplication by AA alone, which is why these methods need nothing but the ability to compute AvA v for any vector vv.

The Arnoldi process

For nonsymmetric AA, building an orthonormal basis of the Krylov subspace uses the Arnoldi iteration, a Gram-Schmidt orthogonalization of Aq1,Aq2,…A q_1, A q_2, \dots against all previous basis vectors. After kk steps it produces:

  • An orthonormal matrix Qk=[q1 ∣ … ∣ qk]Q_k = [q_1\,|\,\dots\,|\,q_k] whose columns span Kk\mathcal{K}_k.
  • An upper Hessenberg matrix H~k∈R(k+1)×k\tilde H_k \in \mathbb{R}^{(k+1) \times k} satisfying
AQk=Qk+1 H~k.A Q_k = Q_{k+1}\, \tilde H_k.

For SPD AA, the Hessenberg matrix degenerates to tridiagonal and Arnoldi reduces to the Lanczos process, which CG uses implicitly through its short three-term recurrence (giving CG its constant memory cost).

The GMRES idea

GMRES picks xk∈x0+Kkx_k \in x_0 + \mathcal{K}_k to minimize the residual norm:

xk=arg⁡min⁡x∈x0+Kk∥b−Ax∥2.x_k = \arg\min_{x \in x_0 + \mathcal{K}_k}\lVert b - A x\rVert_2.

Substituting x=x0+Qkyx = x_0 + Q_k y and using AQk=Qk+1H~kA Q_k = Q_{k+1}\tilde H_k:

∥b−Ax∥=∥r0−Qk+1H~ky∥=∥Qk+1(βe1−H~ky)∥=∥βe1−H~ky∥,\lVert b - A x\rVert = \lVert r_0 - Q_{k+1}\tilde H_k y\rVert = \lVert Q_{k+1}(\beta e_1 - \tilde H_k y)\rVert = \lVert \beta e_1 - \tilde H_k y\rVert,

where β=∥r0∥\beta = \lVert r_0\rVert and the last step uses that Qk+1Q_{k+1} has orthonormal columns. The big residual minimization becomes a small (k+1)×k(k+1) \times k least-squares problem in yy, solved by Givens rotations applied to H~k\tilde H_k[2].

Memory and restarting

Storing QkQ_k costs O(km)O(km) memory and orthogonalization at step kk costs O(km)O(km) work. For large kk this is prohibitive, so practical implementations use GMRES(mm): restart every mm iterations using the current xx as the new x0x_0. Restarting trades a bounded memory cost for the loss of strict residual monotonicity across restart boundaries, and sometimes for slower convergence. Preconditioning is even more important for GMRES than for CG, because reducing kk reduces both work and memory[3].

GMRES minimizes the residual over a growing Krylov subspace, so its residual norm decreases monotonically until the nonsymmetric system is solved.
GMRES minimizes the residual over a growing Krylov subspace, so its residual norm decreases monotonically until the nonsymmetric system is solved.

Check it on arrays

python · numpy
import numpy as np

rng = np.random.default_rng(0)

# Nonsymmetric system: A has no real spectral structure.
n = 80
A = rng.normal(size=(n, n)) + 2 * np.eye(n)
b = rng.normal(size=n)

def gmres(A, b, max_iter=None, tol=1e-10):
    m = len(b)
    max_iter = max_iter or m
    Q = np.zeros((m, max_iter + 1))
    H = np.zeros((max_iter + 1, max_iter))
    r0 = b - A @ np.zeros(m)
    beta = np.linalg.norm(r0)
    Q[:, 0] = r0 / beta
    residuals = [beta]
    for k in range(max_iter):
        v = A @ Q[:, k]
        for j in range(k + 1):
            H[j, k] = Q[:, j] @ v
            v = v - H[j, k] * Q[:, j]
        H[k + 1, k] = np.linalg.norm(v)
        if H[k + 1, k] < 1e-14:
            break
        Q[:, k + 1] = v / H[k + 1, k]
        # Least-squares: min ||beta e_1 - H_{k+1, k} y||
        e1 = np.zeros(k + 2); e1[0] = beta
        y, *_ = np.linalg.lstsq(H[:k + 2, :k + 1], e1, rcond=None)
        x = Q[:, :k + 1] @ y
        res = np.linalg.norm(b - A @ x)
        residuals.append(res)
        if res < tol:
            return x, residuals
    return x, residuals

x_gmres, residuals = gmres(A, b, max_iter=60)
x_true = np.linalg.solve(A, b)
print(f"GMRES iterations: {len(residuals) - 1}")
print(f"final residual:   {residuals[-1]:.2e}")
print(f"error vs solve:   {np.linalg.norm(x_gmres - x_true):.2e}")
print(f"residual monotone decrease: {all(residuals[i] >= residuals[i+1] for i in range(len(residuals)-1))}")

GMRES drives the residual down monotonically and converges in well under the ambient dimension.

Exercises

Exercise 1. Explain why the GMRES residual norm is monotonically nonincreasing across iterations (within a single run, no restart), using the nested structure of the Krylov subspaces.

Solution

Step 1. GMRES picks xkx_k to minimize ∥b−Ax∥2\lVert b - A x\rVert_2 over the affine space x0+Kkx_0 + \mathcal{K}_k, where Kk=span⁡{r0,Ar0,…,Ak−1r0}\mathcal{K}_k = \operatorname{span}\{r_0, A r_0, \dots, A^{k-1} r_0\}.

Step 2. The Krylov subspaces are nested: Kk⊆Kk+1\mathcal{K}_k \subseteq \mathcal{K}_{k+1}, since every generator of Kk\mathcal{K}_k is also a generator of Kk+1\mathcal{K}_{k+1}. So x0+Kk⊆x0+Kk+1x_0 + \mathcal{K}_k \subseteq x_0 + \mathcal{K}_{k+1}.

Step 3. Minimizing the same residual norm over a larger set cannot increase the minimum: the previous minimizer xkx_k is still a candidate at step k+1k+1. Hence

∥b−Axk+1∥2=min⁡x∈x0+Kk+1∥b−Ax∥2≤∥b−Axk∥2.\lVert b - A x_{k+1}\rVert_2 = \min_{x \in x_0 + \mathcal{K}_{k+1}} \lVert b - A x\rVert_2 \le \lVert b - A x_k\rVert_2.

The residual norm never rises. Restarting breaks this, because it discards the accumulated subspace and starts a fresh minimization from x0′=xcurrentx_0' = x_{\text{current}}.

Exercise 2. The Arnoldi relation is AQk=Qk+1H~kA Q_k = Q_{k+1} \tilde H_k with Qk+1Q_{k+1} having orthonormal columns. Show that minimizing ∥b−Ax∥2\lVert b - A x\rVert_2 over x=x0+Qkyx = x_0 + Q_k y reduces to the small least-squares problem min⁡y∥βe1−H~ky∥2\min_y \lVert \beta e_1 - \tilde H_k y\rVert_2 with β=∥r0∥\beta = \lVert r_0\rVert.

Solution

Step 1. Write the residual at x=x0+Qkyx = x_0 + Q_k y: b−Ax=r0−AQkyb - A x = r_0 - A Q_k y, since r0=b−Ax0r_0 = b - A x_0.

Step 2. Substitute the Arnoldi relation AQk=Qk+1H~kA Q_k = Q_{k+1} \tilde H_k. Also write r0=βq1=Qk+1(βe1)r_0 = \beta q_1 = Q_{k+1} (\beta e_1), because q1=r0/βq_1 = r_0/\beta is the first column of Qk+1Q_{k+1} and e1e_1 is the first standard basis vector. Then

b−Ax=Qk+1(βe1)−Qk+1H~ky=Qk+1(βe1−H~ky).b - A x = Q_{k+1}(\beta e_1) - Q_{k+1}\tilde H_k y = Q_{k+1}\left(\beta e_1 - \tilde H_k y\right).

Step 3. Orthonormal columns preserve the 2-norm: ∥Qk+1z∥2=∥z∥2\lVert Q_{k+1} z\rVert_2 = \lVert z\rVert_2 for any zz. Therefore

∥b−Ax∥2=∥βe1−H~ky∥2.\lVert b - A x\rVert_2 = \lVert \beta e_1 - \tilde H_k y\rVert_2.

The mm-dimensional residual minimization collapses to a least-squares problem in the (k+1)×k(k+1) \times k Hessenberg matrix H~k\tilde H_k, which is cheap to solve by Givens rotations.

Exercise 3 (code). Run GMRES on a nonsymmetric system and confirm both that the residual norms are monotonically nonincreasing and that the solution matches a direct solve.

Solution
python · numpy
import numpy as np

rng = np.random.default_rng(0)
n = 50
A = rng.normal(size=(n, n)) + 2 * np.eye(n)   # nonsymmetric, well-separated spectrum
b = rng.normal(size=n)

m = len(b)
Q = np.zeros((m, m + 1))
H = np.zeros((m + 1, m))
r0 = b.copy()                                  # x0 = 0
beta = np.linalg.norm(r0)
Q[:, 0] = r0 / beta
residuals = [beta]
x = np.zeros(m)
for k in range(m):
    v = A @ Q[:, k]
    for j in range(k + 1):
        H[j, k] = Q[:, j] @ v
        v = v - H[j, k] * Q[:, j]
    H[k + 1, k] = np.linalg.norm(v)
    if H[k + 1, k] < 1e-14:
        break
    Q[:, k + 1] = v / H[k + 1, k]
    e1 = np.zeros(k + 2); e1[0] = beta
    y, *_ = np.linalg.lstsq(H[:k + 2, :k + 1], e1, rcond=None)
    x = Q[:, :k + 1] @ y
    residuals.append(np.linalg.norm(b - A @ x))

residuals = np.array(residuals)
assert np.all(np.diff(residuals) <= 1e-10)     # monotone nonincreasing
assert np.linalg.norm(x - np.linalg.solve(A, b)) < 1e-6
print("residual decreased monotonically over", len(residuals) - 1, "iterations")

Each iteration minimizes over a strictly larger Krylov subspace, so the residual norms form a nonincreasing sequence, and the final solution agrees with the direct solve to full working precision.

The final A4 lesson turns from algorithms to the precision they run in: mixed-precision arithmetic.


References

  1. [1]Saad, Y., & Schultz, M. H. (1986). "GMRES: A generalized minimal residual algorithm for solving nonsymmetric linear systems". SIAM Journal on Scientific and Statistical Computing, 7(3), 856-869. The original GMRES and restarting (doi:10.1137/0907058).↩
  2. [2]Trefethen, L. N., & Bau, D. (1997). Numerical Linear Algebra. SIAM. Arnoldi, GMRES, and the least-squares formulation (Lectures 33-35).↩
  3. [3]Saad, Y. (2003). Iterative Methods for Sparse Linear Systems (2nd ed.). SIAM. Krylov methods for nonsymmetric systems and preconditioning (doi:10.1137/1.9780898718003).↩