Source code for spacecore.backend._context

from dataclasses import dataclass
from typing import Any

from ._ops import BackendOps
from ..types import DenseArray, SparseArray, DType, ArrayLike


[docs] @dataclass(frozen=True, slots=True) class Context: """ Backend execution context for SpaceCore objects. A context collects the backend operations object, default dtype, and runtime validation policy used by spaces, linear operators, and context-bound values. It is intentionally small: it does not own arrays, but it defines how new arrays are created and how existing arrays are checked or converted for a backend family. Parameters ---------- ops: Backend operations implementation. This must be an instance of :class:`spacecore.backend.BackendOps`, such as :class:`spacecore.backend.NumpyOps` or :class:`spacecore.backend.JaxOps`. dtype: Default dtype used by :meth:`asarray` and :meth:`assparse`. The value is normalized through ``ops.sanitize_dtype`` during initialization. enable_checks: Whether spaces and linear operators using this context should perform membership and compatibility checks before operations. Notes ----- ``Context`` is frozen and slot-based. Methods that convert values return new backend arrays or sparse objects; they do not mutate the context itself. Equality compares backend family and ``enable_checks``. It currently does not compare ``dtype``. """ ops: BackendOps dtype: DType | None = None enable_checks: bool = True def __post_init__(self): """ Validate and normalize the context after dataclass initialization. Raises ------ TypeError If ``ops`` is not a :class:`BackendOps` instance. """ if not isinstance(self.ops, BackendOps): raise TypeError("ops must be a BackendOps") sanitized = self.ops.sanitize_dtype(self.dtype) object.__setattr__(self, "dtype", sanitized)
[docs] def assert_dense(self, x: Any) -> DenseArray: """ Return ``x`` after verifying that it is a dense array for this backend. Parameters ---------- x: Object to validate. Returns ------- DenseArray The original object, typed as a dense array. Raises ------ TypeError If ``x`` is not recognized as a dense array by ``self.ops``. """ if not self.ops.is_dense(x): raise TypeError(f"Expected dense array for {self.ops.family}, got {type(x).__name__}") return x
[docs] def assert_sparse(self, x: Any) -> SparseArray: """ Return ``x`` after verifying that it is a sparse array for this backend. Parameters ---------- x: Object to validate. Returns ------- SparseArray The original object, typed as a sparse array. Raises ------ TypeError If the backend does not allow sparse arrays or if ``x`` is not recognized as a sparse array by ``self.ops``. """ if not self.ops.allow_sparse: raise TypeError("Sparse objects are disallowed by this backend.") if not self.ops.is_sparse(x): raise TypeError(f"Expected sparse array for {self.ops.family}, got {type(x).__name__}") return x
[docs] def asarray(self, x: Any) -> DenseArray: """ Convert ``x`` to a dense backend array using this context's dtype. Parameters ---------- x: Array-like object accepted by the backend implementation. Returns ------- DenseArray Backend-native dense array with dtype ``self.dtype``. """ return self.ops.asarray(x, dtype=self.dtype)
[docs] def assparse(self, x: Any) -> SparseArray: """ Convert ``x`` to a sparse backend object using this context's dtype. Parameters ---------- x: Array-like or sparse object accepted by the backend implementation. Returns ------- SparseArray Backend-native sparse object with dtype ``self.dtype``. Raises ------ TypeError If the backend implementation does not support sparse conversion. """ return self.ops.assparse(x, dtype=self.dtype)
[docs] def convert(self, x: Any) -> ArrayLike: """ Convert an existing backend array to this context. Dense inputs are converted with :meth:`asarray`; sparse inputs are converted with :meth:`assparse`. Parameters ---------- x: Dense or sparse backend array recognized by ``self.ops``. Returns ------- ArrayLike Converted dense or sparse backend object. Raises ------ NotImplementedError If ``x`` is neither dense nor sparse according to this backend. """ if self.ops.is_dense(x): return self.asarray(x) elif self.ops.is_sparse(x): return self.assparse(x) else: raise NotImplementedError
def __eq__(self, other: Any) -> bool: """ Return whether another object has the same effective backend policy. Parameters ---------- other: Object to compare against. Returns ------- bool ``True`` when ``other`` is a ``Context`` with equal backend operations and equal ``enable_checks``. """ if isinstance(other, Context): return self.ops == other.ops and self.enable_checks == other.enable_checks return False