Operators & Tags¶
Layer 1: structured linear operators extending
lineax.AbstractLinearOperator.
All are immutable equinox.Module pytrees, so they compose freely with jit,
grad, and vmap. The primitives dispatch on these types: a
solve against a Kronecker factorizes per Kronecker factor, a logdet of a
BlockDiag sums per block, a LowRankUpdate solve applies Woodbury.
Structured products & sums¶
The Kronecker product \(A_1 \otimes A_2 \otimes \cdots\) gives \(O(\sum_i n_i^3)\) solves on a \(\prod_i n_i\) grid; the Kronecker sum \(A \otimes I + I \otimes B\) diagonalises in the joint eigenbasis with eigenvalues \(\lambda_i + \mu_j\).
KroneckerSum and SumOfKroneckers are different operators
They are not word-order variants of one idea:
| Math | Eigendecomposition | |
|---|---|---|
KroneckerSum(A, B) |
\(A \otimes I + I \otimes B\) | Closed form — eigenvalues \(\lambda_i + \mu_j\), eigenvectors \(V_A \otimes V_B\) |
SumOfKroneckers(K_1, K_2, …) |
\(\sum_k A_k \otimes B_k\) | None in general. solve / logdet reduce the two-term case with one term positive definite — \(B \otimes C + \sigma^2 I\) and \(B_1 \otimes C_1 + B_2 \otimes C_2\) — to one eigendecomposition per factor, \(O(n_c^3 + n_d^3)\); eigendecompose covers the same two-term symmetric case more generally by densifying a \((n_c n_d)^2\) block |
SumOfKroneckers was called SumKronecker until gh-136. The old name
still imports and subclasses the new one — so isinstance checks keep
working — but emits a DeprecationWarning on construction and will be
removed in gaussx 0.7.0. Its square root and sampler followed in
gh-297: SumKroneckerSqrt → SumOfKroneckersSqrt and
sumkronecker_sample → sum_of_kroneckers_sample. The old names warn
and will be removed in gaussx 0.7.0. Each square-root class sits next to
its operator: KroneckerSumSqrt is the closed-form root of a
KroneckerSum, and SumOfKroneckersSqrt is the lazy Lanczos root that
sqrt returns for a SumOfKroneckers.
Structured linear algebra and Gaussian primitives for JAX.
Kronecker
¶
Bases: AbstractLinearOperator
Kronecker product operator A₁ ⊗ A₂ ⊗ … ⊗ Aₖ.
Matvec uses Roth's column lemma for efficient computation without materializing the full Kronecker product. For two factors A (m x n) and B (p x q), the product (A kron B) vec(X) is computed as vec(B X A^T) where X is reshaped to (q, n).
Complexity: O(sum n_i^3) instead of O((prod n_i)^2) for the naive approach.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*operators
|
AbstractLinearOperator
|
Two or more |
()
|
Examples:
>>> import jax.numpy as jnp
>>> import lineax as lx
>>> import gaussx
>>> psd = lx.positive_semidefinite_tag
>>> A = lx.MatrixLinearOperator(jnp.array([[2.0, 1.0], [1.0, 2.0]]), psd)
>>> B = lx.MatrixLinearOperator(jnp.array([[3.0, 0.5], [0.5, 1.0]]), psd)
>>> K = gaussx.Kronecker(A, B) # 4 x 4, stored as two 2 x 2 factors
>>> K.in_size()
4
>>> bool(jnp.allclose(K.as_matrix(), jnp.kron(A.as_matrix(), B.as_matrix())))
True
>>> type(gaussx.cholesky(K)).__name__ # primitives keep the structure
'Kronecker'
Source code in src/gaussx/_operators/_kronecker.py
BlockDiag
¶
Bases: AbstractLinearOperator
Block diagonal operator diag(A₁, A₂, …, Aₖ).
Each sub-operator acts on its own slice of the input vector. Matvec, transpose, logdet, solve, and cholesky all decompose per-block.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*operators
|
AbstractLinearOperator
|
One or more |
()
|
Examples:
>>> import jax.numpy as jnp
>>> import lineax as lx
>>> import gaussx
>>> psd = lx.positive_semidefinite_tag
>>> A = lx.MatrixLinearOperator(jnp.array([[2.0, 1.0], [1.0, 2.0]]), psd)
>>> B = lx.MatrixLinearOperator(jnp.array([[3.0, 0.5], [0.5, 1.0]]), psd)
>>> D = gaussx.BlockDiag(A, B) # diag(A, B), 4 x 4
>>> D.as_matrix().shape
(4, 4)
>>> logdet_blocks = gaussx.logdet(A) + gaussx.logdet(B)
>>> bool(jnp.allclose(gaussx.logdet(D), logdet_blocks))
True
Source code in src/gaussx/_operators/_block_diag.py
KroneckerSum
¶
Bases: AbstractLinearOperator
Kronecker sum A \oplus B = A \otimes I_b + I_a \otimes B.
Appears in separable PDEs, graph Laplacians, and space-time GPs.
If A = Q_A \Lambda_A Q_A^T and B = Q_B \Lambda_B Q_B^T,
the Kronecker sum has eigenvectors Q_A \otimes Q_B with
eigenvalues \lambda^A_i + \lambda^B_j.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
A
|
AbstractLinearOperator
|
First operator, shape |
required |
B
|
AbstractLinearOperator
|
Second operator, shape |
required |
Examples:
>>> import jax.numpy as jnp
>>> import lineax as lx
>>> import gaussx
>>> psd = lx.positive_semidefinite_tag
>>> A = lx.MatrixLinearOperator(jnp.array([[2.0, 1.0], [1.0, 2.0]]), psd)
>>> B = lx.MatrixLinearOperator(jnp.array([[3.0, 0.5], [0.5, 1.0]]), psd)
>>> S = gaussx.KroneckerSum(A, B) # A ⊗ I + I ⊗ B
>>> I2 = jnp.eye(2)
>>> dense = jnp.kron(A.as_matrix(), I2) + jnp.kron(I2, B.as_matrix())
>>> bool(jnp.allclose(S.as_matrix(), dense))
True
>>> sign, expected = jnp.linalg.slogdet(dense)
>>> bool(jnp.allclose(gaussx.logdet(S), expected, atol=1e-5)) # λ_i + μ_j
True
Source code in src/gaussx/_operators/_kronecker_sum.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 | |
eigendecompose() -> tuple[Float[Array, ' n'], Float[Array, 'n n']]
¶
Symmetric eigendecomposition via per-factor decomposition.
Assumes both factors are symmetric so the returned
Q = Q_A ⊗ Q_B is orthonormal — callers rely on
self == Q @ diag(eigenvalues) @ Q.T. Diagonal factors get a
structural shortcut; other operators are materialized and
decomposed via jnp.linalg.eigh. We deliberately avoid
routing untagged factors through gaussx.eig because
that primitive falls back to jnp.linalg.eig for untagged
operators and would return general (non-orthonormal)
eigenvectors — breaking the Q.T == Q^{-1} contract for the
common case of numerically symmetric matrices wrapped as plain
lineax.MatrixLinearOperator.
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Tuple |
Float[Array, 'n n']
|
eigenvalues are |
Source code in src/gaussx/_operators/_kronecker_sum.py
KroneckerSumSqrt
¶
Bases: AbstractLinearOperator
Symmetric square root of A \oplus B via per-factor eigenvectors.
Represents the symmetric matrix S with S @ S = A \oplus B
(where \oplus is the Kronecker sum A ⊗ I + I ⊗ B). The square
root is never materialized: mv and solve apply S and
S^{-1} matrix-free using the per-factor eigendecompositions, so the
cost is governed by the factor sizes rather than the full n_a · n_b
dimension.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
A
|
AbstractLinearOperator
|
Symmetric PSD factor, shape |
required |
B
|
AbstractLinearOperator
|
Symmetric PSD factor, shape |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If either factor is non-square or untagged as symmetric. |
EquinoxRuntimeError
|
If |
Source code in src/gaussx/_operators/_kronecker_sum.py
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 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 | |
solve(vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
Apply the inverse square root S^{-1} to vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Float[Array, ' n']
|
Input vector, shape |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
|
Source code in src/gaussx/_operators/_kronecker_sum.py
SumOfKroneckers
¶
Bases: AbstractLinearOperator
Sum of Kronecker products Σ_k A_k \otimes B_k.
Appears in multi-output GPs with correlated outputs, e.g.
K_task \otimes K_spatial + \sigma^2 I_task \otimes I_spatial.
Matvec is computed as the sum of the Kronecker matvecs.
gaussx.solve and gaussx.logdet dispatch structurally on two
terms with one of them positive definite — B ⊗ C + σ² I and the
general B₁ ⊗ C₁ + B₂ ⊗ C₂ — by whitening with the positive-definite
term and eigendecomposing each factor of the other. That costs
O(n_c³ + n_d³) rather than the O((n_c n_d)³) of a dense solve,
and the same reduction is reachable through the AddLinearOperator
chains sum_operator builds. The factors of the non-anchor term must
advertise symmetry, and the anchor's must be diagonal, a multiple of the
identity, or tagged positive-semidefinite; anything else keeps the dense
fallback.
The anchor must be positive definite
Whitening inverts the anchor term. positive_semidefinite_tag --
the strongest statement lineax can make -- permits a singular
operator, and whitening by one of those returns NaN where the
dense fallback would have answered. When both terms qualify, the one
whose inversion needs no factorization is preferred, so a scalar or
diagonal shift always wins over a tagged dense factor; a
multiple-of-the-identity anchor is folded in as a shift and is never
inverted at all. Beyond that the definiteness is the caller's to
guarantee, exactly as it is for gaussx.cholesky.
A diagonal or scaled-identity anchor factor is whitened by its
square root, so each entry must be strictly positive. With concrete
values a non-positive one is skipped in favour of the other term, or
of the dense fallback. A traced one cannot be inspected, so it keeps
the diagonal whitening and raises an EquinoxRuntimeError at run
time rather than returning NaN.
Three or more terms have no closed form and keep the dense fallback.
Drive the structured mv instead with
solve(op, b, solver=lineax.CG(...)) and gaussx.SLQLogdet.
eigendecompose is the separate, more general route: a joint
eigendecomposition of the second Kronecker pair (requires
A_2, B_2 to be symmetric). It forms a dense
(n_c n_d) x (n_c n_d) matrix internally, so it is
intended for moderate factor sizes (typical for multi-output GPs
where the task dimension is small).
Not to be confused with KroneckerSum, which is the standard
matrix-theory Kronecker sum A \otimes I + I \otimes B — a
different object with a closed-form eigendecomposition. This class is a
sum of arbitrary Kronecker products, which has none in general.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kron1
|
Kronecker
|
First Kronecker product |
required |
kron2
|
Kronecker
|
Second Kronecker product |
required |
*krons
|
Kronecker
|
Additional two-factor Kronecker products. |
()
|
Examples:
>>> import jax.numpy as jnp
>>> import lineax as lx
>>> import gaussx
>>> psd = lx.positive_semidefinite_tag
>>> A = lx.MatrixLinearOperator(jnp.array([[2.0, 1.0], [1.0, 2.0]]), psd)
>>> B = lx.MatrixLinearOperator(jnp.array([[3.0, 0.5], [0.5, 1.0]]), psd)
>>> I2 = lx.IdentityLinearOperator(A.in_structure())
>>> K = gaussx.SumOfKroneckers(gaussx.Kronecker(A, B), gaussx.Kronecker(I2, I2))
>>> dense = jnp.kron(A.as_matrix(), B.as_matrix()) + jnp.eye(4)
>>> bool(jnp.allclose(K.as_matrix(), dense))
True
>>> sign, expected = jnp.linalg.slogdet(dense)
>>> bool(jnp.allclose(gaussx.logdet(K), expected, atol=1e-5)) # per factor
True
Source code in src/gaussx/_operators/_sum_kronecker.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 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 | |
eigendecompose() -> tuple[Float[Array, ' n'], Float[Array, 'n n']]
¶
Eigendecompose via joint eigendecomposition of the second pair.
Decomposes A_2 = Q_C \Lambda_C Q_C^T and
B_2 = Q_D \Lambda_D Q_D^T, then transforms the first pair
into the eigenbasis and diagonalizes the result.
Note
This forms a dense (n_c n_d) x (n_c n_d) matrix
internally and is O((n_c n_d)^3). Intended for moderate
factor sizes (e.g. multi-output GPs where task dimension
is small).
Raises:
| Type | Description |
|---|---|
ValueError
|
If the factors of |
Returns:
| Type | Description |
|---|---|
Float[Array, ' n']
|
Tuple |
Float[Array, 'n n']
|
|
Source code in src/gaussx/_operators/_sum_kronecker.py
SumOfKroneckersSqrt
¶
Bases: SqrtOperator
Lazy Lanczos square-root operator for SumOfKroneckers covariances.
What gaussx.sqrt returns for a SumOfKroneckers. Not to be confused
with KroneckerSumSqrt, the closed-form root of a KroneckerSum.
Specialization of SqrtOperator that narrows original to a
SumOfKroneckers operator. mv computes sqrt(A) v via
matfree's Lanczos matrix-function product without materializing the full
square root.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
original
|
SumOfKroneckers
|
The |
required |
lanczos_order
|
int
|
Number of Lanczos iterations; clamped to the operator size. |
_DEFAULT_LANCZOS_ORDER
|
Source code in src/gaussx/_primitives/_sqrt.py
Low-rank updates¶
\(L + U\,\mathrm{diag}(d)\,V^\top\) with Woodbury-efficient solves and
matrix-determinant-lemma logdets. The factories build the common special cases
directly from arrays. orthonormal=True is an unchecked claim that \(U\) has
orthonormal columns (\(U^\top U = I_k\): truncated SVD, Nyström, ensemble
factors). It matters only for a symmetric update on a scaled-identity base,
\(cI + U\,\mathrm{diag}(d)\,U^\top\) — build one with
low_rank_plus_identity(U, d, scale=c, orthonormal=True) — where solve and
logdet take \(O(nk)\) / \(O(k)\) closed forms with no \(k \times k\) factorisation,
and inv and sqrt stay orthonormal low-rank updates:
On any other base (e.g. a general diagonal) the flag does not change the result, and tag inference never depends on it.
SVDLowRankUpdate is deprecated
It remains a LowRankUpdate subclass — so isinstance checks and
singledispatch registrations keyed on it still work — but it forces
orthonormal=True and emits a DeprecationWarning on construction. New
code should use LowRankUpdate(base, U, S, V, orthonormal=True) or
svd_low_rank_plus_diag. It will be
removed in gaussx 0.7.0.
Structured linear algebra and Gaussian primitives for JAX.
LowRankUpdate
¶
Bases: AbstractLinearOperator
Low-rank update operator L + U diag(d) Vᵀ.
Represents a base operator L plus a rank-k update. When L is cheap to solve (e.g. diagonal), the Woodbury identity gives efficient solves for the full operator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
AbstractLinearOperator
|
The base operator L, with shape |
required |
U
|
Float[Array, 'n k']
|
Left factor, shape |
required |
d
|
Float[Array, ' k'] | None
|
Diagonal scaling, shape |
None
|
V
|
Float[Array, 'n k'] | None
|
Right factor, shape |
None
|
tags
|
object | frozenset[object]
|
Extra lineax tags -- the caller's structural claims. |
frozenset()
|
orthonormal
|
bool
|
The caller's claim that U (and V) have orthonormal
columns, |
False
|
Tags are inferred from structure only, never from array values, so the
same call gives the same tags (and pytree structure) eagerly and under
jax.jit:
symmetric_tagwhen the base is symmetric and the factors are shared -- V omitted or passed as the same object as U. This is recorded in the staticsymmetric_factorsfield. Value-equal but distinct factors (V = U.copy()) are not inferred symmetric; passtags=lx.symmetric_tagto claim it.positive_semidefinite_tagadditionally when the base is PSD and d is omitted (all ones). A caller-supplied d can have any sign, so passtags=lx.positive_semidefinite_tagto claim PSD.
Examples:
>>> import jax.numpy as jnp
>>> import lineax as lx
>>> import gaussx
>>> base = lx.DiagonalLinearOperator(jnp.array([1.0, 2.0, 3.0]))
>>> U = jnp.array([[1.0], [0.0], [1.0]])
>>> K = gaussx.LowRankUpdate(base, U) # base + U Uᵀ
>>> K.rank
1
>>> dense = K.as_matrix()
>>> b = jnp.ones(3)
>>> x = gaussx.solve(K, b) # Woodbury: one 1 x 1 solve, no 3 x 3 factorisation
>>> bool(jnp.allclose(dense @ x, b, atol=1e-5))
True
Source code in src/gaussx/_operators/_low_rank_update.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 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 | |
rank: int
property
¶
Rank of the low-rank update.
low_rank_plus_diag(diag: Float[Array, ' n'], U: Float[Array, 'n k'], d: Float[Array, ' k'] | None = None, V: Float[Array, 'n k'] | None = None, *, psd: bool = False) -> LowRankUpdate
¶
Construct diag(diag) + U diag(d) Vᵀ.
Common pattern for inducing-point / Nystrom approximations where the base is a diagonal matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
diag
|
Float[Array, ' n']
|
Diagonal entries, shape |
required |
U
|
Float[Array, 'n k']
|
Left factor, shape |
required |
d
|
Float[Array, ' k'] | None
|
Diagonal scaling, shape |
None
|
V
|
Float[Array, 'n k'] | None
|
Right factor, shape |
None
|
psd
|
bool
|
Claim that |
False
|
Returns:
| Type | Description |
|---|---|
LowRankUpdate
|
A |
Source code in src/gaussx/_operators/_low_rank_update.py
low_rank_plus_identity(U: Float[Array, 'n k'], d: Float[Array, ' k'] | None = None, V: Float[Array, 'n k'] | None = None, *, scale: float = 1.0, psd: bool = False, orthonormal: bool = False) -> LowRankUpdate
¶
Construct scale * I + U diag(d) Vᵀ.
Common pattern for regularised low-rank models (e.g. noise + signal).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
U
|
Float[Array, 'n k']
|
Left factor, shape |
required |
d
|
Float[Array, ' k'] | None
|
Diagonal scaling, shape |
None
|
V
|
Float[Array, 'n k'] | None
|
Right factor, shape |
None
|
scale
|
float
|
Scalar multiplier on the identity. Default 1.0. |
1.0
|
psd
|
bool
|
Claim that |
False
|
orthonormal
|
bool
|
Claim that U has orthonormal columns (unchecked).
The base is then a scaled lineax |
False
|
Returns:
| Type | Description |
|---|---|
LowRankUpdate
|
A |
Source code in src/gaussx/_operators/_low_rank_update.py
svd_low_rank_plus_diag(diag: Float[Array, ' n'], U: Float[Array, 'n k'], S: Float[Array, ' k'], V: Float[Array, 'n k'], *, psd: bool = False) -> LowRankUpdate
¶
Construct diag(diag) + U diag(S) Vᵀ from a truncated SVD.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
diag
|
Float[Array, ' n']
|
Diagonal entries, shape |
required |
U
|
Float[Array, 'n k']
|
Left singular vectors, shape |
required |
S
|
Float[Array, ' k']
|
Singular values, shape |
required |
V
|
Float[Array, 'n k']
|
Right singular vectors, shape |
required |
psd
|
bool
|
Claim that |
False
|
Returns:
| Type | Description |
|---|---|
LowRankUpdate
|
A |
Source code in src/gaussx/_operators/_low_rank_update.py
Banded & Toeplitz¶
Block-tridiagonal operators solve in \(O(N d^3)\) via block-banded Cholesky — the precision structure of Markovian (state-space) GPs. Symmetric Toeplitz operators get \(O(n \log n)\) matvecs and sampling via FFT circulant embedding.
Structured linear algebra and Gaussian primitives for JAX.
BlockTriDiag
¶
Bases: AbstractLinearOperator
Block-tridiagonal operator with a symmetric off-diagonal band.
Represents the structure:
[D_1 A_1^T ]
[A_1 D_2 A_2^T ]
[ A_2 D_3 ... ]
[ A_{N-1} D_N]
where D_k are (d, d) diagonal blocks and A_k are
(d, d) sub-diagonal blocks. This is the precision matrix
structure arising from discretized SDEs in state-space GP inference.
All primitives (solve, logdet, cholesky, diag, trace) exploit the banded structure for O(Nd³) cost instead of O((Nd)³).
The off-diagonal band is symmetric by construction (A_k below,
A_kᵀ above), so the operator is symmetric exactly when every D_k
is. With symmetric=True (the default) it carries
lineax.symmetric_tag and gaussx.solve / gaussx.logdet use the
block Cholesky, which reads only the lower triangle of each D_k; a
concrete non-symmetric D_k is rejected with a ValueError (the
check is skipped under tracing, where the flag is a claim). Pass
symmetric=False for non-symmetric diagonal blocks: the tag is then
omitted and the solve and log-determinant fall back to a dense LU.
+, -, unary - and scalar * keep the tags that still hold:
symmetry when both operands are symmetric, positive-semidefiniteness for
a sum of two PSD operators or a concretely non-negative scale (a negative
one, or negation, swaps it with negative-semidefiniteness).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
diagonal
|
Float[Array, 'N d d']
|
Diagonal blocks, shape |
required |
sub_diagonal
|
Float[Array, 'Nm1 d d']
|
Sub-diagonal blocks, shape |
required |
symmetric
|
bool
|
Whether every |
True
|
tags
|
object | frozenset[object]
|
Additional lineax tags. |
frozenset()
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import jax.numpy as jnp
>>> import lineax as lx
>>> import gaussx
>>> diagonal = jnp.stack([3.0 * jnp.eye(2)] * 3) # three 2 x 2 blocks
>>> sub_diagonal = jnp.stack([0.5 * jnp.eye(2)] * 2)
>>> T = gaussx.BlockTriDiag(diagonal, sub_diagonal)
>>> T.as_matrix().shape
(6, 6)
>>> b = jnp.ones(6)
>>> x = gaussx.solve(T, b) # block-banded, O(N d³)
>>> bool(jnp.allclose(T.as_matrix() @ x, b, atol=1e-5))
True
Source code in src/gaussx/_operators/_block_tridiag.py
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 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 | |
add(other: BlockTriDiag) -> BlockTriDiag
¶
Add two block-tridiagonal operators (e.g. prior + likelihood sites).
The sum is symmetric if both operands are, and keeps a definiteness tag (PSD or NSD) that both operands carry.
Source code in src/gaussx/_operators/_block_tridiag.py
LowerBlockTriDiag
¶
Bases: AbstractLinearOperator
Lower triangular block-bidiagonal Cholesky factor.
Represents:
[L_1 ]
[B_1 L_2 ]
[ B_2 L_3 ]
[ ... L_N]
where L_k are (d, d) lower-triangular blocks and B_k are
(d, d) sub-diagonal blocks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
diagonal
|
Float[Array, 'N d d']
|
Lower-triangular diagonal blocks, shape |
required |
sub_diagonal
|
Float[Array, 'Nm1 d d']
|
Sub-diagonal blocks, shape |
required |
Source code in src/gaussx/_operators/_block_tridiag.py
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 | |
transpose() -> UpperBlockTriDiag
¶
Transpose gives upper block-bidiagonal.
UpperBlockTriDiag
¶
Bases: AbstractLinearOperator
Upper triangular block-bidiagonal (transpose of LowerBlockTriDiag).
Represents:
[U_1 C_1 ]
[ U_2 C_2 ]
[ ... C_{N-1}]
[ U_N ]
where U_k are upper-triangular diagonal blocks and C_k are
super-diagonal blocks.
Source code in src/gaussx/_operators/_block_tridiag.py
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 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | |
Toeplitz
¶
Bases: AbstractLinearOperator
Symmetric Toeplitz matrix from its first column.
K_{ij} = c_{|i-j|}. Stored as O(n) with O(n log n) matvec
via circulant embedding and FFT.
For stationary kernels on regular 1-D grids the full kernel matrix is Toeplitz, so this gives an asymptotic win over dense storage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
column
|
Float[Array, ' n']
|
First column of the Toeplitz matrix, shape |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If |
Examples:
>>> import jax.numpy as jnp
>>> import lineax as lx
>>> import gaussx
>>> T = gaussx.Toeplitz(jnp.array([2.0, 0.5, 0.25])) # first column
>>> T.as_matrix().tolist()
[[2.0, 0.5, 0.25], [0.5, 2.0, 0.5], [0.25, 0.5, 2.0]]
>>> [float(v) for v in T.mv(jnp.ones(3))] # FFT matvec
[2.75, 3.0, 2.75]
Source code in src/gaussx/_operators/_toeplitz.py
ToeplitzCholesky
¶
Bases: AbstractLinearOperator
Circulant-embedding sample factor for a symmetric positive Toeplitz matrix.
The operator has shape (n, embedding_factor * n) and satisfies
L @ L.T == Toeplitz(column) — it is a rectangular sample factor,
not a traditional lower-triangular Cholesky factor. Applying it to
standard normal white noise gives samples from
𝒩(0, Toeplitz(column)) when the Wood--Chan condition holds.
The Wood--Chan non-negativity check is implemented with
eqx.error_if so it is JIT-friendly: the error fires at evaluation
time rather than tracing time. If the embedding's spectrum has a
materially negative eigenvalue, bump embedding_factor (typically
4 or 8 suffices for well-behaved covariances).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
column
|
Float[Array, ' n']
|
First column of the Toeplitz matrix, shape |
required |
embedding_factor
|
int
|
Circulant embedding size as a multiple of |
2
|
Source code in src/gaussx/_operators/_toeplitz.py
Fast-diagonalisable operators¶
DiagonalizedOperator is an operator diagonal in a fast transform basis,
\(A = V^{-1}\Lambda V\), given by a forward/inverse transform pair (FFT,
orthonormal DCT/DST, spherical harmonics, or a dense eigenvector matrix) and
the eigenvalue array \(\Lambda\). solve, logdet, inv, sqrt and trace
are elementwise in \(\Lambda\), and shifts/scalings such as \(A - \lambda I\)
stay diagonalised, so a spectral Helmholtz solve is never materialised.
circulant / circulant_from_symbol are the FFT special case (periodic
stencils, stationary covariances on periodic grids), and a KroneckerSum whose
factors are all diagonalised solves through the composed per-axis transforms.
Use DiagonalizedOperator.from_eigen_factorization for dense non-symmetric
diagonalisable factors such as Chebyshev collocation blocks.
import jax.numpy as jnp
import lineax as lx
import gaussx
n = 128
k = 2 * jnp.pi * jnp.fft.fftfreq(n)
symbol = (2 * jnp.cos(k) - 2)[:, None] + (2 * jnp.cos(k) - 2)[None, :]
laplacian = gaussx.circulant_from_symbol(symbol) # periodic 5-point ∇²
helmholtz = laplacian - 1.0 * lx.IdentityLinearOperator(laplacian.in_structure())
psi = gaussx.solve(helmholtz, f) # two FFTs, no matrix
Structured linear algebra and Gaussian primitives for JAX.
DiagonalizedOperator
¶
Bases: AbstractLinearOperator
Linear operator A x = V⁻¹(Λ ⊙ V x) given by a transform pair.
forward applies V (field of shape in_shape → coefficients) and
inverse applies V⁻¹ (coefficients → field); eigenvalues holds
Λ in the coefficient layout. The operator acts on flat vectors of
length prod(in_shape), as every lineax operator does; mv reshapes
internally.
Examples of (forward, inverse, Λ):
- Periodic grid:
fftn/ifftnand the symbol of the stencil — seecirculantandcirculant_from_symbol. - Dirichlet / Neumann grid: an orthonormal DST / DCT pair and the corresponding finite-difference or spectral eigenvalues.
- A dense diagonalisable matrix
M = V Λ V⁻¹(e.g. a Chebyshev collocation block):DiagonalizedOperator.from_eigen_factorization.
Algebra stays closed. A + c·I, A − c·I, c·A, A / c,
−A and A ± B (for B with the same transform pair) return
another DiagonalizedOperator with updated eigenvalues, so a shifted
Helmholtz operator A − λI is never materialised.
Structured primitives. gaussx.solve divides by Λ (eigenvalues
that are exactly zero get a zero coefficient, i.e. the pseudo-inverse for
a normal operator); gaussx.logdet is Σ log|λ| (the slogdet
convention, so −inf for a singular operator); gaussx.inv,
gaussx.sqrt and gaussx.trace are elementwise in Λ. A
gaussx.KroneckerSum whose factors are all DiagonalizedOperator s is
solved through the composed per-axis transforms.
Transpose. With normal=True (V⁻¹ ∝ Vᴴ, e.g. FFT or orthonormal
DCT/DST), Aᵀ for a real operator (real_output=True) is the same
pair with conjugated eigenvalues; for a complex one it is the same
eigenvalues with the conjugated pair conj(V), conj(V)⁻¹, since
Aᵀ x = conj(Aᴴ conj(x)). Otherwise pass
transpose_pair=(forward_T, inverse_T) applying V⁻ᵀ and Vᵀ in
the same roles; without either, transpose raises.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
eigenvalues
|
Inexact[ArrayLike, ' ...']
|
|
required |
forward
|
Transform
|
|
required |
inverse
|
Transform
|
|
required |
in_shape
|
tuple[int, ...]
|
Field shape the transforms act on. |
required |
real_output
|
bool
|
Return the real part of |
True
|
normal
|
bool
|
Whether |
False
|
transpose_pair
|
tuple[Transform, Transform] | None
|
Optional |
None
|
tags
|
object | frozenset[object]
|
lineax tags. |
frozenset()
|
The transform callables are stored as static fields, so they must be
hashable (module-level functions, functools.partial of them, or
closures); keep them stable between calls to avoid jit recompiles.
Source code in src/gaussx/_operators/_diagonalised.py
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 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 | |
size: int
property
¶
Number of degrees of freedom, prod(in_shape).
from_eigen_factorization(factorization, *, tags: object | frozenset[object] = frozenset()) -> DiagonalizedOperator
classmethod
¶
Operator M = V Λ V⁻¹ from a gaussx.EigenDecomposition.
Uses the dense eigenvector matrices as the transform pair
(forward = V⁻¹ ·, inverse = V ·), so non-symmetric but
diagonalisable factors — e.g. Chebyshev collocation second-derivative
blocks — get an exact structured solve and transpose.
Source code in src/gaussx/_operators/_diagonalised.py
forward_flat(vector: Shaped[Array, ' n']) -> Shaped[Array, ' n']
¶
V on a flat vector, returning flat coefficients.
inverse_flat(coeffs: Shaped[Array, ' n']) -> Shaped[Array, ' n']
¶
V⁻¹ on flat coefficients, returning a flat vector.
eigenvalues_flat() -> Inexact[Array, ' n']
¶
with_eigenvalues(eigenvalues: Inexact[ArrayLike, ' ...']) -> DiagonalizedOperator
¶
Same transform pair with new eigenvalues.
Structural tags are recomputed: the automatic symmetric_tag is
re-derived, and definiteness tags (positive_semidefinite_tag,
negative_semidefinite_tag) are dropped because a shift or sign
change can invalidate them. Re-tag the result if you know it holds.
Source code in src/gaussx/_operators/_diagonalised.py
circulant(first_column: Inexact[ArrayLike, ' ...'], *, symmetric: bool | None = None, tags: object | frozenset[object] = frozenset()) -> DiagonalizedOperator
¶
(Block-)circulant operator from its first column.
A circulant matrix C[i, j] = c[(i − j) mod n] (and its n-D
block-circulant analogue, with first_column of the grid's shape) is
diagonalised by the DFT: C = F⁻¹ diag(F c) F. Periodic
finite-difference stencils and stationary covariances on periodic grids
are circulant.
Compare gaussx.Toeplitz, which represents a (non-periodic) symmetric
Toeplitz matrix with an FFT matvec by circulant embedding but has no
closed-form solve; a circulant matrix has exact elementwise
solve / logdet / sqrt.
A real even kernel, c[k] = c[−k mod n], has a real symbol and gives a
symmetric operator. Say so with symmetric=True: the decision then
depends only on the flag, so construction under jax.jit gives the same
eigenvalue dtype and tags as eager construction (gh-440).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
first_column
|
Inexact[ArrayLike, ' ...']
|
|
required |
symmetric
|
bool | None
|
|
None
|
tags
|
object | frozenset[object]
|
Additional lineax tags. |
frozenset()
|
Returns:
| Type | Description |
|---|---|
DiagonalizedOperator
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/gaussx/_operators/_diagonalised.py
circulant_from_symbol(symbol: Inexact[ArrayLike, ' ...'], *, real_output: bool = True, tags: object | frozenset[object] = frozenset()) -> DiagonalizedOperator
¶
(Block-)circulant operator from its DFT symbol.
A x = ifftn(symbol ⊙ fftn(x)) on a periodic grid of shape
symbol.shape. Use this when the eigenvalues are known in closed form,
e.g. the five-point Laplacian Σ_d (2 cos(2π k_d / n_d) − 2) / h_d² or
the spectral −|k|². A real-dtype symbol gives a symmetric operator
(tagged automatically); add lx.positive_semidefinite_tag /
negative_semidefinite_tag yourself when it applies.
With real_output=True the symbol must be conjugate-even,
symbol[k] = conj(symbol[−k mod n]) (for a real symbol: even), since
only then is ifftn(symbol ⊙ fftn(x)) real for real x. Any other
symbol would make mv apply the real part of the operator while
solve / logdet / inv / sqrt describe the complex one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symbol
|
Inexact[ArrayLike, ' ...']
|
Eigenvalues in |
required |
real_output
|
bool
|
Take the real part of |
True
|
tags
|
object | frozenset[object]
|
Additional lineax tags. |
frozenset()
|
Returns:
| Type | Description |
|---|---|
DiagonalizedOperator
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/gaussx/_operators/_diagonalised.py
as_diagonalized(operator: lx.AbstractLinearOperator) -> DiagonalizedOperator | None
¶
Express operator as one DiagonalizedOperator, if possible.
Returns the operator itself for a DiagonalizedOperator, the composed
operator for a gaussx.KroneckerSum whose factors are (recursively)
diagonalisable, and None otherwise.
For A ⊕ B with A = V_A⁻¹ Λ_A V_A and B = V_B⁻¹ Λ_B V_B,
A ⊕ B = (V_A ⊗ V_B)⁻¹ (Λ_A ⊕ Λ_B) (V_A ⊗ V_B): the forward transform
applies V_A along the leading axis of the (n_A, n_B) field and
V_B along the trailing one, and the eigenvalues are all pairwise sums
λ^A_i + λ^B_j.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
The operator to express in a transform basis. |
required |
Returns:
| Type | Description |
|---|---|
DiagonalizedOperator | None
|
The equivalent |
DiagonalizedOperator | None
|
is neither a |
DiagonalizedOperator | None
|
diagonalisable factors. |
Source code in src/gaussx/_operators/_diagonalised.py
SpectralFunction is \(f(A_1\oplus\cdots\oplus A_d)\) for symmetric factors,
stored as the factor eigendecompositions plus the elementwise function \(f\):
mv, solve, logdet, diag_inv (also with pinv=True) and sqrt_matmul
act one axis at a time through the shared eigenvectors. It is the operator
behind the SPDE (Matérn) precision on a regular grid,
spde_precision_grid, and as a factor of a shifted Kronecker
product \(A\otimes f(B) + cI\) it keeps its eigenbasis.
Structured linear algebra and Gaussian primitives for JAX.
SpectralFunction
¶
Bases: AbstractLinearOperator
f(B) for a symmetric B with a factored eigenbasis.
B is typically a Kronecker sum A₁ ⊕ … ⊕ A_d of symmetric factors
(a grid Laplacian L_H ⊕ L_W). With A_m = V_m Λ_m V_mᵀ,
so mv, solve, logdet, diag_inv and sqrt_matmul all act on the
(n_1, …, n_d) grid one axis at a time, O(N Σ_m n_m), and the
joint eigenbasis is never formed. The factor eigendecompositions are
computed once, at construction.
gaussx.solve, gaussx.logdet, gaussx.diag_inv (including
pinv=True) and gaussx.diag dispatch here, and a SpectralFunction
factor of a shifted Kronecker product A ⊗ f(B) + c·I keeps its
eigenbasis there too.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
AbstractLinearOperator
|
The symmetric operator |
required |
fn
|
Callable[[Array], Array]
|
The elementwise function |
required |
tags
|
object | frozenset[object]
|
lineax tags. |
frozenset()
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
import jax.numpy as jnp
import lineax as lx
import gaussx
def path_laplacian(n):
off = -jnp.ones(n - 1)
L = jnp.diag(jnp.r_[1.0, 2.0 * jnp.ones(n - 2), 1.0])
return lx.MatrixLinearOperator(
L + jnp.diag(off, 1) + jnp.diag(off, -1), lx.symmetric_tag
)
# (κ²I + L_H ⊕ L_W)², a Matérn nu = 1 precision on a 4 x 5 grid
base = gaussx.KroneckerSum(path_laplacian(4), path_laplacian(5))
Q = gaussx.SpectralFunction(base, lambda lam: (0.5 + lam) ** 2)
dense = jnp.linalg.matrix_power(0.5 * jnp.eye(20) + base.as_matrix(), 2)
assert jnp.allclose(Q.as_matrix(), dense, atol=1e-4)
assert jnp.allclose(gaussx.diag_inv(Q), jnp.diag(jnp.linalg.inv(dense)))
Source code in src/gaussx/_operators/_spectral_function.py
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 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | |
from_eigen_factorizations(factors: Sequence[EigenDecomposition], fn: Callable[[Array], Array], *, tags: object | frozenset[object] = frozenset()) -> SpectralFunction
classmethod
¶
f(A₁ ⊕ … ⊕ A_d) from precomputed symmetric factorizations.
Skips the eigh of __init__, so it is cheap inside jit; the
base is the nested gaussx.KroneckerSum of the reassembled factors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
factors
|
Sequence[EigenDecomposition]
|
One |
required |
fn
|
Callable[[Array], Array]
|
The elementwise function |
required |
tags
|
object | frozenset[object]
|
lineax tags. |
frozenset()
|
Returns:
| Type | Description |
|---|---|
SpectralFunction
|
The operator. |
Source code in src/gaussx/_operators/_spectral_function.py
spectrum() -> Float[Array, ' ...']
¶
f(λ) on the eigenvalue tensor, shape (n_1, …, n_d).
factored_eigen() -> FactoredEigen
¶
The factored eigendecomposition of f(B) (same basis as B).
solve(vector: Float[Array, ' n']) -> Float[Array, ' n']
¶
logdet() -> Float[Array, '']
¶
diag_inv(*, pinv: bool = False) -> Float[Array, ' n']
¶
diag(f(B)⁻¹) (or of the pseudo-inverse), from the factor bases.
diagonal() -> Float[Array, ' n']
¶
sqrt_matmul(vector: Float[Array, ' n'], *, inverse: bool = False) -> Float[Array, ' n']
¶
f(B)^{1/2} x, or f(B)^{-1/2} x with inverse=True.
The symmetric square root: with inverse=True and z ~ N(0, I),
f(B)^{-1/2} z is an exact sample with precision f(B).
Source code in src/gaussx/_operators/_spectral_function.py
Interpolated & masked operators¶
Grid-interpolated (KISS-GP style) and masked operators, plus the grid and
cubic-interpolation helpers that build them. Kernel operators themselves
(KernelOperator, ImplicitKernelOperator, ImplicitCrossKernelOperator)
moved to kernellib in gaussx 0.2.0;
they are lineax operators, so every gaussx primitive and solver strategy works
on them unchanged.
Structured linear algebra and Gaussian primitives for JAX.
InterpolatedOperator
¶
Bases: AbstractLinearOperator
Structured Kernel Interpolation: K \approx W K_{uu} W^T.
W is a sparse interpolation matrix with p nonzeros per row
(e.g. cubic interpolation weights). The base operator K_{uu}
acts on the inducing grid (typically Toeplitz for stationary
kernels).
Total matvec cost: O(n p + m log m) when the base is Toeplitz,
essentially linear in n.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_operator
|
AbstractLinearOperator
|
The inducing-point kernel |
required |
interp_indices
|
Int[Array, 'n p']
|
Integer indices into the inducing grid,
shape |
required |
interp_values
|
Float[Array, 'n p']
|
Interpolation weights, shape |
required |
tags
|
object | frozenset[object]
|
Extra lineax tags. Symmetry and positive/negative
semi-definiteness are inherited from |
frozenset()
|
Source code in src/gaussx/_operators/_interpolated.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 | |
MaskedOperator
¶
Bases: AbstractLinearOperator
Row/column-masked view of a base operator.
Given a base operator A of shape (N, N) and boolean masks,
produces the sub-matrix A[row_mask][:, col_mask].
Matvec is computed without materializing the sub-matrix: zero-pad input to full size, apply base matvec, then extract masked rows.
Fast masked solves (capacitance method). For a square mask
(row_mask == col_mask == m) whose base B has a fast solve (a
gaussx.DiagonalizedOperator, gaussx.circulant, or a Kronecker sum of
them), pass coupling_indices: the flat indices C of the
masked-out degrees of freedom that B couples to masked-in rows
(B[m, j] ≠ 0). Then gaussx.solve(op, f) solves
B[m][:, m] x = f exactly by the capacitance method: it finds y on
the full grid with y[C] = 0 and (B y)[i] = f[i] at every i ∉ C
(point sources on C absorb the constraints), which on m is exactly
the masked system because the remaining masked-out entries do not couple
to m. The |C| × |C| capacitance factorisation is built once, here
in the constructor, so each solve costs two base solves.
For a local stencil (e.g. a finite-difference Laplacian on a grid) C
is the one-cell ring outside the mask — use grid_coupling_indices, with
periodic=True on axes where B wraps. Passing all masked-out
indices is always exact (for any B) but costs O(|C|) base solves
to build.
If B is singular (e.g. a periodic Poisson operator, whose solve is the
pseudo-inverse), its null vector must enter the capacitance system (see
gaussx.CapacitanceSolver). For a diagonalised B with a normal
transform and exactly one zero eigenvalue this is derived automatically;
otherwise pass null_vector (and left_null_vector if B is not
symmetric).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
AbstractLinearOperator
|
The underlying |
required |
row_mask
|
Bool[Array, ' N']
|
Boolean mask of length N selecting output rows. |
required |
col_mask
|
Bool[Array, ' N']
|
Boolean mask of length N selecting input columns. |
required |
coupling_indices
|
Int[Array, ' Nc'] | None
|
Optional flat indices |
None
|
null_vector
|
Float[Array, ' N'] | None
|
Right null vector of a singular |
None
|
left_null_vector
|
Float[Array, ' N'] | None
|
Left null vector of a singular non-symmetric
|
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
capacitance |
CapacitanceSolver | None
|
The precomputed |
The capacitance is a cache of base. Gradients with respect to
base are exact, but replacing base in an existing operator (e.g.
eqx.apply_updates after an optimiser step, or eqx.tree_at) leaves
the factorisation describing the old base, and solve is then
wrong. Rebuild with with_base, or construct the operator inside the
loss from the current parameters. That also works under jit, except
that a traced base's null vector cannot be derived: for a singular base
built under jit pass null_vector (and left_null_vector)
explicitly. A traced diagonalised base with a zero eigenvalue and no
null_vector raises at run time rather than solving the wrong system.
Source code in src/gaussx/_operators/_masked.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 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | |
with_base(base: lx.AbstractLinearOperator, *, null_vector: Float[Array, ' N'] | None = None, left_null_vector: Float[Array, ' N'] | None = None) -> MaskedOperator
¶
The same masked view of a new base, with its capacitance rebuilt.
Use this after updating base (e.g. an optimiser step): the
capacitance factorisation is a cache of the base and does not follow
leaf updates. Costs the |C| base solves of construction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
AbstractLinearOperator
|
The new |
required |
null_vector
|
Float[Array, ' N'] | None
|
Right null vector of a singular |
None
|
left_null_vector
|
Float[Array, ' N'] | None
|
Left null vector of a singular non-symmetric
|
None
|
Returns:
| Type | Description |
|---|---|
MaskedOperator
|
A new |
MaskedOperator
|
coupling indices. |
Source code in src/gaussx/_operators/_masked.py
grid_coupling_indices(mask: Bool[Array, ' *grid'], *, periodic: bool | tuple[bool, ...] = False, connectivity: int = 1) -> Int[Array, ' Nc']
¶
Flat indices of masked-out grid cells adjacent to the masked-in region.
This is the coupling set C that MaskedOperator needs for a local
stencil operator on a regular grid: every cell outside mask that the
stencil of some cell inside mask reaches. With connectivity=1
neighbours differ by ±1 along one axis (the 5-point / 7-point stencil);
connectivity=mask.ndim includes diagonals (9-point / 27-point).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mask
|
Bool[Array, ' *grid']
|
Boolean grid, |
required |
periodic
|
bool | tuple[bool, ...]
|
Whether each axis wraps around (a bool applies to all
axes). Must match the base operator: a periodic operator couples
cells across the edge, so an edge-touching mask needs the wrapped
neighbours in |
False
|
connectivity
|
int
|
Maximum number of axes a neighbour offset may change. |
1
|
Returns:
| Type | Description |
|---|---|
Int[Array, ' Nc']
|
Flat (row-major) indices of the coupled masked-out cells. |
Source code in src/gaussx/_operators/_masked.py
create_grid(grid_sizes: list[int] | tuple[int, ...], grid_bounds: list[tuple[float, float]] | tuple[tuple[float, float], ...]) -> list[Float[Array, ' n']]
¶
Create a regular grid from per-dimension sizes and bounds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_sizes
|
list[int] | tuple[int, ...]
|
Number of points per dimension, length D. |
required |
grid_bounds
|
list[tuple[float, float]] | tuple[tuple[float, float], ...]
|
(lo, hi) bounds per dimension, length D. |
required |
Returns:
| Type | Description |
|---|---|
list[Float[Array, ' n']]
|
List of 1-D arrays, one per dimension. |
Source code in src/gaussx/_operators/_grid.py
grid_data(grid: list[Float[Array, ' n']]) -> Float[Array, 'G D']
¶
Expand a grid to the full Cartesian product of grid points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
list[Float[Array, ' n']]
|
List of 1-D arrays, one per dimension. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, 'G D']
|
Shape |
Source code in src/gaussx/_operators/_grid.py
cubic_interpolation_weights(x_target: Float[Array, 'B D'], grid: list[Float[Array, ' n']]) -> tuple[Int[Array, 'B K'], Float[Array, 'B K']]
¶
Compute cubic interpolation indices and weights for SKI.
For each target point, finds the 4ᴰ nearest grid neighbors and computes cubic convolution interpolation weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_target
|
Float[Array, 'B D']
|
Target points, shape |
required |
grid
|
list[Float[Array, ' n']]
|
List of 1-D arrays, one per dimension (length D). Each dimension must have ≥ 4 points. |
required |
Returns:
| Type | Description |
|---|---|
Int[Array, 'B K']
|
Tuple |
Float[Array, 'B K']
|
|
tuple[Int[Array, 'B K'], Float[Array, 'B K']]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If any grid dimension has fewer than 4 points. |
Source code in src/gaussx/_operators/_grid.py
Lazy algebra & sampling¶
Sum / scale / compose operators without materializing, sample \(\varepsilon \sim \mathcal{N}(0, A)\) for the structured families, and solve bordered systems through the capacitance (Schur-complement) form.
Structured linear algebra and Gaussian primitives for JAX.
CapacitanceSolver
¶
Bases: Module
Solve a base system subject to homogeneous point constraints.
Given a fast base solver B^{-1} and a set of N_b constrained indices
b_1, …, b_{N_b}, this returns x with x[b_k] = 0 and
B x = f at every unconstrained index, via the capacitance-matrix
correction:
- Base solve:
u = B^{-1} f - Sample boundary:
u_b = u[boundary] - Correction:
α = C^{-1} u_b - Subtract:
x = u − B^{-1}(Σ_k α_k e_{b_k})
where C[k, l] = (B^{-1} e_{b_l})[b_k] is the N_b × N_b capacitance
matrix, LU-factorized once at construction.
Cost and storage: only the factorized C is stored — O(N_b²)
memory. The Green's functions B^{-1} e_{b_k} are never kept; step 4 is
a second base solve with the point sources scattered into a zero field.
Each call therefore costs two base solves plus an O(N_b²)
back-substitution. Construction costs N_b base solves (run one column
at a time under jit, so no dense (N_b, n) array is formed).
Singular base operators. If B is singular with right null vector
r (e.g. the constant for a periodic or Neumann Poisson operator) and
base_solve is the pseudo-inverse that projects out that mode, the plain
method leaves the null-mode component undetermined and the interior
residual is a nonzero constant. Pass null_vector=r (and, for a
non-symmetric B, left_null_vector=w with wᵀ B = 0; it defaults
to r). The solution is then x = u − B^{+}(Σ α_k e_{b_k}) + β r with
(α, β) from the bordered system
[ C −r_b ] [α] [ u_b ]
[ w_bᵀ 0 ] [β] = [ wᵀ f ]
whose second row makes the effective source f − Σ α_k e_{b_k}
orthogonal to the left null space, so that B x = f holds exactly away
from the constrained indices. Without null_vector a singular base
cannot be detected here, so callers must pass it.
The solver operates on flat vectors. Any reshaping between fields and flat vectors, and any masking of the exterior, is the caller's responsibility -- keeping grid/mask concepts out of this class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_solve
|
Callable[[Float[Array, ' n']], Float[Array, ' n']]
|
Callable applying the base inverse |
required |
boundary_indices
|
Int[Array, ' Nb']
|
Flat indices of the constrained degrees of freedom,
shape |
required |
n
|
int
|
Length of the flat solution vector. |
required |
null_vector
|
Float[Array, ' n'] | None
|
Right null vector |
None
|
left_null_vector
|
Float[Array, ' n'] | None
|
Left null vector |
None
|
keep_base_solve
|
bool
|
When |
True
|
Attributes:
| Name | Type | Description |
|---|---|---|
base_solve |
Callable[[Float[Array, ' n']], Float[Array, ' n']] | None
|
The base inverse callable (static), or |
boundary_indices |
Int[Array, ' Nb']
|
The constrained indices. |
capacitance_lu |
tuple[Float[Array, 'm m'], Int[Array, ' m']]
|
LU factorization |
null_vector |
Float[Array, ' n'] | None
|
|
left_null_vector |
Float[Array, ' n'] | None
|
|
transposed |
bool
|
Whether this solver is the |
Source code in src/gaussx/_operators/_capacitance.py
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 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 | |
transpose() -> CapacitanceSolver
¶
The capacitance solver for Bᵀ, reusing this factorisation.
Cᵀ is the capacitance matrix of Bᵀ on the same indices, and
Bᵀ's right/left null vectors are B's left/right ones, so no
base solve is needed. The result stores no base_solve: pass
Bᵀ's inverse to __call__.
Source code in src/gaussx/_operators/_capacitance.py
sum_operator(*operators: lx.AbstractLinearOperator, tags: object | frozenset[object] = frozenset()) -> lx.AbstractLinearOperator
¶
Lazy sum (A + B + …) v = A v + B v + ….
Defers materialization so that structured sub-operators keep their
efficient matvec. All operators must have the same input and output
sizes. Returns a (possibly tagged) chain of lineax
AddLinearOperator compositions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*operators
|
AbstractLinearOperator
|
Two or more |
()
|
tags
|
object | frozenset[object]
|
Optional explicit lineax tags for the combined operator. |
frozenset()
|
Returns:
| Type | Description |
|---|---|
AbstractLinearOperator
|
The lazy sum as a lineax operator. |
Source code in src/gaussx/_operators/_lazy_algebra.py
scaled_operator(operator: lx.AbstractLinearOperator, scalar: float | Float[Array, ''], *, tags: object | frozenset[object] = frozenset()) -> lx.AbstractLinearOperator
¶
Lazy scalar multiply (c A) v = c (A v).
Returns a (possibly tagged) lineax MulLinearOperator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
A |
required |
scalar
|
float | Float[Array, '']
|
A scalar multiplier. |
required |
tags
|
object | frozenset[object]
|
Optional explicit lineax tags for the scaled operator. |
frozenset()
|
Returns:
| Type | Description |
|---|---|
AbstractLinearOperator
|
The lazy scaled operator. |
Source code in src/gaussx/_operators/_lazy_algebra.py
product_operator(left: lx.AbstractLinearOperator, right: lx.AbstractLinearOperator, *, tags: object | frozenset[object] = frozenset()) -> lx.AbstractLinearOperator
¶
Lazy matmul (A B) v = A (B v).
The inner dimension must match: left.in_size() == right.out_size().
Returns a (possibly tagged) lineax ComposedLinearOperator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left
|
AbstractLinearOperator
|
The left operator A. |
required |
right
|
AbstractLinearOperator
|
The right operator B. |
required |
tags
|
object | frozenset[object]
|
Optional explicit lineax tags for the composed operator. |
frozenset()
|
Returns:
| Type | Description |
|---|---|
AbstractLinearOperator
|
The lazy product as a lineax operator. |
Source code in src/gaussx/_operators/_lazy_algebra.py
kronecker_sum_sample(A_op: lx.AbstractLinearOperator, B_op: lx.AbstractLinearOperator, *, key: jax.Array, num_samples: int = 1) -> Float[Array, 'num_samples n_a n_b']
¶
Sample from 𝒩(0, A ⊕ B) using per-factor eigendecompositions.
Draws zero-mean samples with covariance A ⊕ B by applying the
matrix-free KroneckerSumSqrt to standard normal noise, avoiding
materialization of the full (n_a · n_b, n_a · n_b) covariance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
A_op
|
AbstractLinearOperator
|
Symmetric PSD factor, shape |
required |
B_op
|
AbstractLinearOperator
|
Symmetric PSD factor, shape |
required |
key
|
Array
|
PRNG key for the standard normal draws. |
required |
num_samples
|
int
|
Number of samples to draw. |
1
|
Returns:
| Type | Description |
|---|---|
Float[Array, 'num_samples n_a n_b']
|
Samples of shape |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/gaussx/_operators/_kronecker_sum.py
sum_of_kroneckers_sample(op: SumOfKroneckers, *, key: jax.Array, num_samples: int = 1, lanczos_order: int = 50) -> Float[Array, 'num_samples n']
¶
Sample from 𝒩(0, op) with matrix-free Lanczos square roots.
The square-root action is evaluated by matfree Lanczos against
op.mv. This avoids materialising the dense (n_A n_B) ×
(n_A n_B) covariance and costs lanczos_order operator
matvecs per sample.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
op
|
SumOfKroneckers
|
Positive-semidefinite |
required |
key
|
Array
|
JAX PRNG key. |
required |
num_samples
|
int
|
Number of independent samples to draw. |
1
|
lanczos_order
|
int
|
Lanczos truncation order. |
50
|
Returns:
| Type | Description |
|---|---|
Float[Array, 'num_samples n']
|
Samples with shape |
Source code in src/gaussx/_operators/_sum_kronecker.py
toeplitz_sample(column: Float[Array, ' n'], *, key: jax.Array, num_samples: int = 1, embedding_factor: int = 2) -> Float[Array, 'num_samples n']
¶
Sample from 𝒩(0, Toeplitz(column)) via FFT circulant embedding.
The Wood--Chan non-negativity check is JIT-friendly (via
eqx.error_if). If the embedding fails for the given
embedding_factor, the error fires at evaluation time — bump
embedding_factor (typically 4 or 8) for difficult
covariances rather than relying on a runtime fallback.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
column
|
Float[Array, ' n']
|
First column of the covariance matrix. |
required |
key
|
Array
|
JAX PRNG key used to draw white noise. |
required |
num_samples
|
int
|
Number of independent samples to draw. |
1
|
embedding_factor
|
int
|
Circulant embedding size as a multiple of |
2
|
Returns:
| Type | Description |
|---|---|
Float[Array, 'num_samples n']
|
Samples with shape |
Source code in src/gaussx/_operators/_toeplitz.py
Structural tags & predicates¶
Tags mark structure and properties on operators; the is_* predicates are what
the primitives consult when choosing an algorithm. The property tags
(positive_semidefinite_tag, symmetric_tag, the triangular tags, …) are
re-exported from lineax so user code only needs one import.
is_eigen_reducible reports whether a SumOfKroneckers
(or the equivalent sum_operator chain) takes the exact two-term reduction.
Structured linear algebra and Gaussian primitives for JAX.
is_diagonal = lx.is_diagonal
module-attribute
¶
is_symmetric = lx.is_symmetric
module-attribute
¶
is_positive_semidefinite = lx.is_positive_semidefinite
module-attribute
¶
is_negative_semidefinite = lx.is_negative_semidefinite
module-attribute
¶
is_lower_triangular = lx.is_lower_triangular
module-attribute
¶
is_upper_triangular = lx.is_upper_triangular
module-attribute
¶
kronecker_tag = _Tag('kronecker_tag')
module-attribute
¶
Operator is a Kronecker product.
kronecker_sum_tag = _Tag('kronecker_sum_tag')
module-attribute
¶
Operator is a Kronecker sum A (+) B = A (x) I_b + I_a (x) B.
block_diagonal_tag = _Tag('block_diagonal_tag')
module-attribute
¶
Operator is block diagonal.
block_tridiagonal_tag = _Tag('block_tridiagonal_tag')
module-attribute
¶
Operator is block tridiagonal.
low_rank_tag = _Tag('low_rank_tag')
module-attribute
¶
Operator has low-rank structure (e.g. L + U D V^T).
diagonal_tag = lx.diagonal_tag
module-attribute
¶
symmetric_tag = lx.symmetric_tag
module-attribute
¶
positive_semidefinite_tag = lx.positive_semidefinite_tag
module-attribute
¶
negative_semidefinite_tag = lx.negative_semidefinite_tag
module-attribute
¶
lower_triangular_tag = lx.lower_triangular_tag
module-attribute
¶
upper_triangular_tag = lx.upper_triangular_tag
module-attribute
¶
tridiagonal_tag = lx.tridiagonal_tag
module-attribute
¶
unit_diagonal_tag = lx.unit_diagonal_tag
module-attribute
¶
is_kronecker(operator: lx.AbstractLinearOperator) -> bool
¶
Check whether operator carries the Kronecker tag.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator to inspect. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/gaussx/_tags.py
is_kronecker_sum(operator: lx.AbstractLinearOperator) -> bool
¶
Check whether operator carries the Kronecker sum tag.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator to inspect. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/gaussx/_tags.py
is_block_diagonal(operator: lx.AbstractLinearOperator) -> bool
¶
Check whether operator carries the block-diagonal tag.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator to inspect. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/gaussx/_tags.py
is_block_tridiagonal(operator: lx.AbstractLinearOperator) -> bool
¶
Check whether operator carries the block-tridiagonal tag.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator to inspect. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
|
Source code in src/gaussx/_tags.py
is_low_rank(operator: lx.AbstractLinearOperator) -> bool
¶
Check whether operator carries the low-rank tag.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Linear operator to inspect. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/gaussx/_tags.py
is_eigen_reducible(operator: lx.AbstractLinearOperator) -> bool
¶
Whether a sum of Kronecker products has the exact two-term reduction.
"Eigen-reducible" means operator is A₁ ⊗ B₁ + A₂ ⊗ B₂ — a
two-term SumOfKroneckers, or the same two terms built with
sum_operator — whose main factors are symmetric and whose other
("anchor") term is positive definite. Then whitening by the anchor
leaves one eigendecomposition per factor, so gaussx.solve,
gaussx.logdet and SumOfKroneckers.eigendecompose cost
O(n_A³ + n_B³) instead of the O((n_A n_B)³) dense fallback.
Three or more terms are never eigen-reducible.
A purely structural query — operator types and lineax tags only, no
array work — so it is safe to call from solver-strategy selection and
under jax.jit. The exact path can still decline an operator this
accepts, when a diagonal anchor factor is concretely non-positive.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
AbstractLinearOperator
|
Any lineax operator. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/gaussx/_operators/_sum_kronecker.py
Deprecated aliases¶
These names still work but warn on use and will be removed in gaussx 0.7.0 (see the deprecation policy). Each one names its replacement.
Structured linear algebra and Gaussian primitives for JAX.
SumKronecker
¶
Bases: SumOfKroneckers
Deprecated alias for SumOfKroneckers.
The old name was one word-order away from KroneckerSum, a
different operator with a different eigendecomposition — picking the wrong
one produced silently wrong solves. Renamed in gh-136.
Subclasses SumOfKroneckers so isinstance checks and
singledispatch registrations keyed on this class keep working, and
emits a DeprecationWarning on construction. Structural operations that
rebuild the operator — transpose / .T — preserve this type, so
they warn again. Note that instances built via SumOfKroneckers are
not instances of this subclass; internal dispatch keys on the parent.
Will be removed in gaussx 0.7.0.
Source code in src/gaussx/_operators/_sum_kronecker.py
SumKroneckerSqrt
¶
Bases: SumOfKroneckersSqrt
Deprecated alias for SumOfKroneckersSqrt (gh-297).
Subclasses SumOfKroneckersSqrt so isinstance checks keep working,
and emits a DeprecationWarning on construction. gaussx.sqrt returns
the new class, which is not an instance of this alias. Will be removed
in gaussx 0.7.0.
Source code in src/gaussx/_primitives/_sqrt.py
SVDLowRankUpdate
¶
Bases: LowRankUpdate
Deprecated subclass of LowRankUpdate with orthonormal=True.
Preserves the pre-consolidation public API until gaussx 0.7.0:
- Same constructor signature as the old class —
Sdefaults to ones (via the parentLowRankUpdate) if omitted, andVdefaults toUso calls likeSVDLowRankUpdate(base, U, S)andSVDLowRankUpdate(base, U)continue to work. - Inherits from
LowRankUpdatesoisinstance/issubclasschecks andsingledispatchregistrations keyed on this class keep working. - Forces
orthonormal=Trueand emits aDeprecationWarningon construction.
New code should construct LowRankUpdate(base, U, S, V,
orthonormal=True) (or use svd_low_rank_plus_diag)
directly. Will be removed in gaussx 0.7.0.
Source code in src/gaussx/_operators/_low_rank_update.py
sumkronecker_sample(op: SumOfKroneckers, *, key: jax.Array, num_samples: int = 1, lanczos_order: int = 50) -> Float[Array, 'num_samples n']
¶
Deprecated alias for sum_of_kroneckers_sample (gh-297).
Will be removed in gaussx 0.7.0.