diff --git a/README.md b/README.md index 557cf90..aa02f03 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,96 @@ # SpaceCore -Framework for working with vector spaces and linear operators. + +SpaceCore is a lightweight backend-agnostic library for working with vector spaces and linear operators. + +It provides a small set of abstractions for: + +- backend-aware numerical operations +- contexts carrying backend and dtype information +- structured vector spaces +- structured linear operators +- conversion between compatible contexts + +## Installation + +Base install: + +```bash +pip install spacecore +```` + +With JAX support: + +```bash +pip install "spacecore[jax]" +``` + +* `spacecore[jax]`: installs optional JAX support. +* GPU users should install the appropriate CUDA-enabled JAX build first, following the official JAX installation guide. + +## Main concepts + +### `Context` + +A `Context` specifies how objects are represented, in particular: + +* backend (`NumPy`, `JAX`, etc.) +* dtype +* validation/conversion behavior + +### `Space` + +A `Space` describes the structure of objects space, for example: + +* `VectorSpace` - Euclidean space +* `HermitianSpace` - space of Hermitian (symmetric) matrices +* `ProductSpace` - Cartesian product of spaces + +### `LinOp` + +A `LinOp` represents a linear operator between spaces, for example: + +* `DenseLinOp` - linear operator represented by dense matrix +* `SparseLinOp` - linear operator represented by sparse matrix +* `BlockDiagonalLinOp` - linear operator from $X_1 \times \dots \times X_k$ to $Y_1 \times \dots \times Y_k$ +* `StackedLinOp` - linear operator from $X$ to $Y_1 \times \dots \times Y_k$ +* `SumToSingleLinOp` - linear operator from $X_1 \times \dots \times X_k$ to $Y$ + +## Minimal example + +```python +import numpy as np +import spacecore as sc + +sc.set_context('numpy', dtype='float64') + +X = sc.VectorSpace((3,)) +Y = sc.VectorSpace((2,)) + +A = np.array( + [[1.0, 2.0, 3.0], + [0.0, 1.0, 0.0]] +) +linop = sc.DenseLinOp( + A, + dom=X, + cod=Y, +) + +x = X.ctx.asarray([1.0, 0.0, -1.0]) +y = linop.apply(x) + +print(y) +``` + +## Status + +SpaceCore is currently experimental and under active development. +The public API may still evolve. + +## Tutorials + +See the `tutorials/` directory for usage examples and design guidance. + +## License + +Apache License 2.0 diff --git a/spacecore/_contextual/contextual.py b/spacecore/_contextual/contextual.py index 94c4992..eac23d7 100644 --- a/spacecore/_contextual/contextual.py +++ b/spacecore/_contextual/contextual.py @@ -69,10 +69,24 @@ def __init__(self, self.resolution_policy = resolution_policy self.dtype_resolution_policy = dtype_resolution_policy - def normalize_context(self, ctx: Context | BackendFamily | str | None = None) -> Context: + def normalize_context(self, + ctx: Context | BackendFamily | str | None = None, + dtype: Any = None, + enable_checks: bool | None = None + ) -> Context: if ctx is None: + if dtype is not None or enable_checks is not None: + warn( + 'Provided context is None, dtype and enable_checks parameters are ignored.', + UserWarning, + ) return self.default_ctx if isinstance(ctx, Context): + if dtype is not None or enable_checks is not None: + warn( + 'Provided concrete context, dtype and enable_checks parameters are ignored.', + UserWarning, + ) return Context( ops=ctx.ops, dtype=ctx.ops.sanitize_dtype(ctx.dtype), @@ -81,7 +95,7 @@ def normalize_context(self, ctx: Context | BackendFamily | str | None = None) -> if isinstance(ctx, (str, BackendFamily)): ctx = self._backend_key(ctx) ops = self.get_ops(ctx) - return self.ctx_from_ops(ops) + return self.ctx_from_ops(ops, dtype=dtype, enable_checks=enable_checks) else: raise TypeError(f'Expected Context, BackendFamily, str, or None, got {type(ctx)}.') diff --git a/spacecore/_contextual/manager.py b/spacecore/_contextual/manager.py index d2ba739..e463040 100644 --- a/spacecore/_contextual/manager.py +++ b/spacecore/_contextual/manager.py @@ -1,3 +1,5 @@ +from typing import Any + from ..backend import Context, BackendOps from .contextual import Contextual, ContextPolicy, DtypePreservePolicy from ..backend import BackendFamily @@ -6,7 +8,12 @@ ctx_manager = Contextual() -def set_context(ctx: Context | BackendFamily | str | None = None) -> None: +def set_context( + ctx: Context | BackendFamily | str | None = None, + dtype: Any = None, + enable_checks: bool | None = None +) -> None: + ctx = ctx_manager.normalize_context(ctx, dtype=dtype, enable_checks=enable_checks) ctx_manager.default_ctx = ctx @@ -22,8 +29,8 @@ def set_resolution_policy(policy: ContextPolicy | str | None = None) -> None: ctx_manager.resolution_policy = policy -def get_resolution_policy() -> ContextPolicy: - return ctx_manager.resolution_policy +def get_resolution_policy() -> str: + return ctx_manager.resolution_policy.value def set_dtype_resolution_policy( @@ -32,5 +39,5 @@ def set_dtype_resolution_policy( ctx_manager.dtype_resolution_policy = policy -def get_dtype_resolution_policy() -> DtypePreservePolicy: - return ctx_manager.dtype_resolution_policy +def get_dtype_resolution_policy() -> str: + return ctx_manager.dtype_resolution_policy.value