Linear Algebra API#
Linear algebra routines operate on SpaceCore spaces and operators. Preconditions are mathematical: square, Hermitian, positive-definite, and residual statements refer to the domain and codomain inner products, not only coordinate arrays.
Linear solves#
Solve \(A x = b\) by conjugate gradients. |
|
Result of every |
LinalgResultis returned by every solver below:x,converged,num_itersandresidual_norm, pluseigenvalueornormal_residual_normwhere the solver produces them.
Least squares#
Solve \(\min_x \|A x - b\|\) by LSQR. |
Eigenvalue and spectral methods#
Approximate the smallest eigenpair of a Hermitian operator by Lanczos. |
|
Estimate the dominant eigenpair (largest in absolute value) of a self-adjoint action. |
Matrix functions#
Compute \(\exp(t A) v\) by Lanczos projection, without forming |
Native elements and JAX#
lanczos_smallest and expm_multiply retain native space elements and use
zeros, add, scale, and inner together with the operator action.
They do not require a coordinate shape or flatten/unflatten methods.
Built-in dense spaces retain their vectorized basis operations. Custom elements
are held directly during eager execution; on JAX they must be registered pytrees
with fixed structure, leaf shapes, and dtypes throughout the recurrence, including
space.zeros(). Basis capacity is fixed by the static max_iter argument.
Mathematical linear combinations always use space algebra, even when the
representation has multiple array leaves.
Lanczos requires a nonzero starting element. A zero start raises ValueError
eagerly. Under JAX tracing, it returns a NaN eigenvalue, a zero eigenvector,
num_iters=0, and converged=False. This replaces the previous coordinate
vector fallback. expm_multiply accepts zero and returns zero with
num_iters=0 and a zero residual_norm.
Autodoc#
- spacecore.linalg.cg(A, b, *, x0=None, tol=1e-06, atol=0.0, maxiter=None, check_every=64, preconditioner=None)[source]#
Solve \(A x = b\) by conjugate gradients.
Amust be square (A.domain == A.codomain), Hermitian and positive-definite in the domain’s inner product. OnlyA.applyand that inner product are used.- Parameters:
A (LinOp) – Hermitian positive-definite operator. One that is provably not Hermitian is rejected; definiteness is not checked.
b (array-like) – Right-hand side in
A.codomain.x0 (array-like or None, optional) – Initial guess; zero by default.
tol (float, optional) – Converged when the residual norm is below
atol + tol * norm(b).atol (float, optional) – Absolute residual tolerance.
maxiter (int or None, optional) – Iteration cap;
prod(A.domain.shape)by default.check_every (int, optional) – Test convergence every this many iterations, and on the last one.
preconditioner (LinOp or None, optional) – Operator
KonA.domainapproximating the inverse ofA, self-adjoint and positive definite inA.domain; each residualris preconditioned toK r. The residual is still measured inA.domain’s norm.
- Returns:
(x, converged, num_iters, residual_norm).- Return type:
- Raises:
TypeError – If
Ais not aLinOp.ValueError – If
Ais not square or an iteration parameter is invalid.
See also
lsqrLeast squares for rectangular operators.
Notes
JIT-compatible on JAX with
maxiterandcheck_everystatic. Stops early when no useful update remains (residual at machine precision, or non-positive curvature). Trefethen & Bau, Numerical Linear Algebra, Algorithm 38.1. In an orthonormal basis the metric recurrence is ordinary CG; complex spaces use conjugate inner products, and a nonzerox0starts fromb - A x0.Examples
Solve a small positive-definite system.
>>> import numpy as np >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((3,), ctx) >>> M = ctx.asarray([[4.0, 1.0, 0.0], [1.0, 3.0, 1.0], [0.0, 1.0, 2.0]]) >>> A = sc.DenseLinOp(M, X, X, ctx) >>> b = ctx.asarray([1.0, 2.0, 3.0]) >>> result = sc.cg(A, b, tol=1e-10) >>> bool(result.converged) True >>> np.allclose(A.apply(result.x), b) True
- class spacecore.linalg.LinalgResult(x, converged, num_iters, residual_norm, eigenvalue=None, normal_residual_norm=None)[source]#
Bases:
NamedTupleResult of every
spacecore.linalgsolver.Fields a solver does not produce are
None.- Parameters:
x (array-like) – The computed element: the solution (
cg(),lsqr()), the eigenvector estimate (power_iteration(),lanczos_smallest()), or the actionexp(t * A) @ v(expm_multiply()).converged (bool-like) – Whether the solver’s stopping test was met.
num_iters (int-like) – Iterations executed; for the Krylov methods
lanczos_smallest()andexpm_multiply(), the Krylov dimension reached.residual_norm (scalar) – The solver’s residual measure:
norm(b - A x)forcg()andlsqr()(exact or estimated perresidual_mode),norm(A x - eigenvalue x)forpower_iteration(), the Ritz residual estimate forlanczos_smallest(), and forexpm_multiply()the projection indicatorabs(beta[m] phi[m-1]), which omitsnorm(v)and does not bound the error.eigenvalue (scalar or None) – Eigenvalue estimate (
power_iteration(),lanczos_smallest()).normal_residual_norm (scalar or None) –
norm(A.H (A x - b))(lsqr()).
- x: Any#
Alias for field number 0
- converged: Any#
Alias for field number 1
- num_iters: Any#
Alias for field number 2
- residual_norm: Any#
Alias for field number 3
- eigenvalue: Any#
Alias for field number 4
- normal_residual_norm: Any#
Alias for field number 5
- count(value, /)#
Return number of occurrences of value.
- index(value, start=0, stop=sys.maxsize, /)#
Return first index of value.
Raises ValueError if the value is not present.
- spacecore.linalg.lsqr(A, b, *, x0=None, tol=1e-06, atol=0.0, maxiter=None, check_every=64, residual_mode='exact')[source]#
Solve \(\min_x \|A x - b\|\) by LSQR.
Amay map between different spaces. OnlyA.applyandA.H.applyare used; the normal equations are never formed.- Parameters:
A (LinOp) – Operator from
A.domaintoA.codomain.b (array-like) – Right-hand side in
A.codomain.x0 (array-like or None, optional) – Initial guess; zero by default.
tol (float, optional) – Converged when
norm(A.H @ (A @ x - b))is belowatol + tol * norm(b).atol (float, optional) – Absolute tolerance on that normal-equation residual.
maxiter (int or None, optional) – Iteration cap;
prod(A.domain.shape)by default.check_every (int, optional) – Refresh residual diagnostics every this many iterations, and on the last one.
residual_mode ({"exact", "recurrence"}, optional) –
"exact"recomputes both residuals at each check, costing one extra forward and adjoint apply;"recurrence"uses LSQR’s scalar estimates for free.
- Returns:
With
normal_residual_normset.- Return type:
- Raises:
TypeError – If
Ais not aLinOp.ValueError – If an iteration parameter or
residual_modeis invalid.
See also
cgSquare Hermitian positive-definite systems.
Notes
JIT-compatible on JAX with
maxiter,check_everyandresidual_modestatic. A small normal-equation residual does not imply a small error whenAis ill-conditioned: the error solvesA.H A e = A.H (A x - b). Golub & Van Loan, Matrix Computations, 4th ed., Sec. 11.4.2. Orthonormal bases in both spaces give ordinary LSQR; complex spaces replace transposes by conjugate transposes in the bidiagonalization.Examples
Solve a small overdetermined least-squares problem.
>>> import numpy as np >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((2,), ctx) >>> Y = sc.DenseCoordinateSpace((3,), ctx) >>> M = ctx.asarray([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]) >>> A = sc.DenseLinOp(M, X, Y, ctx) >>> b = ctx.asarray([1.0, 2.0, 3.0]) >>> result = sc.lsqr(A, b, tol=1e-10) >>> np.allclose(result.x, [1.0, 2.0]) True
- spacecore.linalg.lanczos_smallest(A, initial_vector, *, max_iter=100, tol=1e-06, check_every=64)[source]#
Approximate the smallest eigenpair of a Hermitian operator by Lanczos.
The eigenvalue returned is the Rayleigh quotient of the Ritz vector, reconstructed in
A.domain.- Parameters:
A (LinOp) – Square operator, Hermitian in the domain’s inner product. Known non-Hermitian operators are rejected; unknown ones are trusted.
initial_vector (array-like) – Nonzero starting element in
A.domain.max_iter (int, optional) – Maximum Krylov dimension; a static argument under
jax.jit.tol (float, optional) – Breakdown threshold, and the residual below which
convergedis set.check_every (int, optional) – Test for breakdown every this many iterations, and on the last one.
- Returns:
xis the Ritz vector,eigenvaluethe Ritz value,num_itersthe Krylov dimension reached.- Return type:
- Raises:
TypeError – If
Ais not aLinOp.ValueError – If
Ais not square, is known non-Hermitian,max_iteris invalid, or the starting element is zero (eager execution).
See also
power_iterationThe dominant eigenpair.
Notes
residual_normis the recurrence estimate \(\beta_m |y_{m-1}|\). The smallest Ritz value can recover the smallest eigenvalue only ifinitial_vectoroverlaps its eigenspace. Trefethen & Bau, Numerical Linear Algebra, Algorithm 36.1 and Thm 36.1; multiplying the Lanczos relation by a unit Ritz vector gives the residual. Uses the space algebra and native elements. Custom JAX elements must be registered pytrees whose structure, leaf shapes, and dtypes stay fixed.max_iterandcheck_everymust be static underjax.jit. A zero start raises eagerly; under JAX tracing it returns a NaN eigenvalue, a zero eigenvector, andconverged=False.Examples
Approximate the smallest eigenpair of a diagonal operator.
>>> import numpy as np >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((3,), ctx) >>> A = sc.DiagonalLinOp(ctx.asarray([1.0, 2.0, 4.0]), X, ctx) >>> result = sc.lanczos_smallest(A, ctx.asarray([1.0, 1.0, 1.0]), max_iter=3) >>> np.allclose(result.eigenvalue, 1.0) True
- spacecore.linalg.power_iteration(A, *, x0=None, tol=1e-06, maxiter=None)[source]#
Estimate the dominant eigenpair (largest in absolute value) of a self-adjoint action.
- Parameters:
A (LinOp or QuadraticForm) – Square operator, or quadratic form with
hess_apply. A form may also supplyhess_quad(x, Hx=None)andhess_residual_norm(x, Hx, eigenvalue)for the diagnostics. For a spectral norm of a rectangularB, passB.H @ B.x0 (array-like or None, optional) – Initial vector; normalized ones by default.
tol (float, optional) – Converged when
norm(A @ x - eigenvalue * x) < tol.maxiter (int or None, optional) – Iteration cap;
prod(A.domain.shape)by default.
- Returns:
xis the eigenvector estimate;eigenvalueits Rayleigh quotient.- Return type:
- Raises:
TypeError – If
Ais neither aLinOpnor aQuadraticForm.ValueError – If a linear operator is not square or known non-Hermitian, or an iteration parameter is invalid.
See also
lanczos_smallestThe smallest eigenpair.
Notes
Convergence needs a separated dominant modulus and nonzero initial overlap with its eigenspace. Trefethen & Bau, Numerical Linear Algebra, Thm 27.1; expansion in a metric-orthonormal eigenbasis gives the same eigenvalue-ratio argument. With both
lambdaand-lambdapresent at the dominant modulus the iterate can oscillate. JIT-compatible on JAX withmaxiterstatic.Examples
Estimate the largest eigenvalue of a diagonal operator.
>>> import numpy as np >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((3,), ctx) >>> A = sc.DiagonalLinOp(ctx.asarray([1.0, 3.0, 2.0]), X, ctx) >>> result = sc.power_iteration(A, maxiter=20, tol=1e-10) >>> np.allclose(result.eigenvalue, 3.0) True
- spacecore.linalg.expm_multiply(A, v, t=1.0, *, max_iter=30, tol=1e-10)[source]#
Compute \(\exp(t A) v\) by Lanczos projection, without forming
A.- Parameters:
A (LinOp) – Square operator, Hermitian in the domain’s inner product. Known non-Hermitian operators are rejected; unknown ones are trusted.
v (array-like) – Element in
A.domain; zero returns zero without applyingA.t (float or complex, optional) – Scalar multiplying
A; a complextneeds a complexctx.dtype.max_iter (int, optional) – Maximum Krylov dimension. Static under
jax.jit.tol (float, optional) – Breakdown threshold, and the residual below which
convergedis set.
- Returns:
xis the result vector,num_itersthe Krylov dimension reached, andresidual_normthe normalized projection indicator \(|\beta_m \phi_{m-1}|\). The approximation isnorm(v) V_m exp(t T_m) e_1in a metric-orthonormal basis. Higham, Functions of Matrices, Sec. 13.2.2, Eq. (13.7).- Return type:
- Raises:
TypeError – If
Ais not aLinOp.ValueError – If
Ais not square, is known non-Hermitian, ormax_iteris invalid.
See also
lanczos_smallestThe same Krylov projection, for eigenpairs.
Examples
Apply the exponential of a diagonal operator.
>>> import numpy as np >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((2,), ctx) >>> A = sc.DiagonalLinOp(ctx.asarray([0.0, 1.0]), X, ctx) >>> v = ctx.asarray([2.0, 3.0]) >>> result = sc.expm_multiply(A, v, t=0.5, max_iter=5) >>> np.allclose(result.x, [2.0, 3.0 * np.exp(0.5)], atol=1e-10) True