Skip to main content

SpaceCore

CI PyPI Python License

In most numerical code a vector is just an array. The mathematics that gives it meaning — which space it belongs to, what inner product measures its length, how an operator's adjoint is actually defined — lives in the author's head and in comments rather than in the code. When that structure stays implicit, it is easy to combine elements from incompatible spaces, assume a Euclidean inner product where the geometry is weighted, or take a matrix transpose where the true adjoint is something else.

SpaceCore makes that structure explicit. A space is a typed object that knows how to validate its elements, compute inner products, flatten structured values, and represent adjoints. An operator is a typed map A : X -> Y between spaces, not merely an array, so the spaces it connects — and the rules they carry — are part of its definition.

The execution backend is explicit too. A Context owns the backend operations, the default dtype, and the validation policy used by spaces and operators. NumPy is the baseline backend; JAX, Torch, and CuPy are optional backends when their extras are installed.

Install

pip install spacecore
pip install "spacecore[jax]"
pip install "spacecore[torch]"
pip install "spacecore[cupy]"

Python 3.11+ is required.

Quick Start

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)
b = ctx.asarray([4.0, 9.0])

result = sc.cg(A, b, tol=1e-12, maxiter=10)
print(result.x)
print(result.converged)

Expected output:

[2. 3.]
True

Core Ideas

Spaces. DenseCoordinateSpace, DenseVectorSpace, ElementwiseJordanSpace, EuclideanElementwiseJordanSpace, HermitianSpace, TreeSpace, and StackedSpace describe element structure and geometry. Dense coordinate spaces can use Euclidean or weighted inner products. Each space also exposes a field ("real" or "complex") — the mathematical scalar-field contract derived from the context dtype — which capability guards consult instead of inspecting precision-bearing dtypes.

import numpy as np
import spacecore as sc

ctx = sc.Context(sc.NumpyOps(), dtype=np.float64)
weights = ctx.asarray([2.0, 5.0])
X = sc.DenseCoordinateSpace((2,), ctx, geometry=sc.WeightedInnerProduct(weights))
x = ctx.asarray([1.0, 2.0])
y = ctx.asarray([3.0, 4.0])

print(X.inner(x, y))
print(X.riesz(x))
print(X.field)

Expected output:

46.0
[ 2. 10.]
real

Linear operators. DenseLinOp, SparseLinOp, DiagonalLinOp, MatrixFreeLinOp, IdentityLinOp, ZeroLinOp, and the lazy algebraic operators (ComposedLinOp, ScaledLinOp, SumLinOp) represent maps A : X -> Y. apply computes the forward map. rapply computes the metric adjoint: the coordinate conjugate transpose only agrees with it when both spaces use Euclidean geometry. The algebraic operators infer Hermiticity structurally, so a normal operator A.H @ A + lam * Identity is recognized as self-adjoint.

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([2.0, 3.0]), X, ctx)

print(A.apply(ctx.asarray([1.0, 2.0])))
print(A.rapply(ctx.asarray([1.0, 1.0])))

M = sc.DenseLinOp(ctx.asarray([[1.0, 2.0], [0.0, 1.0]]), X, X, ctx)
print((M.H @ M).is_hermitian())

Expected output:

[2. 6.]
[2. 3.]
True

Structured (tree) spaces. TreeSpace is the finite direct-product space: it organizes leaf spaces by an optree definition, so an element can be a named, nested Python structure (tuple, dict, namedtuple, or any nesting of them) rather than a flat vector. Operations like inner act across the whole tree, and BlockDiagonalLinOp, BlockMatrixLinOp, and the TreeLinOp base operate over tree domains with correct metric-adjoint behavior.

A block-diagonal operator can be built directly from a matching tree of blocks; its domain and codomain are inferred from the blocks, so the names and nesting are carried through to the result.

import numpy as np
import spacecore as sc

ctx = sc.Context(sc.NumpyOps(), dtype=np.float64)
position = sc.DenseCoordinateSpace((3,), ctx)
linear = sc.DenseCoordinateSpace((3,), ctx)
angular = sc.DenseCoordinateSpace((1,), ctx)

# A named, nested tree of blocks: a top-level "position" block and a grouped
# "twist" block with its own "linear" and "angular" sub-blocks.
A = sc.BlockDiagonalLinOp({
    "position": sc.IdentityLinOp(position, ctx),
    "twist": {
        "linear": sc.DiagonalLinOp(ctx.asarray([2.0, 2.0, 2.0]), linear, ctx),
        "angular": sc.DiagonalLinOp(ctx.asarray([0.5]), angular, ctx),
    },
})

x = A.dom.element({
    "position": ctx.asarray([1.0, 0.0, 0.0]),
    "twist": {
        "linear": ctx.asarray([1.0, 1.0, 1.0]),
        "angular": ctx.asarray([4.0]),
    },
})

print(A.apply(x))
print(A.dom.inner(x, x))

Expected output:

{'position': array([1., 0., 0.]), 'twist': {'linear': array([2., 2., 2.]), 'angular': array([2.])}}
20.0

Functionals. LinearFunctional, InnerProductFunctional, MatrixFreeLinearFunctional, QuadraticForm, and LinOpQuadraticForm model scalar-valued maps on spaces. Gradients are represented in the domain geometry.

Everyday toolbox. spacecore.functional.tools (re-exported at the top level) adds named constructors over that machinery, with no new core types: least_squares for ½‖Ax−b‖², coordinate norms (SquaredL2NormFunctional, LpNormFunctional, L1NormFunctional), NegativeEntropyFunctional, KLDivergenceFunctional, HuberFunctional, the spectral NuclearNormFunctional (and spectralize / SpectralFunctional to lift any coordinate functional to a spectral one), and the metric-aware proximal primitive generalized_shrinkage with the wrappers prox_l1, prox_l2sq, and project_nonneg. Each objective's gradient is the metric (Riesz) gradient under the domain geometry, and the proximal step is taken in the space metric.

Linear algebra. cg, lsqr, lanczos_smallest, power_iteration, and expm_multiply operate on SpaceCore operators and spaces. They document their mathematical preconditions and reject provably invalid inputs at entry; for example cg expects a square Hermitian positive definite map A : X -> X with respect to X.inner, and rejects an operator that is provably non-self-adjoint in its geometry. These native solvers are intentionally small — a correctness baseline and a substrate for space-aware algorithms rather than a full solver suite; backend-specific fast paths and external adapters can be layered on top.

External optimizer adapters. minimize_scipy, line_search_scipy, and minimize_optax (in spacecore.optimize) drive mature external optimizers from a Functional. Because SpaceCore gradients are metric (Riesz) gradients while NumPy/JAX optimizers expect coordinate gradients, each adapter applies the X.riesz(F.grad(x)) handoff for you — the identity on a Euclidean space and mandatory on a weighted one. The external optimizer owns the loop; the SciPy adapters require a real domain and minimize_optax needs a JAX backend (pip install spacecore[optax]).

Backends. NumpyOps is always available. JaxOps, TorchOps, and CuPyOps are exported only when their optional dependencies are installed. Backend portability means SpaceCore uses the same abstract operations and data model; it does not erase backend-specific dtype, device, sparse, tracing, or autograd behavior. Cross-backend agreement is documented in the backend conformance matrix and deviation catalog.

Validation Policy

Every context-bound object — space, operator, functional — carries a check_level that determines how aggressively it validates its inputs, together with solver preconditions. The ordered levels are CHECK_LEVELS = ("none", "cheap", "standard", "strict"): cheap covers shape/dtype/backend/tree-structure, standard adds membership and Hermitian checks, and strict adds bounded expensive probes. Checks are opt-in per object, so hot paths can run unvalidated while development and tests run strict.

The level is a property of the bound object rather than of the Context: a context fixes backend ops and dtype, while two objects sharing that backend and dtype may legitimately validate at different strictness. Pass check_level= to a constructor, or move the ambient default with sc.set_check_level(...) / sc.use_check_level(...).

import numpy as np
import spacecore as sc

ctx = sc.Context(sc.NumpyOps(), dtype=np.float64)
X = sc.DenseCoordinateSpace((2,), ctx, check_level="standard")
A = sc.DenseLinOp(
    ctx.asarray([[2.0, 0.0], [0.0, 3.0]]), X, X, ctx, check_level="standard"
)

try:
    A.apply(ctx.asarray([1.0, 2.0, 3.0]))  # wrong shape
except sc.SpaceValidationError as exc:
    print(exc)

Expected output:

Expected shape (2,), got (3,)

Batching

A space describes one element type. Batched computation is handled by vectorized application methods such as vapply, rvapply, vvalue, and backend vectorization. Batching does not change the mathematical domain or codomain of an operator unless the operator itself is explicitly built over a stacked or tree space. Iterative solvers are unbatched in this release; batched-input invocations raise a clear shape error.

Performance

The typed, validated API is designed to add little run-time overhead over working with raw arrays: operator and functional hot paths use check-free fast paths, and iterative solvers validate their inputs once at entry rather than on every iteration. A unified benchmark framework is available via python -m bench.

Documentation

Project Status

SpaceCore is experimental 0.4.x software. The 0.4.0 release stabilizes the typed linear-algebra core as a validated algebra of structured mathematical objects — a public check-policy, the dtype/scalar-field contract, the TreeSpace direct-product abstraction with block-structured operators, an everyday functional and proximal toolbox, external optimizer adapters, reusable test generators, and a backend conformance matrix. Core abstractions are usable for research and prototyping, but API details may still change before a stable release.

Contributing

Bug reports, feature requests, and PRs are welcome. See CONTRIBUTING.md.

License

Apache 2.0. See LICENSE.

Citation

@software{spacecore,
  author = {Pavlo Pelikh},
  title = {SpaceCore: Backend-aware vector spaces and linear operators},
  url = {https://github.com/Pavlo3P/SpaceCore},
  year = {2026},
}

Metadata

Release files for spacecore 0.4.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spacecore 0.4.3
File Size Uploaded Provenance
spacecore-0.4.3.tar.gz 1.9 MB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for spacecore 0.4.3
File Interpreter ABI Platform
spacecore-0.4.3-py3-none-any.whl Python 3 none any Details

Total release size: 2.2 MB

Release files / spacecore-0.4.3.tar.gz

Download URL spacecore-0.4.3.tar.gz
Size 1.9 MB
Tags Source
SHA-256 checksum
How to use checksums
b38b8af974a06eabdf657849ecaee35707f3405364baab2852fcde2561f43ee7
BLAKE2b-256 checksum
How to use checksums
c50317e4554ec2c210312482ea69cb52fdf7e4ef8444c27241e250f2cce6c3f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release files / spacecore-0.4.3-py3-none-any.whl

Download URL spacecore-0.4.3-py3-none-any.whl
Size 276.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6b5c1493f8eacf30ec179843810d7846c3848ca83d82d002739c8598cb500c59
BLAKE2b-256 checksum
How to use checksums
e71e761d6fd63a43518e91a051888f23d073bb352deb2585b331f3a3568d6929
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Security for spacecore 0.4.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for spacecore-0.4.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page