Linear Operators API#
Linear operators represent typed maps \(A : X \to Y\). apply evaluates
\(A x\); rapply evaluates the metric adjoint \(A^\sharp y\), which
matches the coordinate conjugate transpose only when both spaces are Euclidean.
Matrix-backed operators#
Linear operator stored as a dense array of shape |
|
Linear operator stored as a sparse matrix of shape |
|
Coordinatewise map |
Matrix-free and canonical operators#
A linear map from |
|
Linear operator given by forward and adjoint callables; no matrix is stored. |
|
Lazy identity map on |
|
Lazy zero map from |
MatrixFreeLinOpwraps forward and metric-adjoint callables exactly as supplied; it does not derive or Riesz-correct matrix-free adjoints.
Algebraic operators#
Lazy sum of operators sharing context, domain and codomain. |
|
Lazy composition |
|
Lazy |
|
Return the lazy sum of operators, with nested sums flattened and zero terms dropped. |
|
Return |
|
Return |
Helper constructors perform the same simplifications used by Python operator overloads.
Tree and block operators#
Define a base class for operators assembled from component operators. |
|
Independent blocks acting on the leaves of a direct-product tree. |
|
Block matrix over direct products: |
BlockDiagonalLinOp(blocks)maps corresponding tree leaves independently and infers bothTreeSpaceendpoints from the block domains and codomains.BlockMatrixLinOp(block_rows)computes row sums for a rectangular matrix of compatible blocks. The container of rows is the codomain tree and each row’s container the domain tree; a side written as a plain one-element list or tuple is a single space. SoBlockMatrixLinOp.stack((A1, A2))maps one domain into a tree,x -> (A1 x, A2 x), andBlockMatrixLinOp.from_operators((A1, A2))sums a tree into one codomain,(x1, x2) -> A1 x1 + A2 x2; each is the adjoint of the other built from the blocks’ adjoints.
Both block classes represent operators over finite direct products. They are
not tensor-product or Kronecker-product operators. Their rapply methods use
the metric adjoint of every block, which differs from a coordinate conjugate
transpose when a leaf space has a non-Euclidean inner product.
Autodoc#
- class spacecore.linop.LinOp(*args, **kwargs)[source]#
Bases:
Mapping,Generic[Domain,Codomain]A linear map from
domtocod; subclasses supplyapplyandrapply.rapplyis the Hilbert-space adjoint in each space’s own inner product, \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\), not the coordinate transpose. In orthonormal bases it is the conjugate transpose (Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12); in general coordinates it is \(R_X^{-1} A^\dagger R_Y\), derived inmetric_rapply(). Adjoint products reverse order (Trefethen & Bau, Numerical Linear Algebra, Eq. (2.4)); inserting Gram factors cancels the intermediate metric.- Parameters:
Examples
Use a concrete dense operator as a
LinOp.>>> import numpy as np >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((2,), ctx) >>> A = sc.DenseLinOp(ctx.asarray([[1.0, 0.0], [0.0, 2.0]]), X, X, ctx) >>> A.apply(ctx.asarray([3.0, 4.0])) array([3., 8.])
- property domain: Domain#
Domain space of this linear operator.
- property codomain: Codomain#
Codomain space of this linear operator.
- apply(x)[source]#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- rapply(y)[source]#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- adjoint_apply(y)[source]#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- is_hermitian()[source]#
Return whether this operator is structurally Hermitian when known.
- Returns:
TrueorFalsewhen the subclass can verify the structure cheaply, otherwiseNonefor unknown or matrix-free operators.- Return type:
bool | None
- vapply(xs)[source]#
Apply over a leading batch axis. Input must have shape
(N,) + domain.shape; usemoveaxisfor other layouts.- Parameters:
xs (Any)
- Return type:
Any
- rvapply(ys)[source]#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
ys (Any)
- Return type:
Any
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- fuse(*, materialize=False)[source]#
Return an equivalent operator with dense sub-expressions multiplied out (ADR-021).
Each maximal run of dense operators becomes one
DenseLinOp; other leaves stay lazy. The result equalsselfup to rounding. A leaf returns itself.- Parameters:
materialize (bool, optional) – Also densify matrix-free operands, which may be costly; by default they stay lazy.
- Returns:
The fused operator.
- Return type:
- to_dense()[source]#
Materialize this operator as a dense backend array.
The returned array has shape
self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.- Return type:
Any
- to_matrix()[source]#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- assert_codomain(y)[source]#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- class spacecore.linop.DenseLinOp(*args, **kwargs)[source]#
Bases:
LinOp[Domain,Codomain]Linear operator stored as a dense array of shape
cod.shape + dom.shape.The adjoint is the conjugate transpose on Euclidean spaces and
R_X^{-1} A^dagger R_Yotherwise. The array is used as given, not copied or cast.- Parameters:
A (DenseArray) – Operator tensor of shape
cod.shape + dom.shape.dom (Space) – Domain.
cod (Space or None, optional) – Codomain. When omitted it is a Euclidean space of
A’s leading shape, so for an endomorphism of a weightedXpasscod=Xexplicitly.ctx (Context, str, or None, optional) – Backend context; resolved from the spaces when omitted.
check_level ({"none", "cheap", "standard", "strict"}, optional) – Validation level; the ambient default when omitted.
Examples
>>> import numpy as np >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((2,), ctx) >>> A = sc.DenseLinOp(ctx.asarray([[2.0, 0.0], [0.0, 3.0]]), X, X, ctx) >>> A.apply(ctx.asarray([1.0, 2.0])) array([2., 6.])
- rvapply(ys)#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
self (LinOp)
ys (Any)
- Return type:
Any
- to_dense()[source]#
Return the stored dense tensor representation of this operator.
The returned array has shape
self.codomain.shape + self.domain.shape.- Return type:
DenseArray
- to_matrix()[source]#
Return the flattened dense matrix representation.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). It is a reshape/view of the stored dense tensor when the backend permits.- Return type:
DenseArray
- is_hermitian()[source]#
Return whether this dense operator is structurally self-adjoint.
- Returns:
TrueorFalsewhen the structure can be checked, otherwiseNone.- Return type:
bool or None
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- fuse(*, materialize=False)#
Return an equivalent operator with dense sub-expressions multiplied out (ADR-021).
Each maximal run of dense operators becomes one
DenseLinOp; other leaves stay lazy. The result equalsselfup to rounding. A leaf returns itself.- Parameters:
materialize (bool, optional) – Also densify matrix-free operands, which may be costly; by default they stay lazy.
- Returns:
The fused operator.
- Return type:
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_sparse()#
- vapply(xs)#
Apply over a leading batch axis. Input must have shape
(N,) + domain.shape; usemoveaxisfor other layouts.- Parameters:
xs (Any)
- Return type:
Any
- class spacecore.linop.SparseLinOp(*args, **kwargs)[source]#
Bases:
LinOp[CoordinateSpace,CoordinateSpace]Linear operator stored as a sparse matrix of shape
(cod.size, dom.size).Domain and codomain must be vector spaces. The adjoint is the conjugate transpose on Euclidean spaces and
R_X^{-1} A^dagger R_Yotherwise. The matrix is used as given.- Parameters:
A (SparseArray) – Sparse matrix of shape
(prod(cod.shape), prod(dom.shape)).dom (CoordinateSpace) – Domain.
cod (CoordinateSpace) – Codomain.
ctx (Context, str, or None, optional) – Backend context; resolved from the spaces when omitted.
check_level ({"none", "cheap", "standard", "strict"}, optional) – Validation level; the ambient default when omitted.
Examples
>>> import numpy as np >>> import scipy.sparse as sps >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((2,), ctx) >>> A = sc.SparseLinOp(ctx.assparse(sps.eye(2)), X, X, ctx) >>> A.apply(ctx.asarray([1.0, 2.0])) array([1., 2.])
- rvapply(ys)#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
self (LinOp)
ys (Any)
- Return type:
Any
- to_sparse()[source]#
Return the stored sparse matrix representation without copying.
The returned object is exactly the sparse array supplied at construction.
- Return type:
SparseArray
- to_matrix()[source]#
Materialize the stored sparse matrix as a dense 2D coordinate matrix.
Use
to_sparse()when sparse storage should be preserved.- Return type:
DenseArray
- to_dense()[source]#
Materialize the stored sparse matrix as a dense operator tensor.
The returned array has shape
self.codomain.shape + self.domain.shape.- Return type:
DenseArray
- is_hermitian()[source]#
Return whether this sparse operator is structurally self-adjoint.
- Returns:
TrueorFalsewhen the structure can be checked, otherwiseNone.- Return type:
bool or None
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- fuse(*, materialize=False)#
Return an equivalent operator with dense sub-expressions multiplied out (ADR-021).
Each maximal run of dense operators becomes one
DenseLinOp; other leaves stay lazy. The result equalsselfup to rounding. A leaf returns itself.- Parameters:
materialize (bool, optional) – Also densify matrix-free operands, which may be costly; by default they stay lazy.
- Returns:
The fused operator.
- Return type:
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- vapply(xs)#
Apply over a leading batch axis. Input must have shape
(N,) + domain.shape; usemoveaxisfor other layouts.- Parameters:
xs (Any)
- Return type:
Any
- class spacecore.linop.DiagonalLinOp(*args, **kwargs)[source]#
Bases:
LinOp[CoordinateSpace,CoordinateSpace]Coordinatewise map
x -> diagonal * xon one space.The adjoint conjugates the diagonal on Euclidean spaces and is
R_X^{-1} D^dagger R_Xotherwise.- Parameters:
diagonal (DenseArray) – Diagonal of shape
space.shape.space (Space or None, optional) – Domain and codomain; a vector space of
diagonal.shapewhen omitted.ctx (Context, str, or None, optional) – Backend context; resolved from
spacewhen omitted.check_level ({"none", "cheap", "standard", "strict"}, optional) – Validation level; the ambient default when omitted.
Examples
>>> import numpy as np >>> import spacecore as sc >>> ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) >>> X = sc.DenseCoordinateSpace((2,), ctx) >>> D = sc.DiagonalLinOp(ctx.asarray([2.0, 3.0]), X, ctx) >>> D.apply(ctx.asarray([4.0, 5.0])) array([ 8., 15.])
- rvapply(ys)#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
self (LinOp)
ys (Any)
- Return type:
Any
- to_matrix()[source]#
Return the flattened dense diagonal matrix representation.
- Return type:
DenseArray
- to_dense()[source]#
Return a dense tensor representation of this diagonal operator.
- Return type:
DenseArray
- is_hermitian()[source]#
Return whether this diagonal operator is structurally self-adjoint.
- Returns:
TrueorFalsewhen the structure can be checked, otherwiseNone.- Return type:
bool or None
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- fuse(*, materialize=False)#
Return an equivalent operator with dense sub-expressions multiplied out (ADR-021).
Each maximal run of dense operators becomes one
DenseLinOp; other leaves stay lazy. The result equalsselfup to rounding. A leaf returns itself.- Parameters:
materialize (bool, optional) – Also densify matrix-free operands, which may be costly; by default they stay lazy.
- Returns:
The fused operator.
- Return type:
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_sparse()#
- vapply(xs)#
Apply over a leading batch axis. Input must have shape
(N,) + domain.shape; usemoveaxisfor other layouts.- Parameters:
xs (Any)
- Return type:
Any
- class spacecore.linop.MatrixFreeLinOp(*args, **kwargs)[source]#
Bases:
LinOp[Domain,Codomain]Linear operator given by forward and adjoint callables; no matrix is stored.
rapplymust be the adjoint in the declared inner products,<apply(x), y>_Y = <x, rapply(y)>_X. It is used as given unlesseuclidean_adjoint=True, and no adjoint test runs at construction.- Parameters:
apply (callable) – Forward map
apply(x)fromdomtocod.rapply (callable) – Adjoint map
rapply(y)fromcodtodom.dom (Space) – Domain.
cod (Space) – Codomain.
ctx (Context, str, or None, optional) – Backend context; inferred from
domandcodwhen omitted.vapply (callable or None, optional) – Batched forward map;
vmapofapplyby default.rvapply (callable or None, optional) – Batched adjoint;
vmapofrapplyby default.check_level ({"none", "cheap", "standard", "strict"}, optional) – Validation level; the ambient default when omitted.
euclidean_adjoint (bool, optional) –
True:rapplyis the coordinate adjoint and is wrapped intoR_X^-1 A^T R_Y(seefrom_coordinate_adjoint()).False: it is the metric adjoint. Omitting it on a non-Euclidean space warns, because a coordinate adjoint there is silently wrong.hermitian (bool or None, optional) – Declared self-adjointness, returned by
is_hermitian()so solvers need no probe;None(the default) means unknown. Atstrictchecks a declaredTrueis probed once againstrapply.
- is_hermitian()[source]#
Return the declared self-adjointness (
Nonewhen not declared).- Return type:
bool | None
- classmethod from_coordinate_adjoint(apply, coordinate_rapply, dom, cod, ctx=None, vapply=None, coordinate_rvapply=None)[source]#
Build a matrix-free operator from a Euclidean coordinate adjoint.
The stored adjoint is the metric one,
R_X^-1 A^T R_Y, which is the given callable itself on Euclidean spaces. A non-Euclidean space without usable Riesz maps is rejected.- Parameters:
apply (callable) – Forward map from
domtocod.coordinate_rapply (callable) – Euclidean coordinate adjoint of
apply.dom (Space) – Domain and codomain.
cod (Space) – Domain and codomain.
ctx (Context, str, or None, optional) – Backend context.
vapply (callable or None, optional) – Batched forward map.
coordinate_rvapply (callable or None, optional) – Batched coordinate adjoint, wrapped the same way;
vmapof the wrapped adjoint by default.
- Return type:
- fuse(*, materialize=False)[source]#
Stay matrix-free unless
materialize=Trueis requested (ADR-021/ADR-008).By default a matrix-free operator is never densified, so it returns itself. With
materialize=Truethe caller explicitly opts into densification: the operator is probed into a dense tensor (viato_dense, the basis sweep) and returned as aDenseLinOp, letting an enclosing expression collapse to a single dense operator.- Parameters:
materialize (bool)
- Return type:
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- rvapply(ys)#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
ys (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_dense()#
Materialize this operator as a dense backend array.
The returned array has shape
self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.- Return type:
Any
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- vapply(xs)#
Apply over a leading batch axis. Input must have shape
(N,) + domain.shape; usemoveaxisfor other layouts.- Parameters:
xs (Any)
- Return type:
Any
- class spacecore.linop.IdentityLinOp(*args, **kwargs)[source]#
Bases:
LinOp[Domain,Domain]Lazy identity map on
space.- Parameters:
- vapply(xs)[source]#
Return
xsafter batched domain validation.- Parameters:
xs (Any)
- Return type:
Any
- rvapply(xs)[source]#
Return
xsafter batched codomain validation.- Parameters:
xs (Any)
- Return type:
Any
- to_dense()[source]#
Return the dense tensor representation of this identity map.
The returned array has shape
self.codomain.shape + self.domain.shape.- Return type:
Any
- is_hermitian()[source]#
Return whether this identity operator is Hermitian.
- Returns:
Always
True.- Return type:
bool
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- fuse(*, materialize=False)#
Return an equivalent operator with dense sub-expressions multiplied out (ADR-021).
Each maximal run of dense operators becomes one
DenseLinOp; other leaves stay lazy. The result equalsselfup to rounding. A leaf returns itself.- Parameters:
materialize (bool, optional) – Also densify matrix-free operands, which may be costly; by default they stay lazy.
- Returns:
The fused operator.
- Return type:
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- class spacecore.linop.ZeroLinOp(*args, **kwargs)[source]#
Bases:
ZeroNode,LinOp[Domain,Codomain]Lazy zero map from
domtocod.- Parameters:
- vapply(xs)[source]#
Return the batched zero element of the codomain.
- Parameters:
xs (Any)
- Return type:
Any
- rvapply(ys)[source]#
Return the batched zero element of the domain.
- Parameters:
ys (Any)
- Return type:
Any
- to_dense()[source]#
Return the dense tensor representation of the zero map.
The returned array has shape
self.codomain.shape + self.domain.shape.- Return type:
Any
- is_hermitian()[source]#
Return whether the zero map is Hermitian.
- Returns:
Trueexactly when domain and codomain are the same space.- Return type:
bool
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- fuse(*, materialize=False)#
Return an equivalent operator with dense sub-expressions multiplied out (ADR-021).
Each maximal run of dense operators becomes one
DenseLinOp; other leaves stay lazy. The result equalsselfup to rounding. A leaf returns itself.- Parameters:
materialize (bool, optional) – Also densify matrix-free operands, which may be costly; by default they stay lazy.
- Returns:
The fused operator.
- Return type:
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- class spacecore.linop.SumLinOp(*args, **kwargs)[source]#
Bases:
SumNode,LinOp[Domain,Codomain]Lazy sum of operators sharing context, domain and codomain.
- Parameters:
ops (sequence of LinOp) – The summands.
check_level ({"none", "cheap", "standard", "strict"}, optional) – Validation level; the ambient default when omitted.
- fuse(*, materialize=False)[source]#
Fuse each term and combine the dense terms into one
DenseLinOp(ADR-021).Fuse every term, sum the matrices of the densely-fusible ones into a single
DenseLinOp, and keep the remaining (matrix-free or structured) terms as lazy summands — so a matrix-free term is never densified. Adjoint-consistent and additive:(A + B)^* = A^* + B^*. Combining reassociates the term order, so equality holds up to rounding.- Parameters:
materialize (bool)
- Return type:
- is_hermitian()[source]#
Return whether this lazy sum is structurally Hermitian.
The adjoint is additive,
(A1 + ... + Ak)* = A1* + ... + Ak*, so a sum is self-adjoint when every term is. If all operands are provably Hermitian this returnsTrue; otherwise the verdict is not cheaply decidable (a sum of non-Hermitian terms may still be Hermitian) andNoneis returned.Falseis never returned.- Returns:
Truewhen every part is provably Hermitian, otherwiseNone.- Return type:
bool | None
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_dense()#
Materialize this operator as a dense backend array.
The returned array has shape
self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.- Return type:
Any
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- class spacecore.linop.ComposedLinOp(*args, **kwargs)[source]#
Bases:
LinOp[Domain,Codomain]Lazy composition
left @ right; the adjoint isright.H @ left.H.- Parameters:
- rvapply(ys)#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
ys (Any)
- Return type:
Any
- fuse(*, materialize=False)[source]#
Fuse a composition of dense operators into one
DenseLinOp(ADR-021).Fuse each operand first, then — when both fused operands are dense — replace
A @ Bwith a singleDenseLinOpholding the matrix product \(M_A M_B\). Any operand that does not fuse to a dense operator (matrix-free leaves, sparse, structured) keeps the composition lazy, so a matrix-free operand is never densified. The fused matrix is adjoint-consistent on any geometry: the shared middle-space Riesz maps cancel, so the fused operator’s metric adjoint equalsB* @ A*up to floating-point rounding.- Parameters:
materialize (bool)
- Return type:
- is_hermitian()[source]#
Return
Truefor an adjoint-palindromic chain (R.H @ R,L @ V @ L.HwithVself-adjoint), elseNone.- Returns:
Truewhen structurally Hermitian,Nonewhen unknown.- Return type:
bool | None
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_dense()#
Materialize this operator as a dense backend array.
The returned array has shape
self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.- Return type:
Any
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- class spacecore.linop.ScaledLinOp(*args, **kwargs)[source]#
Bases:
ScaledNode,LinOp[Domain,Codomain]Lazy
scalar * op; its adjoint isconj(scalar) * op.H.- Parameters:
scalar (scalar-like) – Coefficient.
op (LinOp) – Operator to scale.
check_level ({"none", "cheap", "standard", "strict"}, optional) – Validation level; the ambient default when omitted.
- rvapply(ys)#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
ys (Any)
- Return type:
Any
- fuse(*, materialize=False)[source]#
Fuse the operand and fold the scalar into a dense matrix (ADR-021).
When the fused operand is dense, replace
c · Awith oneDenseLinOpholdingc · M_A; otherwise keep the scaling lazy, so a matrix-free operand is never densified. Adjoint-consistent: the fused operator’s adjoint isconj(c) · M_A^*(with the metric), exactly the lazyScaledLinOpadjoint.- Parameters:
materialize (bool)
- Return type:
- is_hermitian()[source]#
Return
op.is_hermitian()for a real scalar, elseNone(undecided).- Returns:
Structural Hermiticity, or
Nonewhen unknown.- Return type:
bool | None
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_dense()#
Materialize this operator as a dense backend array.
The returned array has shape
self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.- Return type:
Any
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- spacecore.linop.make_sum(ops)[source]#
Return the lazy sum of operators, with nested sums flattened and zero terms dropped.
- spacecore.linop.make_composed(left, right)[source]#
Return
left @ right, dropping identity factors and collapsing zero factors.
- spacecore.linop.make_scaled(scalar, op)[source]#
Return
scalar * op:0gives zero,1givesop, nested scalars fold.
- class spacecore.linop.TreeLinOp(*args, **kwargs)[source]#
Bases:
LinOp[Domain,Codomain]Define a base class for operators assembled from component operators.
- Parameters:
dom (Space) – Domain space of the assembled operator.
cod (Space) – Codomain space of the assembled operator.
parts (sequence of LinOp) – Nonempty sequence of component operators.
ctx (Context, str, or None, optional) – Backend context specification. Default is resolved from
domandcod.check_level ({"none", "cheap", "standard", "strict"}, optional) – Validation level; the ambient default when omitted.
- property H: LinOp#
Hermitian-adjoint view of this linear operator.
- Returns:
Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- abstractmethod classmethod from_operators(parts)[source]#
Build a tree-structured operator from component operators.
- fuse(*, materialize=False)#
Return an equivalent operator with dense sub-expressions multiplied out (ADR-021).
Each maximal run of dense operators becomes one
DenseLinOp; other leaves stay lazy. The result equalsselfup to rounding. A leaf returns itself.- Parameters:
materialize (bool, optional) – Also densify matrix-free operands, which may be costly; by default they stay lazy.
- Returns:
The fused operator.
- Return type:
- is_hermitian()#
Return whether this operator is structurally Hermitian when known.
- Returns:
TrueorFalsewhen the subclass can verify the structure cheaply, otherwiseNonefor unknown or matrix-free operators.- Return type:
bool | None
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- rvapply(ys)#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
ys (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_dense()#
Materialize this operator as a dense backend array.
The returned array has shape
self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.- Return type:
Any
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- vapply(xs)#
Apply over a leading batch axis. Input must have shape
(N,) + domain.shape; usemoveaxisfor other layouts.- Parameters:
xs (Any)
- Return type:
Any
- class spacecore.linop.BlockDiagonalLinOp(*args, **kwargs)[source]#
Bases:
TreeLinOp[TreeSpace,TreeSpace]Independent blocks acting on the leaves of a direct-product tree.
BlockDiagonalLinOp(blocks)infers both tree spaces from the blocks; the tree ofblocksis the element structure. This is a direct product, not a Kronecker product.- Parameters:
- vapply(x)[source]#
Apply each block over a tree of leading-axis batches.
- Parameters:
x (Any)
- Return type:
Any
- rvapply(y)[source]#
Apply each metric adjoint over a tree of leading-axis batches.
- Parameters:
y (Any)
- Return type:
Any
- property H: BlockDiagonalLinOp#
Return a block-diagonal adjoint with every block replaced by
A_i.H.
- fuse(*, materialize=False)[source]#
Fuse each block (ADR-021), preserving the tree layout and context.
- Parameters:
materialize (bool)
- Return type:
- classmethod from_operators(parts)[source]#
Build a tuple-structured block-diagonal operator.
- Parameters:
parts (Sequence[LinOp])
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- is_hermitian()#
Return whether this operator is structurally Hermitian when known.
- Returns:
TrueorFalsewhen the subclass can verify the structure cheaply, otherwiseNonefor unknown or matrix-free operators.- Return type:
bool | None
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_dense()#
Materialize this operator as a dense backend array.
The returned array has shape
self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.- Return type:
Any
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- class spacecore.linop.BlockMatrixLinOp(*args, **kwargs)[source]#
Bases:
TreeLinOp[Any,Any]Block matrix over direct products:
y_i = sum_j A_ij x_jfor blocksA_ij : X_j -> Y_i.The container of rows is the codomain’s tree and the container of each row is the domain’s tree, so a NamedTuple of rows gives NamedTuple codomain elements. A side given as a plain one-element list or tuple is a single space, not a one-leaf tree:
BlockMatrixLinOp([[A1], [A2]])stacksx -> (A1 x, A2 x)andBlockMatrixLinOp([[A1, A2]])sums(x1, x2) -> A1 x1 + A2 x2; each is the other’s adjoint when the blocks are replaced by theirs.- Parameters:
block_rows (sequence of sequences of LinOp) – Rectangular block matrix; rows share codomains and columns share domains.
check_level ({"none", "cheap", "standard", "strict"}, optional) – Validation level; the ambient default when omitted.
- is_hermitian()[source]#
Return
Truewhen the block layout is square,A_ij# == A_jiand every diagonal block is self-adjoint.- Return type:
bool | None
- property H: BlockMatrixLinOp#
Transpose the block layout and replace every block by its adjoint.
- fuse(*, materialize=False)[source]#
Fuse each block (ADR-021), preserving the block layout.
- Parameters:
materialize (bool)
- Return type:
- classmethod from_operators(parts)[source]#
Build a one-row block matrix,
(x_1, ..., x_k) -> sum_j A_j x_j.- Parameters:
parts (Sequence[LinOp])
- Return type:
- classmethod stack(parts)[source]#
Build a one-column block matrix,
x -> (A_1 x, ..., A_k x).- Parameters:
parts (Sequence[LinOp])
- Return type:
- adjoint_apply(y)#
Apply the adjoint of this linear operator to
y.Spelled-out alias for the inner-product adjoint
rapply().- Parameters:
y (Any)
- Return type:
Any
- apply(x)#
Apply the forward map to an element of
self.domain.- Parameters:
x (Any)
- Return type:
Any
- assert_codomain(y)#
Raise if
yis not in the codomain.- Parameters:
y (Any)
- Return type:
None
- assert_domain(x)#
Raise if
xis not in the domain.- Parameters:
x (Any)
- Return type:
None
- property check_level: Literal['none', 'cheap', 'standard', 'strict']#
Return this object’s runtime validation level.
The level is stored on the bound object (
_check_level), seeded at construction. The fallback covers any construction path that assigns_ctxwithout going through__init__/_bind_context.
- property codomain: Codomain#
Codomain space of this linear operator.
- convert(new_ctx=None)#
Return this object represented in
new_ctx.The object’s
check_levelis a property of the object, not of the backend/dtype context, so it is preserved across conversion rather than re-seeded from the ambient default.- Parameters:
new_ctx (Context | BackendFamily | str | None)
- Return type:
Self
- property domain: Domain#
Domain space of this linear operator.
- rapply(y)#
Apply the adjoint map to an element of
self.codomain.This is the Hilbert-space adjoint
A^#, defined by<Ax, y>_Y = <x, A^# y>_X. Trefethen & Bau, Numerical Linear Algebra, Lecture 2, pp. 11-12, gives the conjugate transpose in orthonormal coordinates;spacecore.linop._metric.metric_rapply()derives the Gram-matrix form. The dual map sends a linear functionalphitophi o A; writingphi(y) = b^T ygives dual coordinatesA^T b, even over the complex field.- Parameters:
y (Any)
- Return type:
Any
- rvapply(ys)#
Apply the adjoint over a leading batch axis. Input must have shape
(N,) + codomain.shape; usemoveaxisfor other layouts.- Parameters:
ys (Any)
- Return type:
Any
- same_math(other)#
Tier-1 equality gate: same concrete type and same math context.
Combines a runtime-type identity check with
Context.same_math()(equal backend ops and dtype, ignoringcheck_level— a validation policy, not a mathematical or backend property). This is the mandatory first check in every__eq__: the type gate guaranteesotherexposes the same attributes this object’s__eq__will read, andsame_mathguarantees any laterops.allcloseruns only on same-backend, same-dtype arrays, making cross-backend objects compare unequal.Callers that fail this gate should return
NotImplementedso Python can try the reflected comparison and fall back to identity symmetrically.- Parameters:
other (Any)
- Return type:
bool
- to_dense()#
Materialize this operator as a dense backend array.
The returned array has shape
self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.- Return type:
Any
- to_matrix()#
Materialize this operator as a 2D dense coordinate matrix.
The returned array has shape
(prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and callsvapply()once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.- Return type:
Any
- to_sparse()#
- vapply(xs)#
Apply over a leading batch axis. Input must have shape
(N,) + domain.shape; usemoveaxisfor other layouts.- Parameters:
xs (Any)
- Return type:
Any