Operator API¶
Lightweight, physics-agnostic linear-operator containers.
Thin equinox modules wrapping the action of a linear map, interoperable
with the plain-callable convention used throughout solvax: everything in
solvax.krylov takes a matvec callable, and every operator here is
itself callable (op(v) == op.matvec(v)), so operators can be passed
directly as matvec= or precond= arguments. The single invariant all
operators maintain is adjoint consistency,
<A x, y> = <x, A^T y> for all x, y,
with A^T produced by the .T property.
Structure exploited per container:
MatrixFreeOperatorwraps an arbitrary linear callable; its transpose falls back onjax.linear_transpose()when no hand-written transpose is supplied.SumOperatorapplies(A_1 + ... + A_p) v = sum_i A_i v; the transpose distributes over the sum.KroneckerOperatorapplies(A (x) B) vthrough the reshape identity(A (x) B) vec(X) = vec(B X A^T)(equivalently, with the row-major flattening used byjnp.reshape,A X B^T), so the(pr) x (qs)product is never formed: cost is two small matrix products instead of one huge one.BlockTridiagonalOperatorstores dense bands(L_k, D_k, U_k)in the layout ofsolvax.directand applies all blocks with batched einsums;to_blocks()feedssolvax.direct.block_thomas_factordirectly, pairing the operator with its natural preconditioner.BorderedOperatoris the saddle-point (KKT-like) structure- K = [[A, B],
[C, 0]], K [x, y] = [A x + B y, C x],
arising when constraint or source rows border a physics block
A.schur_projected_precond()turns an approximate inverse ofAalone into a preconditioner for the full bordered system via the (small, dense) Schur complementS = C A^{-1} B:y = S^{-1} (C A^{-1} r_x - r_y), x = A^{-1} (r_x - B y),
which is exactly
K^{-1}whenA^{-1}is exact — so a preconditioner built for the physics block preconditions the constrained system.
References
M. Benzi, G. H. Golub & J. Liesen, “Numerical solution of saddle point problems”, Acta Numerica 14, 1 (2005) — block preconditioners and Schur complement methods for bordered/KKT systems.
C. F. Van Loan, “The ubiquitous Kronecker product”, J. Comput. Appl. Math. 123, 85 (2000) — the vec/reshape identity.
Y. Saad, Iterative Methods for Sparse Linear Systems, 2nd ed., SIAM (2003), chapter 9 — preconditioned Krylov methods with matrix-free operators.
- class solvax.operators.MatrixFreeOperator(apply, transpose_apply=None, *, shape)¶
A linear operator defined only by its action
v -> A v.- Variables:
apply (collections.abc.Callable) – linear callable computing
A @ von flat(n_in,)arrays; must be pure JAX (traceable).transpose_apply (collections.abc.Callable | None) – optional callable computing
A^T @ w. When omitted,.Tfalls back onjax.linear_transpose().shape (tuple[int, int]) – static
(n_out, n_in)extent of the map (keyword-only).
- Parameters:
- property T: MatrixFreeOperator¶
The transposed operator
A^T.When
transpose_applywas supplied it is used directly (and the forwardapplybecomes the transpose of the transpose, soop.T.Tis free). Otherwise the transpose is derived withjax.linear_transpose(): each application then costs roughly one forward evaluation ofapply(its jaxpr is transposed rule by rule), plus a re-trace per call outsidejit— supply an explicittranspose_applywhen the adjoint is hot.- Returns:
A
MatrixFreeOperatorwith shape(n_in, n_out).
- class solvax.operators.SumOperator(terms)¶
A sum of linear operators, applied term by term:
(sum_i A_i) v.Typical use: a structured principal part (e.g. a
BlockTridiagonalOperatorthat a direct solve can precondition) plus matrix-free perturbations or coupling terms.- Variables:
terms (tuple) – tuple of operators, plain linear callables, or dense matrices, all with the same
(n_out, n_in)extent.- Parameters:
terms (tuple)
- property T: SumOperator¶
(sum A_i)^T = sum A_i^T.Plain callables (no
.Tof their own) are wrapped inMatrixFreeOperatorwith this operator’s shape and transposed viajax.linear_transpose()— seeMatrixFreeOperator.Tfor the cost.- Type:
The transpose, distributed over the terms
- class solvax.operators.KroneckerOperator(a, b)¶
The Kronecker product
A (x) Bapplied without forming it.Uses the reshape identity
(A (x) B) vec(X) = vec(B X A^T); with the row-major flattening ofjnp.reshapethis reads, forAof shape(p, q)andBof shape(r, s),(A (x) B) v = (A X B^T).reshape(-1), X = v.reshape(q, s),
so one matvec costs
O(pqs + prs)instead of theO(pqrs)of the assembled(pr) x (qs)matrix.- Variables:
a (Any) – left factor — an operator (anything with
matvec-style__call__,shape,.T) or a dense matrix. Wrap plain callables inMatrixFreeOperator.b (Any) – right factor, same protocol as
a.
- Parameters:
- matvec(v)¶
Apply
(A (x) B) vvia two small products on the reshaped vector.
- property T: KroneckerOperator¶
(A (x) B)^T = A^T (x) B^T.
- class solvax.operators.BlockTridiagonalOperator(lower, diag, upper)¶
Block-tridiagonal operator over dense per-block bands.
Row
kof the action isL_k x_{k-1} + D_k x_k + U_k x_{k+1}, computed for all blocks at once with batched einsums over shifted slices (no Python loop over blocks). The band layout matchessolvax.direct:lower[0]andupper[-1]are carried but ignored, soto_blocks()feedssolvax.direct.block_thomas_factor()unchanged — the natural direct preconditioner for this operator.- Variables:
- Parameters:
- matvec(v)¶
Apply the operator to a flat
(n_blocks * m,)vector.
- property T: BlockTridiagonalOperator¶
bands swap and every block transposes.
(A^T)_{k,k-1} = U_{k-1}^Tand(A^T)_{k,k+1} = L_{k+1}^T, implemented as a roll of the transposed bands (the wrapped-aroundlower[0]/upper[-1]entries land in the ignored slots).- Type:
The transpose
- to_blocks()¶
Return
(lower, diag, upper)forsolvax.direct.block_thomas_factor().
- class solvax.operators.BorderedOperator(a, b_cols, c_rows)¶
The bordered (KKT-like) operator
[[A, B], [C, 0]].Acts on the concatenated vector
[x, y]as[A x + B y, C x]— the structure of a physics blockAaugmented with constraint rowsCand source/coupling columnsB(Benzi, Golub & Liesen 2005). Pair withschur_projected_precond()to recycle a preconditioner forAon the full constrained system.- Variables:
a (Any) – the
(n, n)principal block — an operator or dense matrix (wrap plain callables inMatrixFreeOperator).b_cols (jax.Array) – border columns
B, shape(n, p).c_rows (jax.Array) – border rows
C, shape(q, n).
- Parameters:
- matvec(v)¶
Apply to the concatenated vector:
[A x + B y, C x].
- property T: BorderedOperator¶
[[A, B], [C, 0]]^T = [[A^T, C^T], [B^T, 0]].The border rows become the transposed columns and vice versa.
- solvax.operators.schur_projected_precond(a_inv, b_cols, c_rows, d_block=None)¶
Preconditioner for a bordered system from an approximate inverse of
A.Given
a_inv ~ A^{-1}for the principal block alone, forms the small dense Schur complement of the border once (papplications ofa_invto the columns ofB, then an LU factorization of theq x presult, which must be square) and returns the projectiony = S^{-1} (C a_inv(r_x) - r_y), x = a_inv(r_x - B y),
i.e. the exact inverse of the bordered (KKT-like) matrix
[[A, B], [C, D]]withA^{-1}replaced bya_invthroughout (Benzi, Golub & Liesen, Acta Numerica 2005, section 5). The border-border blockDdefaults to zero (the constraint/saddle-point case[[A, B], [C, 0]]); pass a nonzerod_blockfor a general bordered system (e.g. a quasineutrality / potential border that couples the border unknowns to themselves), in which case the Schur complement isS = C A^{-1} B - D. Witha_invexact, the preconditioned operator is the identity and GMRES converges in one iteration; with an approximatea_inv, the border is still eliminated exactly through the projected Schur system, so a preconditioner built for the physics blockApreconditions the full bordered system. Each application costs two calls toa_invplus one small triangular solve.- Parameters:
a_inv (Callable) – callable
r -> A^{-1} r(approximate is fine) on flat(n,)arrays; must be linear and pure JAX.b_cols (Array) – border columns
B, shape(n, p).c_rows (Array) – border rows
C, shape(p, n)— the Schur complement must be square.d_block (Array | None) – optional border-border block
D, shape(p, p); whenNone(default) the border is the pure[[A, B], [C, 0]]saddle point.
- Returns:
A callable
[r_x, r_y] -> [x, y]on concatenated(n + p,)vectors, suitable asprecond=forsolvax.krylov.gmres()on the matching bordered operator.- Return type: