FGMRES and GCROT recycling¶
SOLVAX provides restarted flexible GMRES for general matrix-free systems and a GCROT-style extension for sequences of related systems. GMRES supports real or complex arrays and arbitrary matching JAX pytrees; GCROT currently requires a one-dimensional array. Both use right preconditioning.
Flexible GMRES¶
Given \(A x=b\) and an initial guess \(x_0\), form \(r_0=b-Ax_0\) and \(v_1=r_0/\beta\), where \(\beta=\lVert r_0\rVert_2\). At Arnoldi step \(j\),
Orthogonalization produces
where \(Z_m=[z_1,\ldots,z_m]\), \(V_{m+1}\) has orthonormal columns, and \(\bar H_m\) is upper Hessenberg. The correction is
Storing \(Z_m\) instead of assuming a fixed \(M^{-1}\) makes the method flexible: the preconditioner may change at every step [Saa03].
SOLVAX uses two passes of classical Gram-Schmidt (CGS2) and incremental Givens rotations. For complex inputs, projections are Hermitian and the rotations use a scaled unitary LAPACK-style convention. The true residual is recomputed at restart boundaries and after the final cycle.
API usage¶
solution = sx.gmres(
matvec,
b,
x0=None,
precond=None,
restart=30,
rtol=1e-8,
atol=0.0,
max_restarts=50,
)
Inputs¶
Input |
Meaning |
|---|---|
|
pure JAX callable |
|
array or matching-leaf pytree right-hand side, real or complex |
|
optional initial guess; zero by default |
|
right inverse action |
|
maximum Arnoldi steps per cycle |
|
true residual stopping tolerances |
|
maximum number of Arnoldi cycles |
The optimized array path expects a flat one-dimensional right-hand side. For
a three-dimensional field, either flatten before calling gmres and reshape
the solution afterward, or supply an inner_product such as
lambda left, right: jnp.vdot(left, right). Supplying inner_product selects
the shape-general PyTree path and the operator, initial guess, and
preconditioner must preserve that array/PyTree structure.
Output¶
KrylovSolution contains x, true residual_norm, total inner
iterations, converged, and recycle=None.
Restart trade-off¶
Unrestarted GMRES minimizes over an expanding Krylov space but stores
\(O(nm)\) basis data and performs \(O(nm^2)\) orthogonalization through \(m\) steps.
Restarting caps memory and compiled shapes, but discards spectral information
and can stagnate. Increase restart when memory permits and convergence stalls;
improve the preconditioner before relying on very large restart spaces.
GCROT-style recycling¶
For a sequence \(A_i x_i=b_i\), retain source and image bases \((U,C)\) satisfying
Before each Arnoldi cycle, project the residual over the recycled space:
The new Arnoldi cycle is built for the deflated operator
SOLVAX inserts the cycle’s normalized correction into a fixed-size FIFO recycle space. When a recycle pair is supplied for a changed operator, it recomputes \(AU\) and uses a thin QR factorization to restore \(AU=C\) for the current system.
recycle = None
for parameter in scan:
solution = sx.gcrot(
make_matvec(parameter),
b,
m=30,
k=10,
recycle=recycle,
rtol=1e-10,
)
recycle = solution.recycle
m is the inner FGMRES cycle length and k is the fixed recycle dimension.
The returned recycle arrays have shape (n, k) and may contain zero-padded
unused columns.
Recycling across optimization, and the drift diagnostic¶
A gradient-based optimization loop solves two slowly varying families: the
primal A(theta_j) x = b and the adjoint A(theta_j)^T lam = g. Each is
its own sequence — carry one recycle pair per direction:
primal = sx.gcrot(matvec, b, m=30, k=10, recycle=primal_pair)
adjoint = sx.gcrot(matvec_t, g, m=30, k=10, recycle=adjoint_pair)
primal_pair, adjoint_pair = primal.recycle, adjoint.recycle
On a warm start the solution reports recycle_drift: the mean principal-angle
sine between the incoming recycle image space and its re-established span
under the current operator. It is zero for an unchanged operator, and grows
linearly with the operator step ||A(theta_{j+1}) - A(theta_j)|| (a
Davis–Kahan subspace-rotation effect), so it tells the caller directly whether
the optimizer’s step size keeps the recycled space useful — small drift and
falling iteration counts, or drift near one and a pair no better than a cold
start. On the continuation family in benchmarks/benchmark_recycling.py, both
carried pairs collapse the per-step iterations from 32 to 2–3 within four
steps (a 3.3× total matvec reduction over ten steps) at measured drift
~1e-3 per step, while per-step cold GCROT shows no gain at all — the carrying
is the mechanism, not the deflation itself.
Relationship to the literature¶
The implementation follows the recycling framework of Parks et al. and the deflated-restart motivation of Morgan [Mor02, PdSM+06]. It is not a full harmonic-Ritz GCRO-DR implementation: it retains one optimal cycle correction per restart rather than selecting harmonic Ritz vectors. This keeps the update shape-static and \(O(nk)\) but may identify difficult invariant subspaces more slowly.
Choosing between Krylov methods¶
Method |
Matrix assumptions |
Memory |
Best use |
|---|---|---|---|
PCG |
Hermitian positive definite |
short recurrences |
SPD elliptic and energy systems |
FGMRES |
general square operator |
restart-sized basis |
isolated nonsymmetric/indefinite solve |
GCROT |
general sequence |
basis plus recycle pair |
continuation, time stepping, optimization |
BiCGSTAB |
general; not in SOLVAX |
short recurrences |
memory-limited nonsymmetric solves, but irregular residuals |
direct structured |
exact known structure |
factors |
repeated solves with exact band/block form |
Preconditioning patterns¶
jacobifor scaling problems;block_jacobifor strong within-cell coupling;exact banded or block-Thomas inverse of a coupling-dropped operator;
line_smootherfor anisotropy;p_multigridfor scale-separated elliptic error;mixed-precision or truncated inner solves, which flexibility permits.
See Preconditioners for formulas and examples.
Failure and performance diagnostics¶
converged=Falsemeans the true final residual missed the requested tolerance; increase work or improve the preconditioner.A small internal least-squares estimate is not used as the final truth; inspect
residual_norm.Near linear dependence increases orthogonalization sensitivity. CGS2 reduces but does not eliminate finite-precision loss of orthogonality [GLRvdE05].
A stale recycle space remains algebraically refreshed for the current operator but can be ineffective. Drop it after discontinuous parameter changes.
restart,m,k, andmax_restartsare static compiled sizes.
Complex systems and gradients¶
Complex systems use conjugate inner products. gmres can be supplied as the
primal and transposed solver to solvax.implicit.linear_solve(). For a
real objective of complex state, validate the adjoint convention with finite
differences; see examples/18_complex_krylov_gradient.py.
API summary¶
Runnable counterparts: examples/02_advection_preconditioning.py,
examples/03_recycled_continuation.py, and
examples/18_complex_krylov_gradient.py.