Solvers & Preconditioners¶
Layer 1.5: strategy objects that encapsulate how a solve or logdet is
computed, decoupled from what is being solved. Everything that accepts a
solver= keyword anywhere in gaussx takes one of these; None falls back to
structural dispatch on the operator.
The one exception is the primitive gaussx.solve(A, b, solver=...), whose
solver= historically takes a lineax solver for its dense fallback (and per
structured factor). Both kinds are accepted in both places: gaussx.solve
hands the whole solve to a gaussx strategy, and linear_solve wraps a lineax
solver in LineaxSolver (solve only: a distribution
also needs a logdet). Anything else raises TypeError.
A strategy bundles a solve and a logdet algorithm. Mix and match with
ComposedSolver — e.g. CG for the solve, stochastic
Lanczos quadrature for the logdet — which is the standard recipe for large
kernel matrices.
The front door¶
linear_solve is the high-level entry point: it accepts a lineax operator or
a bare (matvec, shape) pair, normalises negative-definite systems, picks a
sensible iterative solver from the operator's tags, and threads a
preconditioner through. as_linear_operator wraps a raw matvec callable into a
tagged FunctionLinearOperator for matrix-free workflows.
Structured linear algebra and Gaussian primitives for JAX.
linear_solve(operator: OperatorLike, vector: Float[Array, ' n'], *, solver: AbstractSolveStrategy | lx.AbstractLinearSolver | None = None, preconditioner: PreconditionerLike | None = None) -> Float[Array, ' n']
¶
Solve A x = b through the unified front door.
Accepts either a built operator or a (matvec, shape) pair, handles the
negative-definite sign convention, picks a default solver when none is
given, and optionally applies a preconditioner.
Sign handling: an operator tagged negative semidefinite (and not PSD) is
solved via the equivalent positive-definite system (-A) x = -b, so that
a CG-style solver can be used directly. This is the common case for elliptic
PDE operators (e.g. a discrete Laplacian), which finite-volume / spectral
callers hand over as negative-definite matvecs.
Default solver selection (when solver is None):
- positive semidefinite operator ->
AutoSolver: an exact structural or dense solve for structured or small operators,CGSolverfor large ones (gh-390) - symmetric (possibly indefinite) operator ->
MINRESSolver - otherwise -> a
ValueErrorasking for an explicit solver
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
OperatorLike
|
The linear operator |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
solver
|
AbstractSolveStrategy | AbstractLinearSolver | None
|
Solve strategy to use, or a lineax solver (wrapped in
|
None
|
preconditioner
|
PreconditionerLike | None
|
Optional preconditioner. May be an
|
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
The solution |
Source code in src/gaussx/_solve_frontend.py
as_linear_operator(matvec: MatvecLike, *, shape: tuple[int, int] | None = None, in_structure: object | None = None, dtype: object = float, symmetric: bool = False, positive_semidefinite: bool = False, negative_definite: bool = False) -> lx.AbstractLinearOperator
¶
Wrap a raw matvec callable as a tagged lineax operator.
This is the matrix-free front door: callers that only have a
v -> A @ v function (rather than a structured operator object) use this
to obtain an operator that gaussx's solvers and primitives understand.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
matvec
|
MatvecLike
|
The matrix-vector product |
required |
shape
|
tuple[int, int] | None
|
Matrix shape |
None
|
in_structure
|
object | None
|
An explicit input structure -- either a
|
None
|
dtype
|
object
|
Dtype used when building the input structure from |
float
|
symmetric
|
bool
|
Tag the operator symmetric ( |
False
|
positive_semidefinite
|
bool
|
Tag the operator PSD (implies symmetric). |
False
|
negative_definite
|
bool
|
Tag the operator negative semidefinite (implies
symmetric). Use this for elliptic operators such as a discrete
Laplacian; |
False
|
Returns:
| Type | Description |
|---|---|
AbstractLinearOperator
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
If neither |
Source code in src/gaussx/_solve_frontend.py
Abstract interfaces¶
Structured linear algebra and Gaussian primitives for JAX.
AbstractSolveStrategy
¶
Bases: Module
Protocol for linear solve strategies.
A solve strategy encapsulates the choice of algorithm for
solving A x = b. Separating solve from logdet lets
users mix-and-match via ComposedSolver.
Source code in src/gaussx/_strategies/_base.py
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
abstractmethod
¶
Solve A x = b.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Solution |
Source code in src/gaussx/_strategies/_base.py
AbstractLogdetStrategy
¶
Bases: Module
Protocol for log-determinant strategies.
A logdet strategy encapsulates the choice of algorithm for
computing log |det(A)|. Separating logdet from solve
lets users mix-and-match via ComposedSolver.
All implementations accept an optional key parameter for
stochastic methods. Deterministic strategies ignore it.
Source code in src/gaussx/_strategies/_base.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
abstractmethod
¶
Compute log |det(A)|.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator. |
required |
key
|
Array | None
|
Optional PRNG key for stochastic estimators. |
None
|
Source code in src/gaussx/_strategies/_base.py
AbstractSolverStrategy
¶
Bases: AbstractSolveStrategy, AbstractLogdetStrategy
Protocol for solver strategies that pair solve + logdet.
A solver strategy encapsulates the choice of algorithm for solving linear systems and computing log-determinants. This decouples distribution objects from solver implementation details.
Subclasses must implement both solve and logdet.
For independent control, see AbstractSolveStrategy and
AbstractLogdetStrategy, composable via
ComposedSolver.
Source code in src/gaussx/_strategies/_base.py
Direct & iterative solvers¶
Structured linear algebra and Gaussian primitives for JAX.
DenseSolver
¶
Bases: AbstractSolverStrategy
Dense solver strategy using gaussx structural dispatch.
Delegates to gaussx.solve and gaussx.logdet which
automatically select the best algorithm based on operator
structure (Diagonal, BlockDiag, Kronecker, LowRankUpdate,
or dense fallback via lineax).
Source code in src/gaussx/_strategies/_dense.py
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve A x = b via structural dispatch (gaussx.solve).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Solution |
Source code in src/gaussx/_strategies/_dense.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Compute log|det(A)| via structural dispatch (gaussx.logdet).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator |
required |
key
|
Array | None
|
Unused; present for protocol compatibility with stochastic estimators. |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar log-determinant. |
Source code in src/gaussx/_strategies/_dense.py
AutoSolver
¶
Bases: AbstractSolverStrategy
Automatic solver selection based on operator type and size.
Selection logic:
- Structured, i.e. any operator with an exact structural solve and
log-determinant (Diagonal,
DiagonalizedOperator, BlockDiag, Kronecker, LowRankUpdate,
KroneckerSum, SpectralFunction, BlockTriDiag and its bidiagonal
factors, eigen-reducible sums of Kronecker products; also inside
TaggedLinearOperator,c * A,A / cand-A): DenseSolver, whose structural dispatch is exact and cheap - Small dense (N <= size_threshold): DenseSolver
- Large PSD: CGSolver. Its
logdetis a stochastic, fixed-seed SLQ estimate (the same probes on every call unless akeyis passed, e.g. throughgaussx.KeyedSolver); for an exact one useComposedSolver(CGSolver(), DenseLogdet()) - Large general: DenseSolver (fallback)
Attributes:
| Name | Type | Description |
|---|---|---|
size_threshold |
int
|
Matrix dimension above which iterative solvers are preferred. Default: 1000. |
throw |
bool
|
Forwarded to the |
preconditioner |
AbstractPreconditioner | None
|
Forwarded to the |
Source code in src/gaussx/_strategies/_auto.py
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve A x = b with automatically selected algorithm.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
The linear operator A. |
required |
vector
|
Float[Array, ' n']
|
The right-hand side b. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
The solution x. |
Source code in src/gaussx/_strategies/_auto.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Compute log |det(A)| with automatically selected algorithm.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
The linear operator A. |
required |
key
|
Array | None
|
Optional PRNG key (forwarded to stochastic strategies). |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar log |det(A)|. |
Source code in src/gaussx/_strategies/_auto.py
CGSolver
¶
Bases: AbstractSolverStrategy
Iterative CG solver with stochastic log-determinant.
Uses lineax CG for the linear solve and matfree's stochastic Lanczos quadrature (SLQ) for the log-determinant. Suitable for large PSD operators where dense factorization is too expensive.
The relative tolerance defaults to None, which resolves from the
operator's dtype at solve time: 1e-5 in float64 and 1e-3 in
float32, where
a relative residual of 1e-5 is out of reach once the condition number
passes about 1e3 (gh-327). Set them explicitly to override.
Attributes:
| Name | Type | Description |
|---|---|---|
rtol |
float | None
|
Relative tolerance for CG. |
atol |
float | None
|
Absolute tolerance for CG. |
max_steps |
int
|
Maximum CG iterations. |
num_probes |
int
|
Number of probe vectors for stochastic logdet. |
lanczos_order |
int
|
Order of the Lanczos decomposition for SLQ. |
seed |
int
|
Seed of the SLQ probes when |
preconditioner |
AbstractPreconditioner | None
|
Optional preconditioner. When set, its approximate inverse is passed to lineax CG to accelerate convergence. |
throw |
bool
|
Raise when CG does not converge within |
Source code in src/gaussx/_strategies/_cg.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve A x = b with conjugate gradients.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Solution |
Source code in src/gaussx/_strategies/_cg.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Stochastic log-determinant via Lanczos quadrature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator. |
required |
key
|
Array | None
|
PRNG key for probe vector sampling. If None,
uses |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of log |det(A)|. |
Source code in src/gaussx/_strategies/_cg.py
PreconditionedCGSolver
¶
Bases: AbstractSolverStrategy
CG solver with pivoted partial Cholesky preconditioner.
Uses gaussx.PartialCholeskyPreconditioner's guarded pivoted
partial Cholesky to build a rank-k preconditioner, then solves
(σ² I + F Fᵀ)⁻¹ v via the Woodbury identity inside lineax CG.
For operators of the form K + σ² I, preconditioning
dramatically reduces the number of CG iterations.
Pass a preconditioner built once with
gaussx.PartialCholeskyPreconditioner.from_operator or
gaussx.NystromPreconditioner.from_operator (on K, with
shift=σ²) to reuse it across solves. Otherwise a rank
preconditioner_rank factor of A − shift · I is rebuilt from the
system operator A at every solve, so the noise is never counted
twice (#345).
Attributes:
| Name | Type | Description |
|---|---|---|
preconditioner_rank |
int
|
Rank of the partial Cholesky built per solve.
Set to 0 to disable preconditioning (falls back to plain CG).
Ignored when |
shift |
float
|
The noise variance |
rtol |
float | None
|
Relative tolerance for CG. |
atol |
float | None
|
Absolute tolerance for CG. |
max_steps |
int
|
Maximum CG iterations. |
num_probes |
int
|
Number of probe vectors for stochastic logdet. |
lanczos_order |
int
|
Lanczos iterations for SLQ logdet. |
seed |
int
|
Seed for probe vector generation. |
preconditioner |
AbstractPreconditioner | None
|
A prebuilt preconditioner, used for every solve. |
throw |
bool
|
Raise when CG does not converge within |
Source code in src/gaussx/_strategies/_precond_cg.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | |
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve A x = b via preconditioned CG.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator. |
required |
vector
|
Float[Array, ' n']
|
The right-hand side b. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
The solution x. |
Source code in src/gaussx/_strategies/_precond_cg.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Stochastic log-determinant via Lanczos quadrature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of log |det(A)|. |
Source code in src/gaussx/_strategies/_precond_cg.py
MINRESSolver
¶
Bases: AbstractSolverStrategy
MINRES solver for symmetric (possibly indefinite) systems.
Uses the Lanczos-based MINRES algorithm for the linear solve and matfree's stochastic Lanczos quadrature (SLQ) for the log-determinant. Unlike CG, MINRES only requires symmetry — it works on indefinite and singular systems.
Use cases: EP natural parameters, saddle-point systems, Laplace approximation Hessians.
The solve stops as soon as it converges and goes through
lineax.linear_solve, so gradients are implicit (one more MINRES solve)
rather than a tape through the iterations. Running out of max_steps
raises, like gaussx.CGSolver, unless throw=False (gh-336).
Attributes:
| Name | Type | Description |
|---|---|---|
rtol |
float | None
|
Relative tolerance for MINRES. |
atol |
float | None
|
Absolute tolerance for MINRES. |
max_steps |
int
|
Maximum MINRES iterations. |
shift |
float
|
Diagonal shift — solves |
num_probes |
int
|
Number of probe vectors for stochastic logdet. |
lanczos_order |
int
|
Order of the Lanczos decomposition for SLQ. |
seed |
int
|
Seed of the SLQ probes when |
throw |
bool
|
Raise when MINRES does not converge within |
Source code in src/gaussx/_strategies/_minres.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | |
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve (A + shift I) x = b with MINRES.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A symmetric (possibly indefinite) linear operator |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Solution |
Source code in src/gaussx/_strategies/_minres.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Stochastic log|det(A + shift I)| via Lanczos quadrature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A symmetric linear operator. |
required |
key
|
Array | None
|
PRNG key for probe vector sampling. If None,
uses |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of |
Source code in src/gaussx/_strategies/_minres.py
LSMRSolver
¶
Bases: AbstractSolverStrategy
LSMR iterative least-squares solver (Fong & Saunders 2011).
Matrix-free solver that only requires matvec and transpose-matvec.
Supports Tikhonov regularization via damp parameter:
minimizes ||Ax - b||^2 + damp^2 ||x||^2.
Suitable for rectangular, ill-conditioned, or regularized systems.
The undamped path delegates to lineax.LSMR (with lineax's
implicit-differentiation rules). lineax's LSMR has no Tikhonov
damp parameter, so damped solves use matfree's LSMR, which has
a custom VJP for memory-efficient backpropagation.
Attributes:
| Name | Type | Description |
|---|---|---|
atol |
float | None
|
Absolute tolerance. |
btol |
float | None
|
Relative tolerance on the residual. |
ctol |
float
|
Condition number tolerance (the lineax path uses
|
max_steps |
int
|
Maximum iterations (formerly |
damp |
float
|
Tikhonov damping parameter. |
num_probes |
int
|
Number of probe vectors for stochastic logdet. |
lanczos_order |
int
|
Lanczos iterations for SLQ logdet. |
seed |
int
|
Seed for probe vector generation. |
throw |
bool
|
Raise when LSMR stops without converging ( |
Source code in src/gaussx/_strategies/_lsmr.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
maxiter: int
property
¶
Deprecated: max_steps (gh-405).
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' m']) -> Float[Array, ' n']
¶
Solve A x = b via LSMR.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A linear operator (may be rectangular). |
required |
vector
|
Float[Array, ' m']
|
The right-hand side b. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
The (least-squares) solution x. |
Source code in src/gaussx/_strategies/_lsmr.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Stochastic log-determinant via Lanczos quadrature.
LSMR itself does not use this: it is SLQ on a square symmetric
PSD operator, for the AbstractSolverStrategy interface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A square symmetric PSD linear operator. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of log |det(A)|. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the operator is not square (gh-402). |
Source code in src/gaussx/_strategies/_lsmr.py
BBMMSolver
¶
Bases: AbstractSolverStrategy
CG solve and SLQ logdet with BBMM's defaults (Gardner et al. 2018).
Solve: an ordinary lineax CG solve of each right-hand side.
Logdet: stochastic Lanczos quadrature (SLQ) via matfree, with
lanczos_order Lanczos steps on each of num_probes probes.
The two are independent: solve_and_logdet is a convenience wrapper
that calls solve and then logdet, sharing no matvecs (gh-396). The
modified batched CG (mBCG) pass that does share them, running the
right-hand sides and the probes through one block CG, is
gaussx.inv_quad_logdet(operator, rhs, strategy=BBMMSolver(...)). It
is not automatically cheaper: it runs the probe columns to convergence
rather than stopping at lanczos_order Lanczos steps, so it can apply
the operator to more columns than a separate solve and SLQ do.
Only the integer seed is stored. With no key, logdet draws
its probes from PRNGKey(seed) at every call, so it is a
deterministic function of the operator: the same probes every time
(common random numbers). Pass key or use gaussx.KeyedSolver to
vary them.
The options use the names shared by every iterative strategy (gh-405).
The old keywords still work, with a deprecation warning:
cg_tolerance=t sets rtol=atol=t, cg_max_iter is
max_steps and lanczos_iter is lanczos_order.
Attributes:
| Name | Type | Description |
|---|---|---|
rtol |
float | None
|
Relative tolerance for CG. |
atol |
float | None
|
Absolute tolerance for CG. |
max_steps |
int
|
Maximum CG iterations. |
lanczos_order |
int
|
Lanczos iterations for SLQ. |
num_probes |
int
|
Number of probe vectors for Hutchinson. |
seed |
int
|
Seed for probe vector generation. |
throw |
bool
|
Raise when CG does not converge within |
Source code in src/gaussx/_strategies/_bbmm.py
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
cg_tolerance: float | None
property
¶
Deprecated: rtol (gh-405).
cg_max_iter: int
property
¶
Deprecated: max_steps (gh-405).
lanczos_iter: int
property
¶
Deprecated: lanczos_order (gh-405).
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve A x = b via CG.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator. |
required |
vector
|
Float[Array, ' n']
|
The right-hand side b. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
The solution x. |
Source code in src/gaussx/_strategies/_bbmm.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Stochastic log-determinant via Lanczos quadrature.
Probe vectors are generated deterministically from self.seed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of log |det(A)|. |
Source code in src/gaussx/_strategies/_bbmm.py
solve_and_logdet(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> tuple[Float[Array, ' n'], Float[Array, '']]
¶
(solve(A, b), logdet(A)) in one call.
A convenience wrapper: it costs exactly solve plus logdet, with
no shared matvecs. For the shared mBCG pass use
gaussx.inv_quad_logdet(operator, rhs, strategy=self).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator. |
required |
vector
|
Float[Array, ' n']
|
The right-hand side b. |
required |
Returns:
| Type | Description |
|---|---|
tuple[Float[Array, ' n'], Float[Array, '']]
|
Tuple of (solution, log_determinant). |
Source code in src/gaussx/_strategies/_bbmm.py
ComposedSolver
¶
Bases: AbstractSolverStrategy
Mix-and-match solve and logdet from different strategies.
This lets you pair, e.g., an exact dense solve with a stochastic log-determinant estimator, or an iterative CG solve with a closed-form Kronecker log-determinant.
Accepts either fine-grained protocols (AbstractSolveStrategy,
AbstractLogdetStrategy) or full solver strategies.
Attributes:
| Name | Type | Description |
|---|---|---|
solve_strategy |
AbstractSolveStrategy
|
Strategy whose |
logdet_strategy |
AbstractLogdetStrategy
|
Strategy whose |
Examples:
solver = ComposedSolver(
solve_strategy=DenseSolver(),
logdet_strategy=SLQLogdet(num_probes=50, lanczos_order=30),
)
Source code in src/gaussx/_strategies/_composed.py
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve A x = b by delegating to solve_strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Solution |
Source code in src/gaussx/_strategies/_composed.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Compute log|det(A)| by delegating to logdet_strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator |
required |
key
|
Array | None
|
Optional PRNG key forwarded to stochastic estimators. |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar log-determinant. |
Source code in src/gaussx/_strategies/_composed.py
KeyedSolver
¶
Bases: AbstractSolverStrategy
Bind a PRNG key to a strategy's stochastic log-determinant (gh-384).
A stochastic logdet strategy (gaussx.SLQLogdet, gaussx.CGSolver,
gaussx.BBMMSolver, ...) called without a key draws its probes from
PRNGKey(seed), so every call sees the same probes: common random
numbers. That suits stochastic-gradient training (the objective is a
smooth, fixed function of the parameters), but averaging such estimates
reduces no variance, and MCMC then targets a fixed pseudo-likelihood.
Distribution methods such as MultivariateNormal.log_prob cannot take
a key under the numpyro contract. Wrap the strategy instead and pass a
fresh key per step. The key is a PyTree leaf, so it can change under
jax.jit without recompiling:
solver = gaussx.KeyedSolver(gaussx.CGSolver(), key)
mvn = gaussx.MultivariateNormal(loc, cov, solver=solver)
The functional API (gaussx.gaussian_log_prob,
gaussx.gaussian_entropy, gaussx.kl_standard_normal) takes
key= directly.
Attributes:
| Name | Type | Description |
|---|---|---|
strategy |
AbstractLogdetStrategy
|
The wrapped strategy. |
key |
Array
|
The PRNG key passed to |
Source code in src/gaussx/_strategies/_keyed.py
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve A x = b with the wrapped strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Solution |
Raises:
| Type | Description |
|---|---|
TypeError
|
If the wrapped strategy has no |
Source code in src/gaussx/_strategies/_keyed.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
log|det(A)| from the wrapped strategy, with the bound key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator |
required |
key
|
Array | None
|
Overrides the bound key when given. |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar log-determinant. |
Source code in src/gaussx/_strategies/_keyed.py
LineaxSolver
¶
Bases: AbstractSolveStrategy
A lineax solver as a gaussx solve strategy.
solver= means a gaussx strategy everywhere except the fallback of
gaussx.solve, which historically takes a lineax solver. Both
gaussx.linear_solve and gaussx.dispatch_solve wrap a bare
lineax.AbstractLinearSolver in this class, so either kind works in
either place.
Attributes:
| Name | Type | Description |
|---|---|---|
solver |
AbstractLinearSolver
|
The lineax solver, e.g. |
Source code in src/gaussx/_strategies/_lineax.py
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
lineax.linear_solve(operator, vector, solver).value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Solution |
Source code in src/gaussx/_strategies/_lineax.py
SparseCholeskySolver
¶
Bases: AbstractSolverStrategy
Exact solve / logdet / diag_inv through a sparse Cholesky factor.
For a symmetric positive-definite SparseOperator (a GMRF precision, a
Laplace Hessian Q + AᵀWA). The symbolic analysis is cached per
sparsity pattern, so only the numeric factorisation runs per call; its
gradients are exact (Takahashi for logdet, an adjoint solve for
solve). Pass it explicitly: AutoSolver keeps choosing CG for large
PSD operators, and nothing picks this strategy by size.
Attributes:
| Name | Type | Description |
|---|---|---|
ordering |
Ordering
|
Fill-reducing ordering, |
backend |
Backend
|
|
Examples:
import jax.numpy as jnp
import lineax as lx
import numpy as np
import gaussx
# Path-graph Laplacian plus a unit shift
n = 5
Q = gaussx.SparseOperator.from_coo(
np.r_[np.arange(n), np.arange(1, n)],
np.r_[np.arange(n), np.arange(n - 1)],
jnp.r_[jnp.array([2.0, 3.0, 3.0, 3.0, 2.0]), -jnp.ones(n - 1)],
(n, n),
symmetric=True,
tags=lx.positive_semidefinite_tag,
)
solver = gaussx.SparseCholeskySolver()
x = solver.solve(Q, jnp.ones(n))
ld = solver.logdet(Q)
variances = gaussx.diag_inv(Q, solver=solver) # Takahashi
Source code in src/gaussx/_strategies/_sparse_cholesky.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
factor(operator: lx.AbstractLinearOperator) -> SparseCholeskyFactor
¶
The sparse Cholesky factor of operator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A |
required |
Returns:
| Type | Description |
|---|---|
SparseCholeskyFactor
|
The factor, on the cached symbolic analysis of its pattern. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
Source code in src/gaussx/_strategies/_sparse_cholesky.py
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Solve A x = b with two sparse triangular solves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A symmetric positive-definite |
required |
vector
|
Float[Array, ' n']
|
Right-hand side |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Solution |
Source code in src/gaussx/_strategies/_sparse_cholesky.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Exact log|A| = 2 Σ log L_jj.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A symmetric positive-definite |
required |
key
|
Array | None
|
Unused; present for protocol compatibility. |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar log-determinant. |
Source code in src/gaussx/_strategies/_sparse_cholesky.py
diag_inv(operator: lx.AbstractLinearOperator) -> Float[Array, ' n']
¶
Exact diag(A⁻¹) (marginal variances) by one Takahashi sweep.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A symmetric positive-definite |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Shape |
Source code in src/gaussx/_strategies/_sparse_cholesky.py
Tolerances and float32¶
The iterative strategies (CGSolver, PreconditionedCGSolver, BBMMSolver,
MINRESSolver, LSMRSolver) leave their tolerances unset by default and
resolve them from the operator's dtype at solve time: the historical
constants in float64 (1e-5; 1e-4 for BBMM; 1e-6 for LSMR) and 1e-3
in float32. A float32 CG solve cannot reach a relative residual of 1e-5 once
the condition number passes about \(10^3\). It runs out of steps, or breaks
down, and returns an iterate worse than zero. Set rtol/atol explicitly to
override.
CGSolver, PreconditionedCGSolver, BBMMSolver and AutoSolver raise when
CG exhausts its step budget. With throw=False they return the last iterate
unchecked instead. Use that only where the caller checks the result, because
an unconverged CG iterate can be far from the solution.
Probe keys and common random numbers¶
A stochastic log-determinant (SLQ, used by CGSolver, PreconditionedCGSolver,
BBMMSolver, MINRESSolver, LSMRSolver and a large PSD AutoSolver) called
without a key draws its probes from PRNGKey(seed), so every call sees the
same probes. These are common random numbers: right for stochastic-gradient
training, where the objective becomes a fixed smooth function of the
parameters, but averaging such estimates reduces no variance, and MCMC then
targets a fixed pseudo-likelihood. Pass key= to gaussian_log_prob,
gaussian_entropy or kl_standard_normal. Distribution methods cannot take
one, so wrap their strategy in KeyedSolver, whose key
is a PyTree leaf, or change seed.
Sketch-and-precondition least squares¶
SketchAndPrecondLSMR solves tall (optionally ridge-damped) least squares
\(\min_x \|Ax - b\|^2 + \delta^2\|x\|^2\), \(m \gg n\), in a number of LSMR
steps that does not grow with \(m\) or \(\kappa(A)\). A sketch
\(S \in \mathbb{R}^{d \times m}\) (Sketching) of \(A\) gives
\([SA;\ \delta I] = QR\), and \(M = R^{-1}\) is a right preconditioner with
when \(S\) is an \(\varepsilon\)-embedding for \(\operatorname{range}(A)\). The
sketch-and-solve estimate \(x_0 = R^{-1}Q_{1:d}^\top Sb\) is the warm start;
sketch_and_solve returns it on its own (an \(O(\varepsilon)\) approximation).
The dense QR costs \(O(dn^2)\), so this targets \(n \lesssim 10^4\); for larger
\(n\), solve the normal equations with PreconditionedCGSolver and a
NystromPreconditioner on \(A^\top A\) with shift=δ².
# Gauss–Newton step on a (10⁶ pixels × 300 parameters) Jacobian J_op
step = gx.SketchAndPrecondLSMR(damp=1e-3).solve(J_op, -residuals)
Structured linear algebra and Gaussian primitives for JAX.
SketchAndPrecondLSMR
¶
Bases: AbstractSolverStrategy
Sketch-and-precondition LSMR for tall least squares (Rokhlin & Tygert, 2008).
Solves
i.e. plain least squares on the augmented system \(\tilde A = [A;\ \delta I]\), \(\tilde b = [b;\ 0]\) (\(\tilde A = A\) when \(\delta = 0\)). A sketch \(S \in \mathbb{R}^{d \times m}\), \(d = \lceil \gamma n \rceil\), gives the QR factorisation \([SA;\ \delta I] = QR\), and \(M = R^{-1}\) is a right preconditioner: if \(S\) is an \(\varepsilon\)-embedding for \(\operatorname{range}(A)\), then \(\|[SA;\ \delta I]\,My\| = \|y\|\) and
so LSMR on \(\tilde A M\) contracts the error by about \((\kappa - 1)/(\kappa + 1)\) per step, independently of \(m\) and of \(\kappa(A)\). The default \(\gamma = 4\) gives \(\varepsilon \approx \sqrt{1/\gamma} = 0.5\) for a Gaussian sketch (and comparable for the sparse sign sketch), so \(\kappa \lesssim 3\) and LSMR converges in a few tens of steps (Meng, Saunders & Mahoney, 2014; Epperly, 2024).
S = sample(sketch, d = ceil(gamma n), m; PRNGKey(seed))
Q, R = qr([S A; δ I]) # O(d n²)
x₀ = R⁻¹ Q[:d]ᵀ (S b) # sketch-and-solve warm start
y = LSMR(Ã R⁻¹, [b; 0] − Ã x₀) # undamped, lineax
return x₀ + R⁻¹ y
The inner solve is lineax.LSMR on the composed operator \(\tilde A M\)
(the undamped path of LSMRSolver), so gradients use lineax's implicit
differentiation. Only \(A\) is sketched; the ridge block is exact.
With damp == 0, \(A\) must have full column rank (otherwise the
solution is not unique and \(R\) is singular; throw=True raises);
damp > 0 always has a unique solution. A matrix-free \(A\) is
sketched with \(d\) transpose-matvecs, a batch of rows of \(S\) at a time,
so the extra memory is \(O(m)\) per batch, not \(O(dm)\).
Scope. The dense QR of the \(d \times n\) sketch costs \(O(d n^2)\) and
\(O(dn)\) memory, so this targets \(n \lesssim 10^4\) (e.g. Gauss-Newton
steps on a Jacobian with \(10^6\) pixels and a few hundred parameters).
For larger \(n\), solve the normal equations \((A^\top A + \delta^2 I)x =
A^\top b\) with PreconditionedCGSolver and a NystromPreconditioner
built from \(A^\top A\) with shift=δ².
Attributes:
| Name | Type | Description |
|---|---|---|
sketch |
SketchName
|
|
sampling_factor |
float
|
\(\gamma \ge 1\); the sketch has \(d = \min(\lceil \gamma n \rceil, m)\) rows. |
nnz |
int
|
Non-zeros per column of the sparse sign sketch. |
damp |
float
|
Ridge parameter \(\delta \ge 0\). |
atol |
float | None
|
LSMR tolerance on the normal-equations residual. |
btol |
float | None
|
LSMR relative tolerance on the residual. |
max_steps |
int
|
Maximum LSMR iterations. |
seed |
int
|
The sketch is drawn from |
throw |
bool
|
Raise when LSMR stops without converging, as |
References
Rokhlin, V. & Tygert, M. (2008). A fast randomized algorithm for overdetermined linear least-squares regression. PNAS, 105(36), 13212-13217.
Meng, X., Saunders, M. A. & Mahoney, M. W. (2014). LSRN: A parallel iterative solver for strongly over- or underdetermined systems. SIAM J. Sci. Comput., 36(2), C95-C118.
Epperly, E. N. (2024). Fast and forward stable randomized algorithms for linear least-squares problems. SIAM J. Matrix Anal. Appl., 45(4), 1782-1804.
Examples:
>>> import jax.numpy as jnp, jax.random as jr, lineax as lx
>>> import gaussx as gx
>>> A = jr.normal(jr.key(0), (2000, 10))
>>> b = jr.normal(jr.key(1), (2000,))
>>> solver = gx.SketchAndPrecondLSMR(atol=1e-6, btol=1e-6)
>>> x = solver.solve(lx.MatrixLinearOperator(A), b)
>>> bool(jnp.allclose(x, jnp.linalg.lstsq(A, b)[0], atol=1e-4))
True
Source code in src/gaussx/_strategies/_sketch_precond.py
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | |
solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' m']) -> Float[Array, ' n']
¶
Least-squares solution \(\arg\min_x \|Ax - b\|^2 + \delta^2\|x\|^2\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Operator \(A\), shape |
required |
vector
|
Float[Array, ' m']
|
Right-hand side \(b\), shape |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
The solution \(x\), shape |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
EquinoxRuntimeError
|
With |
Source code in src/gaussx/_strategies/_sketch_precond.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Stochastic log-determinant, delegated to LSMRSolver's SLQ path.
Present for the AbstractSolverStrategy protocol only: the sketch
plays no part. For the log-determinant of a square system, use a
square-system strategy (SLQLogdet, CGSolver, DenseSolver).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A square symmetric PSD linear operator. |
required |
key
|
Array | None
|
PRNG key for the probes; |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of \(\log|\det A|\). |
Source code in src/gaussx/_strategies/_sketch_precond.py
sketch_and_solve(operator: lx.AbstractLinearOperator, vector: Float[Array, ' m'], *, sketch: AbstractSketch, damp: float = 0.0) -> Float[Array, ' n']
¶
Approximate least squares from a sketch.
Solves the sketched problem \(\min_x \|S(Ax - b)\|^2 + \delta^2\|x\|^2\)
exactly, through the QR factorisation
\([SA;\ \delta I] = QR\) of the \(d \times n\) sketch (stacked on
\(\delta I\) when damp \(= \delta > 0\)):
If \(S\) is an \(\varepsilon\)-subspace embedding for \(\operatorname{range}([A\ b])\), the residual is within a constant factor of the optimum \(x^\star\) (Woodruff, 2014, §2):
The solution itself is only \(O(\varepsilon)\)-accurate, so this is a
function, not a solver strategy; SketchAndPrecondLSMR uses it as its
warm start and then solves to tolerance.
SA = S A # d × n, matrix-free via sketch_operator
Q, R = qr([SA; δ I]) # reduced QR, O(d n²)
x̂ = R⁻¹ Q[:d]ᵀ (S b)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Tall operator \(A\), shape |
required |
vector
|
Float[Array, ' m']
|
Right-hand side \(b\), shape |
required |
sketch
|
AbstractSketch
|
A sampled sketch with |
required |
damp
|
float
|
Ridge parameter \(\delta \ge 0\). |
0.0
|
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
The sketch-and-solve estimate \(\hat x\), shape |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
EquinoxRuntimeError
|
If |
References
Woodruff, D. P. (2014). Sketching as a tool for numerical linear algebra. Foundations and Trends in Theoretical Computer Science, 10(1-2), 1-157.
Examples:
>>> import jax.numpy as jnp, jax.random as jr, lineax as lx
>>> import gaussx as gx
>>> A = jr.normal(jr.key(0), (2000, 10))
>>> b = A @ jnp.ones(10) + 0.1 * jr.normal(jr.key(1), (2000,))
>>> S = gx.GaussianSketch.sample(jr.key(2), d=200, m=2000)
>>> x = gx.sketch_and_solve(lx.MatrixLinearOperator(A), b, sketch=S)
>>> bool(jnp.max(jnp.abs(x - 1.0)) < 0.05)
True
Source code in src/gaussx/_strategies/_sketch_precond.py
Logdet strategies¶
Dense eigendecomposition for exactness; stochastic Lanczos quadrature (SLQ) for \(O(n^2 \cdot \text{rank})\) estimates on large PSD (or symmetric-indefinite) operators.
NystromLogdet (Wenger et al., 2022) is the variance-reduced SLQ for a
covariance-form \(K + \sigma^2 I\). The log-determinant of the Nyström
preconditioner \(P = \hat K + \sigma^2 I\) is exact, and SLQ only estimates
\(\log|P^{-1/2}(K + \sigma^2 I)P^{-1/2}|\), whose operator is close to the
identity, so the probe variance collapses. Use plain SLQLogdet for
precision-form GMRF systems.
Structured linear algebra and Gaussian primitives for JAX.
DenseLogdet
¶
Bases: AbstractLogdetStrategy
Dense log-determinant via gaussx structural dispatch.
Delegates to gaussx.logdet which automatically selects
the best algorithm based on operator structure (Diagonal,
BlockDiag, Kronecker, LowRankUpdate, or dense fallback).
Source code in src/gaussx/_strategies/_slq_logdet.py
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Compute log |det(A)| via structural dispatch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A linear operator. |
required |
key
|
Array | None
|
Ignored (deterministic). |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar |
Source code in src/gaussx/_strategies/_slq_logdet.py
SLQLogdet
¶
Bases: AbstractLogdetStrategy
Stochastic log-determinant via Lanczos quadrature (SLQ).
Estimates log det(A) for PSD operators using stochastic
trace estimation: logdet(A) = tr(log(A)). Uses matfree's
Lanczos decomposition with sign-flip ("Rademacher") probe vectors
by default.
With no key, logdet uses PRNGKey(seed), so every call sees the
same probes: common random numbers, which suit stochastic-gradient
training but give no variance reduction when estimates are averaged and
a fixed pseudo-likelihood under MCMC. Pass a fresh key (or wrap a
strategy in gaussx.KeyedSolver) to decorrelate calls.
Attributes:
| Name | Type | Description |
|---|---|---|
num_probes |
int
|
Number of probe vectors for Hutchinson estimator. |
lanczos_order |
int
|
Order of the Lanczos decomposition. |
seed |
int
|
Seed for probe vector generation (used when no
|
sampler |
SamplerName
|
Probe distribution ( |
Source code in src/gaussx/_strategies/_slq_logdet.py
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | |
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Stochastic log det(A) via Lanczos quadrature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator. |
required |
key
|
Array | None
|
PRNG key for probe vector sampling. If |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of |
Source code in src/gaussx/_strategies/_slq_logdet.py
logdet_and_error(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> tuple[Float[Array, ''], Float[Array, '']]
¶
Stochastic log det(A) with its standard error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A PSD linear operator. |
required |
key
|
Array | None
|
PRNG key for probe vector sampling. If |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Tuple |
Float[Array, '']
|
error is the standard error of the mean across probes. |
Source code in src/gaussx/_strategies/_slq_logdet.py
IndefiniteSLQLogdet
¶
Bases: AbstractLogdetStrategy
Stochastic log|det(A)| for symmetric (possibly indefinite) operators.
Like SLQLogdet but uses log(|lambda|) as the matrix
function, so it works on indefinite and negative-definite matrices.
Supports a diagonal shift (A + shift * I).
Attributes:
| Name | Type | Description |
|---|---|---|
num_probes |
int
|
Number of probe vectors for Hutchinson estimator. |
lanczos_order |
int
|
Order of the Lanczos decomposition. |
shift |
float
|
Diagonal shift applied before computing the logdet. |
seed |
int
|
Seed for probe vector generation. |
sampler |
SamplerName
|
Probe distribution ( |
Source code in src/gaussx/_strategies/_slq_logdet.py
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | |
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Stochastic log|det(A + shift I)| via Lanczos quadrature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A symmetric linear operator. |
required |
key
|
Array | None
|
PRNG key for probe vector sampling. If |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of |
Source code in src/gaussx/_strategies/_slq_logdet.py
logdet_and_error(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> tuple[Float[Array, ''], Float[Array, '']]
¶
Stochastic log|det(A + shift I)| with its standard error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A symmetric linear operator. |
required |
key
|
Array | None
|
PRNG key for probe vector sampling. If |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Float[Array, ''], Float[Array, '']]
|
Tuple |
Source code in src/gaussx/_strategies/_slq_logdet.py
NystromLogdet
¶
Bases: AbstractLogdetStrategy
Nyström-preconditioned log-determinant of \(A + \mu I\) (Wenger et al., 2022).
For a covariance-form system \(A + \mu I\) (\(A\) PSD, e.g. \(K + \sigma^2 I\))
and the rank-\(k\) randomized Nyström approximation \(\hat A = U\hat\Lambda
U^\top\) of \(A\) (gaussx.randomized_nystrom), the preconditioner
\(P = \hat A + \mu I\) splits the log-determinant as
The first term is exact. The second is SLQ (gaussx.SLQLogdet) on the
preconditioned operator, whose eigenvalues cluster at one once \(k\)
exceeds the effective dimension \(d_\text{eff}(\mu) = \operatorname{tr}(A
(A + \mu I)^{-1})\); SLQ's variance scales with \(\|\log M\|_F^2\), so it
collapses as \(M \to I\), and its Lanczos quadrature converges in few
steps. With
every matvec with \(M\) costs one matvec with \(A + \mu I\) and \(O(nk)\).
key₁, key₂ = split(key)
c = 10 n eps μ # above the rounding of A + μI − μI
U, Λ̂ = randomized_nystrom(A + μI − (μ − c)I, rank; key₁); Λ̂ ← max(Λ̂ − c, 0)
log|P| = Σ log(λ̂ᵢ + μ) + (n − k) log μ
M = P^{-1/2} (A + μI) P^{-1/2}
return log|P| + SLQ(M; num_probes, lanczos_order; key₂)
Covariance form only: \(\hat A\) captures the top of \(A\)'s
spectrum, as for gaussx.NystromPreconditioner. For a precision-form
GMRF system \(Q + A^\top W A\) the hard directions are the smallest
eigenvalues; use SLQLogdet (or an exact sparse factorisation) there.
operator is the full system \(A + \mu I\), as for every other logdet
strategy; the strategy sketches its PSD part as the matrix-free
operator − shift·I (plus \(c = 10\,n\,\epsilon\,\mu\), removed again
from \(\hat\Lambda\), so that the rounding of the subtraction cannot make
it indefinite), so shift must be the \(\mu\) that operator
contains (#345). shift is a PyTree leaf, so it can be
a traced, learned noise variance.
Attributes:
| Name | Type | Description |
|---|---|---|
shift |
float | Float[Array, '']
|
\(\mu > 0\), e.g. the noise variance \(\sigma^2\). |
rank |
int
|
Nyström rank \(k\) ( |
oversample |
int
|
Extra sketch columns (see |
num_probes |
int
|
SLQ probe vectors for the remainder. |
lanczos_order |
int
|
Lanczos steps per probe. |
seed |
int
|
|
sampler |
SamplerName
|
SLQ probe distribution ( |
References
Wenger, J., Pleiss, G., Hennig, P., Cunningham, J. P. & Gardner, J. R. (2022). Preconditioning for scalable Gaussian process hyperparameter optimization. ICML, PMLR 162, 23751-23780.
Frangella, Z., Tropp, J. A. & Udell, M. (2023). Randomized Nyström preconditioning. SIAM J. Matrix Anal. Appl., 44(2), 718-752.
Examples:
>>> import einx, jax.numpy as jnp, lineax as lx
>>> import gaussx as gx
>>> x = jnp.linspace(0.0, 10.0, 200)
>>> K = jnp.exp(-0.5 * einx.subtract("i, j -> i j", x, x) ** 2)
>>> A = lx.MatrixLinearOperator(
... K + 0.1 * jnp.eye(200), lx.positive_semidefinite_tag
... )
>>> est = gx.NystromLogdet(shift=0.1, rank=40).logdet(A)
>>> exact = jnp.linalg.slogdet(A.as_matrix())[1]
>>> bool(jnp.abs(est - exact) < 1e-2 * jnp.abs(exact))
True
Source code in src/gaussx/_strategies/_nystrom_logdet.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |
logdet(operator: lx.AbstractLinearOperator, *, key: jax.Array | None = None) -> Float[Array, '']
¶
Estimate \(\log|A + \mu I|\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
The symmetric PSD system \(A + \mu I\), |
required |
key
|
Array | None
|
PRNG key for the sketch and the probes. |
None
|
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar estimate of \(\log|A + \mu I|\). |
Source code in src/gaussx/_strategies/_nystrom_logdet.py
Preconditioners¶
Approximate inverses \(M^{-1} \approx A^{-1}\) that accelerate the iterative
solvers above. Pass them via the preconditioner= argument of
linear_solve, CGSolver, or
PreconditionedCGSolver.
For a covariance-form system \(K + \sigma^2 I\), build
NystromPreconditioner or
PartialCholeskyPreconditioner once
with from_operator(K, rank, shift=σ²): the operator is the PSD part \(K\) only
and the noise is passed separately, so it is never counted twice. With a
Nyström rank \(\ell \gtrsim 2\lceil 1.5\,d_{\text{eff}}(\sigma^2)\rceil + 1\),
\(d_{\text{eff}}(\mu) = \operatorname{tr}\big(K(K + \mu I)^{-1}\big)\), CG
needs a number of iterations that does not grow with \(n\) (Frangella, Tropp &
Udell, 2023).
Structured linear algebra and Gaussian primitives for JAX.
AbstractPreconditioner
¶
Bases: Module
Protocol for preconditioners producing an approximate inverse.
Subclasses implement as_operator, returning a PSD lineax operator
that applies M^{-1} (or None to disable preconditioning).
Source code in src/gaussx/_preconditioners/_base.py
as_operator(operator: lx.AbstractLinearOperator | None = None) -> lx.AbstractLinearOperator | None
abstractmethod
¶
Return M^{-1} as a PSD lineax operator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator | None
|
The system operator |
None
|
Returns:
| Type | Description |
|---|---|
AbstractLinearOperator | None
|
A positive-semidefinite operator applying |
AbstractLinearOperator | None
|
indicate that no preconditioning should be applied. |
Source code in src/gaussx/_preconditioners/_base.py
JacobiPreconditioner
¶
Bases: AbstractPreconditioner
Diagonal preconditioner M^{-1} = diag(1 / |diag(A)|).
The cheapest preconditioner: scales each coordinate by the reciprocal of
the corresponding diagonal entry of A. Effective when A is
diagonally dominant.
Cost. Without an explicit diagonal it is read from the system
operator at every solve. A stored matrix and the structured operators
(gaussx.diag: BlockDiag, Kronecker, LowRankUpdate, ..., also
inside sums and scalings) give it exactly for free. A matrix-free part,
such as a bare lineax.FunctionLinearOperator, is never materialised,
but its diagonal costs one matvec per row (n matvecs, O(n) memory),
since Jacobi needs it exactly (gh-361). Pass diagonal= when it is
known in closed form, e.g. a kernel's variance.
A negative diagonal entry is inverted in absolute value, so the
operator really is positive semi-definite, as CG requires (the usual
choice for indefinite Jacobi). A zero entry is left unscaled (0),
with a zero gradient (gh-402).
Attributes:
| Name | Type | Description |
|---|---|---|
diagonal |
Float[Array, ' n'] | None
|
The diagonal of |
Source code in src/gaussx/_preconditioners/_jacobi.py
as_operator(operator: lx.AbstractLinearOperator | None = None) -> lx.AbstractLinearOperator
¶
Return diag(1 / d) as a PSD operator.
Source code in src/gaussx/_preconditioners/_jacobi.py
OperatorPreconditioner
¶
Bases: AbstractPreconditioner
Use an externally supplied approximate inverse as a preconditioner.
Attributes:
| Name | Type | Description |
|---|---|---|
approx_inverse |
AbstractLinearOperator | Callable
|
The approximate inverse |
in_structure |
object
|
Input structure for the callable form. When |
Source code in src/gaussx/_preconditioners/_operator.py
as_operator(operator: lx.AbstractLinearOperator | None = None) -> lx.AbstractLinearOperator
¶
Return the approximate inverse as a PSD lineax operator.
Source code in src/gaussx/_preconditioners/_operator.py
NystromPreconditioner
¶
Bases: AbstractPreconditioner
Randomized Nyström preconditioner for A + μ I.
For a system A + μ I with A PSD (e.g. K + σ² I), it builds a
rank-l randomized Nyström approximation  = U Λ̂ Uᵀ of the PSD
part A alone (gaussx.randomized_nystrom) and applies (Frangella,
Tropp & Udell, 2023)
On range(U) the preconditioned operator is ≈ λ̂_l + μ; on the
complement it is A + μ I itself, with eigenvalues in
[μ, λ_{l+1} + μ]. So
which is O(1) once l ≳ d_eff(μ) = tr(A (A + μ I)⁻¹), the
effective dimension. With l = 2⌈1.5 d_eff(μ)⌉ + 1 the expected
condition number is below 28, so CG needs a number of iterations that
does not grow with n.
Build it once with from_operator on the PSD part A and an explicit
shift=μ, like gaussx.PartialCholeskyPreconditioner: A must not
already contain μ, or the noise is counted twice (#345). The
factors are stored as arrays, so as_operator ignores its argument and
repeated solves cost no further matvecs.
Covariance form only. Â captures the top of A's spectrum,
which is right for K + σ² I. It is not a good preconditioner for a
precision-form GMRF system Q + Aᵀ W A, whose hard directions are its
smallest eigenvalues; use an exact sparse factorisation or
Jacobi-preconditioned CG there.
Up to gaussx 0.4 this class was a randomized Rayleigh-Ritz projection
onto a random subspace, which made CG slower below full rank (#354).
For a Rayleigh-Ritz eigendecomposition use
gaussx.randomized_eigh(op, rank, n_power_iter=0); it projects onto
orth(AΩ) rather than orth(Ω), which is more accurate, so it is not
numerically identical to the old preconditioner.
Attributes:
| Name | Type | Description |
|---|---|---|
basis |
Float[Array, 'n l']
|
Orthonormal |
eigenvalues |
Float[Array, ' l']
|
|
shift |
Float[Array, '']
|
|
Examples:
A GP with n = 200k and a Matérn-3/2 kernel is solved by preconditioned CG in tens of iterations, not thousands::
K_op = kl.to_operator(kernel, X, implicit=True)
P = gx.NystromPreconditioner.from_operator(
K_op, rank=500, shift=noise_var, key=key
)
A_op = K_op + lx.DiagonalLinearOperator(jnp.full(n, noise_var))
alpha = gx.PreconditionedCGSolver(preconditioner=P).solve(A_op, y)
A small runnable version:
>>> import einx, jax.numpy as jnp, jax.random as jr, lineax as lx
>>> import gaussx as gx
>>> x = jnp.linspace(0.0, 10.0, 100)
>>> K = jnp.exp(-0.5 * einx.subtract("i, j -> i j", x, x) ** 2)
>>> psd = lx.positive_semidefinite_tag
>>> P = gx.NystromPreconditioner.from_operator(
... lx.MatrixLinearOperator(K, psd), rank=30, shift=0.1, key=jr.key(0)
... )
>>> A = lx.MatrixLinearOperator(K + 0.1 * jnp.eye(100), psd)
>>> alpha = gx.PreconditionedCGSolver(preconditioner=P).solve(A, jnp.ones(100))
Source code in src/gaussx/_preconditioners/_nystrom.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | |
from_operator(operator: lx.AbstractLinearOperator, rank: int = 50, *, shift: float | Float[Array, ''], oversample: int = 0, key: jax.Array | None = None) -> NystromPreconditioner
classmethod
¶
Sketch the PSD part once and store the preconditioner.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
The PSD part |
required |
rank
|
int
|
Sketch size |
50
|
shift
|
float | Float[Array, '']
|
|
required |
oversample
|
int
|
Extra sketch columns, discarded after the
approximation (see |
0
|
key
|
Array | None
|
PRNG key for the test matrix. |
None
|
Returns:
| Type | Description |
|---|---|
NystromPreconditioner
|
A built |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/gaussx/_preconditioners/_nystrom.py
as_operator(operator: lx.AbstractLinearOperator | None = None) -> lx.AbstractLinearOperator
¶
Return P⁻¹ as a PSD operator; operator is ignored.
Source code in src/gaussx/_preconditioners/_nystrom.py
PartialCholeskyPreconditioner
¶
Bases: AbstractPreconditioner
Preconditioner from a pivoted partial Cholesky factor.
For a system A = K + σ² I it builds a rank-k partial Cholesky
factor F of the PSD part, F Fᵀ ≈ K, and applies
(σ² I + F Fᵀ)⁻¹ through the Woodbury identity. At full rank this is
exactly A⁻¹. Pivots are greedy (argmax of the residual diagonal)
or random (gaussx.rp_cholesky, proportional to it).
Two ways to build it:
- Once, with
from_operatoron the PSD partKand an explicitshift=σ². The factor and the Woodbury capacitance are stored as arrays, soas_operatorignores its argument and repeated solves cost no further matvecs ofK(#371). - Lazily, as
PartialCholeskyPreconditioner(rank, shift). Thenas_operatorreceives the system operatorAat every solve and factorsA − shift · Iimplicitly, soshiftis the part ofA's diagonal treated as noise and the noise is never counted twice (#345).shiftmust not exceed theσ²actually inA.
The factor is guarded like LAPACK ?pstrf: once rank exceeds the
numerical rank (small datasets, duplicated inputs, noiseless kernels),
the surplus columns are exactly zero instead of NaN or inf, and the
preconditioner degrades gracefully to the lower-rank one (gh-237).
Cost. A build applies the operator to rank pivot columns (or
calls column, see from_operator) and needs its diagonal. A given
diagonal, a stored matrix and the structured operators
(gaussx.diag) cost nothing more. For a matrix-free part, such as a bare
lineax.FunctionLinearOperator, the diagonal is a Hutchinson estimate
from 20 matvecs: it only selects the pivots, and each factor column is
read exactly from its pivot column, so the operator is never
materialised (O(n · rank) memory, gh-361).
Covariance form only: the factor captures the top of K's spectrum,
which is right for K + σ² I. It is not a good preconditioner for a
precision-form system Q + Aᵀ W A, whose hard directions are its
smallest eigenvalues.
Attributes:
| Name | Type | Description |
|---|---|---|
rank |
int
|
Rank of the partial Cholesky. |
shift |
float
|
Noise variance |
pivoting |
Literal['greedy', 'random']
|
|
key |
Array | None
|
PRNG key for |
diagonal |
Float[Array, ' n'] | None
|
The diagonal of the system operator for the lazy path.
|
factor |
Float[Array, 'n k'] | None
|
Stored factor |
capacitance |
Float[Array, 'k k'] | None
|
Lower Cholesky factor of |
factor_shift |
Float[Array, ''] | None
|
The |
Examples:
Build once on the PSD part, then reuse across solves.
>>> import einx, jax.numpy as jnp, lineax as lx, gaussx
>>> x = jnp.linspace(0.0, 10.0, 50)
>>> K = jnp.exp(-0.5 * einx.subtract("i, j -> i j", x, x) ** 2)
>>> psd = lx.positive_semidefinite_tag
>>> P = gaussx.PartialCholeskyPreconditioner.from_operator(
... lx.MatrixLinearOperator(K, psd), rank=20, shift=0.1
... )
>>> A = lx.MatrixLinearOperator(K + 0.1 * jnp.eye(50), psd)
>>> solver = gaussx.CGSolver(preconditioner=P)
>>> x1 = solver.solve(A, jnp.ones(50))
>>> x2 = solver.solve(A, jnp.arange(50.0)) # no rebuild
Source code in src/gaussx/_preconditioners/_partial_cholesky.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
from_operator(operator: lx.AbstractLinearOperator, rank: int = 50, *, shift: float | Float[Array, ''], pivoting: Literal['greedy', 'random'] = 'greedy', key: jax.Array | None = None, diagonal: Float[Array, ' n'] | None = None, column: Callable[[Int[Array, '']], Float[Array, ' n']] | None = None) -> PartialCholeskyPreconditioner
classmethod
¶
Factor the PSD part once and store the preconditioner.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
The PSD part |
required |
rank
|
int
|
Number of pivots, clamped to the operator size. |
50
|
shift
|
float | Float[Array, '']
|
The noise variance |
required |
pivoting
|
Literal['greedy', 'random']
|
|
'greedy'
|
key
|
Array | None
|
PRNG key for |
None
|
diagonal
|
Float[Array, ' n'] | None
|
The diagonal of |
None
|
column
|
Callable[[Int[Array, '']], Float[Array, ' n']] | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
PartialCholeskyPreconditioner
|
A built |
PartialCholeskyPreconditioner
|
ignores its argument. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/gaussx/_preconditioners/_partial_cholesky.py
as_operator(operator: lx.AbstractLinearOperator | None = None) -> lx.AbstractLinearOperator | None
¶
Return (σ² I + F Fᵀ)⁻¹ as a PSD operator.
A preconditioner built by from_operator ignores operator. The lazy
one factors operator − shift · I, where operator is the system.