Source code for spacecore.space._base
from __future__ import annotations
from abc import abstractmethod
from typing import Any, Callable, ClassVar, Tuple
from ..backend import Context
from .._contextual import ContextBound
from ..types import DenseArray
from ._checks import SpaceCheck
[docs]
class Space(ContextBound):
"""
Abstract Space.
A Space owns the *geometry* (inner product, norm) and the basic linear
structure (add/scale/axpy) for its elements.
Solvers should use only this API.
"""
checks: ClassVar[tuple[SpaceCheck, ...]] = ()
def __init__(self, shape: Tuple[int, ...], ctx: Context | str | None = None) -> None:
super().__init__(ctx)
self.shape = shape
self._enable_checks = self.ctx.enable_checks
def __eq__(self, other: Any) -> bool:
if isinstance(other, Space):
return self.ctx == other.ctx and self.shape == other.shape
return False
[docs]
def member_checks(self) -> tuple[SpaceCheck, ...]:
checks: list[SpaceCheck] = []
for klass in reversed(type(self).__mro__):
checks.extend(klass.__dict__.get("checks", ()))
local_checks = klass.__dict__.get("_local_checks")
if local_checks is not None:
checks.extend(local_checks(self))
return tuple(checks)
def _check_member(self, x: Any) -> None:
"""
Raise if `x` is not a valid element of this space.
Typical checks:
- x.space is self (if your elements carry a .space)
- backend family consistency (via ctx)
- representation is supported
- shape/structure constraints (Hermitian, block sizes, etc.)
"""
for check in self.member_checks():
check(self, x)
[docs]
def check_member(self, x: Any) -> None:
if self._enable_checks:
self._check_member(x)
[docs]
@abstractmethod
def zeros(self) -> Any:
"""Return the additive identity in the requested representation."""
[docs]
@abstractmethod
def add(self, x: Any, y: Any) -> Any:
"""Return x + y."""
[docs]
@abstractmethod
def scale(self, a: Any, x: Any) -> Any:
"""Return a * x."""
[docs]
def axpy(self, a: Any, x: Any, y: Any) -> Any:
"""Return a*x + y."""
return self.add(self.scale(a, x), y)
[docs]
@abstractmethod
def inner(self, x: Any, y: Any) -> Any:
"""
Inner product ⟨x, y⟩ for elements of this space.
"""
[docs]
def norm(self, x: Any) -> Any:
"""Induced norm ||x|| = sqrt(real(⟨x,x⟩)). Override if you can do better."""
v = self.ctx.ops.real(self.inner(x, x))
return self.ctx.ops.sqrt(v)
[docs]
@abstractmethod
def eigh(self, x: Any, k: int = None) -> Any:
"""Eigendecomposition of x (if applicable).)"""
[docs]
@abstractmethod
def flatten(self, x: Any) -> DenseArray:
"""
Return a dense 1D coordinate vector (backend-native dense array).
If a representation forbids materialization, raise a policy/capability error.
"""
[docs]
@abstractmethod
def unflatten(self, v: DenseArray) -> Any:
"""Inverse of flatten; returns an element in the requested representation."""
raise NotImplementedError
def _convert(self, new_ctx: Context) -> Space:
raise NotImplementedError()
[docs]
def apply(self, x: Any, f: Callable) -> Any:
raise NotImplementedError(
f"{type(self).__name__} does not define functional application."
)