Why Iterative Linear Solvers Matters
A temperature model on a large grid can produce millions of linear equations, while each equation involves only a few neighboring grid cells. A dense factorization would ignore that structure and consume far more memory than storing the nonzero coefficients. Iterative solvers improve a candidate solution through repeated updates or matrix-vector products, often reaching useful accuracy without a full factorization.
This lesson solves real square systems $A\mathbf x=\mathbf b$ using Jacobi, Gauss–Seidel, successive overrelaxation (SOR), conjugate gradient (CG), and generalized minimal residual (GMRES). You will distinguish stationary iterations from Krylov methods, trace a small system exactly, implement all five, and check the true residual before declaring convergence.
The teaching programs use dense nested arrays so every update is visible. Sparse storage and matrix-free products are the natural next step for large problems. An iterative method is not automatically cheaper or more reliable than a direct solve: convergence depends on the matrix, the starting guess, tolerances, and often a preconditioner.
An iterative linear solver starts from a guess and repeatedly reduces the equation mismatch $\mathbf r=\mathbf b-A\mathbf x$. Choose a method whose assumptions fit the matrix, and verify a fresh residual before accepting the result rather than relying only on small updates or an internal recurrence.
Core Concepts and Terminology
Given $A\in\mathbb R^{n\times n}$, $\mathbf b\in\mathbb R^n$, and an initial vector $\mathbf x_0$, the output is an approximate solution, its residual norm, the number of completed updates, and a termination reason. The programs use
$$ \lVert\mathbf b-A\mathbf x\rVert_2 \le \text{atol}+\text{rtol}\,\lVert\mathbf b\rVert_2, \qquad \text{atol}\gt0,\quad\text{rtol}\ge0. $$
The positive absolute tolerance handles a zero right-hand side without dividing by its norm. This additive threshold is the contract used here; other libraries may use a maximum of absolute and relative thresholds instead.
Use $A=\begin{bmatrix}4&1\\1&3\end{bmatrix}$ and $\mathbf b=[1,2]^T$. The exact solution is $[1/11,7/11]^T$. Starting from zero gives residual $[1,2]^T$. Each method changes the candidate while leaving the original equations fixed.
| Term | Definition or example | Role |
|---|---|---|
| Iterate | $\mathbf x_k$ | Current approximate solution. |
| True residual | $\mathbf r_k=\mathbf b-A\mathbf x_k$ recomputed from the original inputs | Used for every convergence decision in the code. |
| Solution error | $\mathbf e_k=\mathbf x_k-\mathbf x_\star$ | Usually unknown; differs from the residual. |
| Stationary iteration | $\mathbf x_{k+1}=T\mathbf x_k+\mathbf c$ with fixed $T$ | Includes Jacobi, Gauss–Seidel, and fixed-parameter SOR. |
| Spectral radius | $\rho(T)=\max_i\lvert\lambda_i(T)\rvert$ | Governs convergence of stationary iteration for every starting guess. |
| SPD matrix | $A=A^T$ and $\mathbf z^TA\mathbf z\gt0$ for every nonzero $\mathbf z$ | Required by standard real CG theory. |
| Krylov subspace | $\mathcal K_k(A,\mathbf r_0)=\operatorname{span}(\mathbf r_0,A\mathbf r_0,\ldots,A^{k-1}\mathbf r_0)$ | Search space used by CG and GMRES. |
| Relaxation parameter | $\omega=1$ recovers Gauss–Seidel from SOR | Changes the stationary iteration and its convergence behavior. |
| GMRES restart length | $m$ Arnoldi steps per cycle | Bounds basis storage while potentially slowing convergence. |
| Breakdown | A required curvature, basis extension, or small solve becomes unusable | Must be distinguished from successful convergence. |
| Preconditioner | A more easily solved approximation to the system | Can improve convergence; not implemented in these examples. |
A small residual is not automatically a small solution error. For nonsingular $A$ and nonzero $\mathbf b$, $\lVert\mathbf e\rVert_2/\lVert\mathbf x_\star\rVert_2\le\kappa_2(A)\lVert\mathbf r\rVert_2/\lVert\mathbf b\rVert_2$. Ill-conditioning can magnify the effect of residual error and data perturbations.
How Iterative Linear Solvers Works, Step by Step
1. Validate the matrix and the method's assumptions. Require a finite, nonempty square matrix and matching finite vectors. Jacobi, Gauss–Seidel, and SOR need usable diagonal entries; the code rejects $\lvert a_{ii}\rvert\le10^{-14}\max_j\lvert a_{ij}\rvert$. Supported SOR parameters satisfy $0\lt\omega\lt2$. GMRES needs a positive restart length. CG checks exact stored symmetry and relies on the caller to supply positive definiteness; checking symmetry and encountered positive curvatures does not certify SPD on every direction.
2. Compute the initial residual and test it. If the starting guess already meets the residual threshold, return converged with zero updates. Otherwise proceed within the iteration budget. All method-specific input requirements are checked before this shortcut. An iteration budget of zero permits the initial check but makes no updates.
3. For stationary methods, choose which values a row update uses. Write $A=D+L+U$, with $L$ and $U$ containing the actual strictly lower and upper entries of $A$.
Jacobi uses only the previous iterate:
$$ x_i^{(k+1)}=\frac{b_i-\sum_{j\ne i}a_{ij}x_j^{(k)}}{a_{ii}}. $$
Gauss–Seidel uses values already computed in the current sweep:
$$ x_i^{(k+1)}=\frac{b_i-\sum_{j\lt i}a_{ij}x_j^{(k+1)}-\sum_{j\gt i}a_{ij}x_j^{(k)}}{a_{ii}}. $$
SOR first forms that same row's Gauss–Seidel candidate $\widehat x_i$, then relaxes it using $x_i^{(k+1)}=(1-\omega)x_i^{(k)}+\omega\widehat x_i$. With $\omega=1$ this is exactly Gauss–Seidel. A larger $\omega$ is not automatically faster, and $0\lt\omega\lt2$ does not guarantee convergence on an arbitrary nonsymmetric matrix.
Each completed sweep is followed by a fresh residual calculation. Reusing the updated array for Jacobi would silently turn it into a different iteration. The implementations keep the old and new vectors separate and select the appropriate source for each off-diagonal term.
4. For CG, update conjugate search directions. Initialize $\mathbf r=\mathbf b-A\mathbf x$, $\mathbf p=\mathbf r$, and $\gamma=\mathbf r^T\mathbf r$. On each step,
$$ \alpha=\frac{\gamma}{\mathbf p^TA\mathbf p},\qquad \mathbf x_{\mathrm{new}}=\mathbf x+\alpha\mathbf p,\qquad \mathbf r_{\mathrm{new}}=\mathbf r-\alpha A\mathbf p, $$
then $\beta=(\mathbf r_{\mathrm{new}}^T\mathbf r_{\mathrm{new}})/\gamma$ and $\mathbf p_{\mathrm{new}}=\mathbf r_{\mathrm{new}}+\beta\mathbf p$. Under SPD assumptions, directions are mutually $A$-conjugate in exact arithmetic. The code tests the true residual after changing $\mathbf x$, while keeping the recursive residual for the next CG direction. A nonpositive curvature $\mathbf p^TA\mathbf p$, or an unusable zero residual dot product while the true residual still fails, produces breakdown.
5. For GMRES, build an orthonormal Krylov basis and solve a small least-squares problem. At a cycle's base vector $\mathbf x_b$, let $\mathbf r_b=\mathbf b-A\mathbf x_b$, $\beta=\lVert\mathbf r_b\rVert_2$, and $\mathbf v_1=\mathbf r_b/\beta$. Arnoldi orthogonalizes $A\mathbf v_j$ against the existing basis to obtain
$$ AV_j=V_{j+1}\overline H_j, $$
where $\overline H_j$ is upper Hessenberg. GMRES chooses
$$ \mathbf z_j=\operatorname{arg\,min}_{\mathbf z} \lVert\beta\mathbf e_1-\overline H_j\mathbf z\rVert_2, \qquad \mathbf x_j=\mathbf x_b+V_j\mathbf z_j. $$
The implementations use two passes of modified Gram–Schmidt for orthogonalization. Previously computed Givens rotations are applied to each new Hessenberg column, followed by a new rotation that eliminates its last subdiagonal entry. The rotated small system is upper triangular, so back-substitution yields the coefficient vector without forming normal equations.
The triangular solve explicitly checks its pivots against $10^{-14}$ times the largest magnitude in its current upper triangle. This is a guarded QR-based small solve, not Gaussian elimination on the original matrix. No row pivoting is applied to $A$: arbitrary row changes can alter symmetry, stationarity, and the intended Krylov problem. A diagonal problem in a stationary method should prompt a deliberate reordering or another method, rather than an undocumented permutation.
6. Verify the candidate and handle restart or breakdown. GMRES forms a candidate and recomputes $\mathbf b-A\mathbf x$ at every completed inner step, regardless of the rotated residual estimate. If Arnoldi's next vector has norm at most $10^{-14}$ times the original $\lVert A\mathbf v_j\rVert$, the code treats the basis as closed. If the resulting candidate passes the true residual check, this is a successful termination, often called happy breakdown. If it fails, return breakdown. A zero rotation or rejected triangular pivot also returns breakdown with the latest completed candidate.
After $m$ inner steps without convergence, restart at the latest candidate with a new residual and basis. The global iteration budget counts completed candidate updates across all cycles. A breakdown attempt can perform work before producing another completed update, so iteration count is not an exact count of matrix-vector products.
flowchart TD
A["Validate system, controls, and method requirements"] --> B["Compute true initial residual"]
B --> C{"Residual within threshold?"}
C -->|Yes| R["Return converged"]
C -->|No| D{"Update budget remains?"}
D -->|No| L["Return limit"]
D -->|Yes| E["Build sweep, CG direction, or GMRES candidate"]
E --> F{"Required diagonal, curvature, or small solve usable?"}
F -->|No| X["Return breakdown or input error"]
F -->|Yes| G["Complete update; recompute true residual"]
G --> H{"Residual within threshold?"}
H -->|Yes| R
H -->|No| I{"GMRES basis closed?"}
I -->|Yes| X
I -->|No| J["Continue recurrence or restart GMRES when due"]
J --> D
Invalid inputs and checked nonfinite arithmetic raise an exception. Ordinary budget exhaustion returns limit; it is not labeled convergence. The code stores one true residual norm per completed update, including the initial norm, making history length equal to iterations + 1.
Worked Example with a Full Trace
Solve $A\mathbf x=\mathbf b$ for $A=\begin{bmatrix}4&1\\1&3\end{bmatrix}$, $\mathbf b=[1,2]^T$, and $\mathbf x_0=[0,0]^T$. The exact solution is $[1/11,7/11]^T$. For the stationary-method trace, impose a budget of three sweeps with a very tight residual threshold; the complete budgeted runs are:
| Sweep | Jacobi iterate | Jacobi residual norm | Gauss–Seidel iterate | Gauss–Seidel residual norm |
|---|---|---|---|---|
| $0$ | $[0,0]$ | $\sqrt5$ | $[0,0]$ | $\sqrt5$ |
| $1$ | $[1/4,2/3]$ | $\sqrt{73}/12$ | $[1/4,7/12]$ | $7/12$ |
| $2$ | $[1/12,7/12]$ | $\sqrt5/12$ | $[5/48,91/144]$ | $7/144$ |
| $3$ | $[5/48,23/36]$ | $\sqrt{73}/144$ | $[53/576,1099/1728]$ | $7/1728$ |
Both capped runs return limit, because their residuals remain above the tight target. On the first Gauss–Seidel sweep, the second row uses the newly computed $1/4$, rather than Jacobi's old zero. SOR with $\omega=1.1=11/10$ gives first-sweep values $[11/40,253/400]$; its first residual is $[-293/400,-69/400]$. This particular relaxation initially overshoots and is not guaranteed to beat Gauss–Seidel.
CG reaches the exact solution in two steps in exact arithmetic. Its complete recurrence trace is:
| Step | Current residual $\mathbf r$ | Search direction $\mathbf p$ | $A\mathbf p$ | $\alpha$ | New iterate | New residual |
|---|---|---|---|---|---|---|
| $1$ | $[1,2]$ | $[1,2]$ | $[6,7]$ | $5/20=1/4$ | $[1/4,1/2]$ | $[-1/2,1/4]$ |
| $2$ | $[-1/2,1/4]$ | $[-7/16,3/8]$ | $[-11/8,11/16]$ | $(5/16)/(55/64)=4/11$ | $[1/11,7/11]$ | $[0,0]$ |
Between the two steps, $\beta=(5/16)/5=1/16$. The CG residual norms are $\sqrt5$, $\sqrt5/4$, and $0$.
For GMRES with restart length $m=2$, the complete Arnoldi construction is especially small:
- Start $\beta=\sqrt5$ and $\mathbf v_1=[1,2]^T/\sqrt5$.
- $A\mathbf v_1=[6,7]^T/\sqrt5$. Its projection onto $\mathbf v_1$ is $h_{11}=4$. The remaining vector is $[2,-1]^T/\sqrt5$, with norm $h_{21}=1$, giving $\mathbf v_2$.
- $A\mathbf v_2=[7,-1]^T/\sqrt5$. Its basis coefficients are $h_{12}=1$ and $h_{22}=3$; the remainder is zero, so $h_{32}=0$.
Thus $\overline H_2=\begin{bmatrix}4&1\\1&3\\0&0\end{bmatrix}$. The first GMRES coefficient is $4\sqrt5/17$, giving $\mathbf x_1=[4/17,8/17]^T$ and residual $[-7/17,6/17]^T$. At the second step the least-squares solution is $\mathbf z=[3\sqrt5/11,-\sqrt5/11]^T$, giving $\mathbf x_2=[1/11,7/11]^T$ and zero residual. The closed basis is a successful termination after the true check.
The first Givens rotation has cosine $4/\sqrt{17}$ and sine $1/\sqrt{17}$. The final rotated upper triangle is $\begin{bmatrix}\sqrt{17}&7/\sqrt{17}\\0&11/\sqrt{17}\end{bmatrix}$ with right-hand side $[4\sqrt5/\sqrt{17},-\sqrt5/\sqrt{17}]^T$. This exposes the small back-substitution carried out by the programs.
Trace It by Hand
Starting from zero, predict the first Jacobi and Gauss–Seidel sweeps. Then predict CG's first residual after using $\alpha=1/4$ along $[1,2]^T$.
Reveal: Jacobi gives $[1/4,2/3]$, while Gauss–Seidel gives $[1/4,7/12]$ because its second row sees the new first component. CG gives $[1/4,1/2]$ with residual $[-1/2,1/4]$.
GMRES's first residual norm is $\sqrt{5/17}\approx0.542326$, smaller than CG's $\sqrt5/4\approx0.559017$. This does not contradict CG theory: CG minimizes the SPD energy-norm error in its search space, while GMRES minimizes the Euclidean residual norm.
Implementation in Python, C++, and Java
Each panel is a complete program implementing all five methods on dense nested arrays. The common solver takes a method name, $A$, $\mathbf b$, an initial guess, relative and absolute tolerances, a maximum number of completed updates, SOR's relaxation parameter, and GMRES's restart length. Unused method-specific parameters do not affect the other methods.
Returned fields are the latest candidate x, completed iterations, its true residual norm, status, and the residual history. Status is converged, limit, or breakdown. Inputs are not modified. No solution-vector history is stored, keeping stationary and CG workspace small; the scalar residual history uses one extra number per update.
For CG, exact stored symmetry is enforced; the routines do not silently symmetrize an input. Positive definiteness remains a caller assumption. For GMRES, the small QR triangular pivots are explicitly checked, while the original matrix is not row-pivoted. Reordering a stationary problem or applying a preconditioner requires a deliberate extension, rather than inserting a direct-elimination step into these recurrences.
Use the worked matrix and zero starting guess, $\text{rtol}=10^{-10}$, $\text{atol}=10^{-12}$, budget $200$, $\omega=1.1$, and GMRES restart $2$. The driver verifies both the true residual criterion and error relative to the independently known exact solution.
The identical printed output is:
jacobi: x = [0.090909, 0.636364], residual check = PASSgauss-seidel: x = [0.090909, 0.636364], residual check = PASSsor: x = [0.090909, 0.636364], residual check = PASScg: x = [0.090909, 0.636364], residual check = PASSgmres: x = [0.090909, 0.636364], residual check = PASSall methods = PASS
For this setup the completed update counts are $19$, $10$, $11$, $2$, and $2$, respectively. The printed vectors are rounded to six decimals; the convergence check uses the unrounded values. SOR taking eleven updates here versus Gauss–Seidel's ten illustrates why a fixed relaxation choice should be measured rather than assumed optimal.
Complexity, Correctness, and Invariants
Let $n$ be the dimension, $k$ the completed update count, $K$ its budget, and $m\le n$ the GMRES restart length. The programs store $A$ densely, so a matrix-vector product or sweep costs $\Theta(n^2)$. With a suitable sparse representation, let $q$ denote the number of stored nonzeros; the analogous product or sweep is typically $O(q+n)$. The table lists extra workspace beyond the matrix and input vectors, including the stored residual history.
| Method | Implemented dense work | Extra space | Convergence assumptions and limitations |
|---|---|---|---|
| Jacobi | $O((k+1)n^2)$ | $O(n+k)$ | $\rho(-D^{-1}(L+U))\lt1$ ensures convergence for every start; strict row diagonal dominance is sufficient |
| Gauss–Seidel | $O((k+1)n^2)$ | $O(n+k)$ | SPD or strict row diagonal dominance is sufficient; ordering can change convergence |
| SOR | $O((k+1)n^2)$ | $O(n+k)$ | For SPD matrices, $0\lt\omega\lt2$ gives convergence; this range alone does not suffice in general |
| CG | $O((k+1)n^2)$ | $O(n+k)$ | Standard guarantees need SPD; at most $n$ steps in exact arithmetic, not a floating-point iteration promise |
| Restarted GMRES | $O(n^2+k(n^2+nm+m^2))$ | $O(nm+m^2+k)$ | Handles nonsymmetric systems; restart may stagnate, and guarded small solves may break down |
The $m^2$ term per worst-case inner step includes the repeated small back-substitution; the $nm$ term covers basis orthogonalization and candidate construction. GMRES with no effective restart uses $m=n$ and a growing basis; its cumulative full-cycle orthogonalization cost is $O(nk^2)$ for $k\le n$, plus matrix-vector and small-solve work. Sparse products replace the dense $n^2$ product term by approximately $q+n$, but the supplied loops themselves remain dense.
All methods must validate the dense matrix and form an initial residual, so even an accepted initial guess costs $O(n^2)$ here. For a given budget, worst-case work is obtained by substituting $k=K$, unless a failure exits sooner. There is no matrix-independent average iteration count, and stationary methods or restarted GMRES have no unconditional iteration count to a target tolerance. Omitting the scalar history removes its $O(k)$ storage term.
Stationary convergence. In exact arithmetic, error follows $\mathbf e_{k+1}=T\mathbf e_k$. Convergence to the solution for every starting guess is equivalent to $\rho(T)\lt1$. For the splitting convention used here,
$$ T_J=-D^{-1}(L+U),\qquad T_{GS}=-(D+L)^{-1}U,\qquad T_{SOR}=(D+\omega L)^{-1}[(1-\omega)D-\omega U]. $$
Diagonal dominance and SPD results are useful sufficient conditions for the appropriate methods, not interchangeable guarantees for all stationary schemes.
CG's invariant and bound. Under SPD assumptions in exact arithmetic, $\mathbf x_k$ lies in $\mathbf x_0+\mathcal K_k(A,\mathbf r_0)$ and minimizes the error in the $A$-norm there. Search directions are $A$-conjugate and residuals are mutually orthogonal. If $\kappa=\kappa_2(A)$, the classical bound is
$$ \lVert\mathbf e_k\rVert_A\le 2\left(\frac{\sqrt\kappa-1}{\sqrt\kappa+1}\right)^k\lVert\mathbf e_0\rVert_A. $$
Finite precision can weaken orthogonality and separate the recursive residual from the true residual. The extra true-residual calculation prevents declaring success merely because the recurrence reports a misleadingly small value; it does not restore every exact-arithmetic invariant.
GMRES's invariant. Arnoldi constructs an orthonormal basis of the current Krylov space and a small Hessenberg representation of its action under $A$. Minimizing the small least-squares residual is equivalent to minimizing the full Euclidean residual in that affine search space. For a nonsingular $n$-dimensional system, full GMRES reaches a solution in at most $n$ steps in exact arithmetic. Restart discards basis information and loses that finite-step guarantee. Reorthogonalization improves numerical basis quality, but the final original-system residual remains the acceptance check.
Preserve each method's update rule and search space, then verify the candidate against the original system. converged is returned only when a fresh $\lVert\mathbf b-A\mathbf x\rVert_2$ meets the declared threshold; a small step, closed Arnoldi basis, or small recursive residual alone is insufficient.
Variants, Pitfalls, and Optimizations
SPD alone does not guarantee Jacobi convergence. Consider the $3\times3$ matrix with diagonal $1$ and every off-diagonal entry $0.6$, with $\mathbf b=[2.2,2.2,2.2]^T$ and zero starting guess. Its eigenvalues are $2.2,0.4,0.4$, so it is SPD. Jacobi's iteration matrix has an eigenvalue $-1.2$, and all components follow $u_{k+1}=2.2-1.2u_k$: $0,2.2,-0.44,2.728,-1.0736,\ldots$. Errors grow in magnitude. Gauss–Seidel converges on this SPD system, while CG solves this particular eigenvector-aligned right-hand side in one step in exact arithmetic.
Restart can cause GMRES stagnation. For $A=\begin{bmatrix}0&-1\\1&0\end{bmatrix}$, $\mathbf b=[1,0]^T$, and zero start, GMRES with restart length $1$ repeatedly minimizes along $[1,0]^T$. Its best coefficient is zero, so the iterate stays zero even though the matrix is nonsingular. Full GMRES with restart length $2$ reaches $[0,-1]^T$. Increase restart length or choose a useful preconditioner based on evidence, while accounting for memory and orthogonalization cost.
Choose a compatible preconditioner. Diagonal scaling is cheap; incomplete factorizations, block methods, algebraic multigrid, and domain decomposition can be more effective for suitable systems. Standard preconditioned CG needs an SPD-compatible preconditioner. GMRES permits more general left or right preconditioning, but its internally monitored preconditioned residual may differ from the original residual. Flexible GMRES is appropriate when the preconditioner changes between iterations; ordinary fixed-preconditioner formulas should not be assumed valid for that case.
Respect the matrix class. Nonsymmetric or indefinite systems are not justified CG inputs merely because they sometimes run without breakdown. MINRES can be useful for symmetric indefinite problems; GMRES handles general nonsymmetric systems. Forming $A^TA$ to force SPD squares the two-norm condition number when $A$ has full column rank, so least-squares methods such as LSQR or LSMR can be preferable to explicitly forming normal equations.
Do not hide numerical failure. A zero diagonal can defeat a stationary update even when a system is nonsingular. A tiny update can reflect stagnation rather than a small residual. A nearly closed Arnoldi basis can result from finite-precision dependence; verify the candidate instead of assuming happy breakdown. The code's relative guard is a numerical policy, so very ill-conditioned valid problems may return breakdown. Overflow in a diverging iteration raises an exception rather than reporting a solution.
Exploit structure deliberately. Jacobi updates can be parallelized readily, whereas Gauss–Seidel has row dependencies; colored or block variants change the implementation and convergence behavior. SOR parameter tuning depends on the problem and ordering. Sparse CSR storage avoids processing zeros, and matrix-free Krylov methods need only an operator product. For many right-hand sides with the same matrix, reusing a direct factorization or a preconditioner may be more efficient than repeated unprepared solves.
Interpret residuals with scale and conditioning. Compare the achieved true residual with the requested threshold, and consider a normwise backward-error measure such as $\lVert\mathbf r\rVert/(\lVert A\rVert\lVert\mathbf x\rVert+\lVert\mathbf b\rVert)$ when meaningful. A small backward error can coexist with a larger forward error for an ill-conditioned matrix. The printed sample's known solution supplies a separate forward-error check that a general application may not have.
Do not apply one method's convergence theorem to another. Check diagonal usability for stationary sweeps, SPD assumptions for CG, restart effects for GMRES, and the true original-system residual for every accepted solve. A budget or breakdown exit must remain visible to the caller.
Practice Problems and Real-World Use
Start with exact small systems, then vary one structural property at a time. Record completed updates, matrix-vector work where measured, true residual histories, and solution error when an independent reference is known. Comparing iteration counts alone hides GMRES's growing basis cost and repeated residual verification.
| Problem or system | Concrete task | Idea tested | Difficulty |
|---|---|---|---|
| Two-equation exact trace | Solve the worked $2\times2$ system using all five methods | Different iterates and common true-residual acceptance | Easy |
| Diagonally dominant systems | Generate a known solution, form $\mathbf b=A\mathbf x_\star$, and compare sweeps | Correct Jacobi versus Gauss–Seidel update ordering | Easy |
| SPD Jacobi counterexample | Use diagonal $1$ and off-diagonal $0.6$ in dimension $3$ | SPD does not imply Jacobi convergence | Medium |
| Restarted rotation system | Compare GMRES restart lengths $1$ and $2$ on the rotation matrix | Stagnation despite nonsingularity | Medium |
| Discrete Poisson problem | Solve a sparse grid-based elliptic system | Sparse products, slow stationary modes, and preconditioning | Medium |
| Coupled transport model | Use a nonsymmetric sparse discretization | GMRES and preconditioner selection | Hard |
| Implicit time step | Solve the linearized system inside an implicit dynamics solver | Inner tolerances, matrix reuse, and total simulation cost | Hard |
| Conditioning experiment | Compare residual and forward error on a controlled ill-conditioned system | Backward accuracy versus sensitivity of the solution | Hard |
For further reading, the authors' Templates for the Solution of Linear Systems develops stationary methods, Krylov methods, preconditioners, and stopping criteria. SciPy's CG documentation and GMRES documentation state their matrix assumptions and API-specific convergence controls. Compare restart, iteration-budget, and residual conventions before treating two implementations as identical.