From b85608ea2202d7bb2b9a3a21299a510c29ef9274 Mon Sep 17 00:00:00 2001 From: Pavlo Pelikh Date: Sat, 12 Sep 2026 17:39:27 -0400 Subject: [PATCH 1/4] Add Field, Space.scalars, and the ADR-010 guard `Field(InnerProductSpace)` is the scalar field as a one-dimensional space over itself, with `inner(a, b) = conj(a) * b` and Euclidean geometry. It is pointedly not a `CoordinateSpace`: the surface a scalar cannot honour -- `size`, `flatten`, `stacked` -- lives there, so the hierarchy already cuts in the right place. `RealField` and `ComplexField` are the concrete forms, public alongside `Field`. `ScalarShapeCheck` carries `minimum_level = "standard"` to match the `out_scalar` branch it will replace; at `cheap` it would tighten validation rather than preserve it. Under batched membership it permits leading axes, because ordinary space membership cannot require exactly one. `Space.scalars` returns the coefficient field on the same backend without touching the owning space's storage: a Hermitian space over `complex128` yields a `RealField` over `float64` and stays `complex128` itself. `Space.__init__` now rejects integer and Boolean storage, and a complex coefficient field over real storage -- the latter was accepted silently, since `_check_scalar` only rejects non-real multipliers when the field is real. The ADR-010 guard lands here rather than later because this commit opens the hole: `Field` is a `VectorSpace`, and `LinOp` endpoints have been bound to `VectorSpace` since #67, so `LinOp[X, Field]` would otherwise satisfy the bound and reinstate the class collapse ADR-010 rejected. No type bound expresses "a vector space that is not the field", so `LinOp.__init__` rejects a `Field` codomain explicitly. Co-Authored-By: Claude Opus 5 --- spacecore/__init__.py | 6 ++ spacecore/linop/_base.py | 7 +- spacecore/space/__init__.py | 6 ++ spacecore/space/base/__init__.py | 4 + spacecore/space/base/_field.py | 99 +++++++++++++++++++++++ spacecore/space/base/_space.py | 18 +++++ tests/generators/spaces.py | 13 ++- tests/spaces/_generated_helpers.py | 6 +- tests/spaces/test_fields.py | 123 +++++++++++++++++++++++++++++ 9 files changed, 277 insertions(+), 5 deletions(-) create mode 100644 spacecore/space/base/_field.py create mode 100644 tests/spaces/test_fields.py diff --git a/spacecore/__init__.py b/spacecore/__init__.py index a8d76e6..16c73e0 100644 --- a/spacecore/__init__.py +++ b/spacecore/__init__.py @@ -91,6 +91,9 @@ DTypeCheck, FieldCheck, HermitianCheck, + Field, + RealField, + ComplexField, CoordinateSpace, DenseCoordinateSpace, DenseVectorSpace, @@ -233,6 +236,9 @@ "TreeElement", "TreeSpace", "TreeSpectralDecomposition", + "Field", + "RealField", + "ComplexField", "CoordinateSpace", "InnerProductSpace", "StarSpace", diff --git a/spacecore/linop/_base.py b/spacecore/linop/_base.py index fdc30e9..161755e 100644 --- a/spacecore/linop/_base.py +++ b/spacecore/linop/_base.py @@ -13,7 +13,7 @@ from ..space._capabilities import _CAP_BATCH, _CAP_COORDINATE, require from ..backend import PyTreeNode from .._repr import describe_space -from ..space import VectorSpace +from ..space import VectorSpace, Field from ..contextual import Context from ..contextual import ContextBound @@ -96,6 +96,11 @@ def __init__( ctx: Context | str | None = None, check_level: CheckLevel | bool | None = None, ): + if isinstance(cod, Field): + raise TypeError( + "LinOp codomain cannot be a Field (ADR-010); use Functional " + "for scalar-valued maps and Riesz gradients." + ) self.dom, self.cod = self._bind_context(ctx, dom, cod, check_level=check_level) @property diff --git a/spacecore/space/__init__.py b/spacecore/space/__init__.py index 9f60be9..376cd58 100644 --- a/spacecore/space/__init__.py +++ b/spacecore/space/__init__.py @@ -12,6 +12,9 @@ SquareMatrixCheck, ) from .base import ( + Field, + RealField, + ComplexField, CoordinateSpace, EuclideanInnerProduct, EuclideanJordanAlgebraSpace, @@ -44,6 +47,9 @@ "InnerProduct", "EuclideanInnerProduct", "WeightedInnerProduct", + "Field", + "RealField", + "ComplexField", "CoordinateSpace", "InnerProductSpace", "StarSpace", diff --git a/spacecore/space/base/__init__.py b/spacecore/space/base/__init__.py index 634c4aa..2016a3b 100644 --- a/spacecore/space/base/__init__.py +++ b/spacecore/space/base/__init__.py @@ -9,10 +9,14 @@ InnerProductSpace, WeightedInnerProduct, ) +from ._field import Field, RealField, ComplexField from ._star import StarSpace from ._jordan import JordanAlgebraSpace, EuclideanJordanAlgebraSpace __all__ = [ + "Field", + "RealField", + "ComplexField", "Space", "VectorSpace", "CoordinateSpace", diff --git a/spacecore/space/base/_field.py b/spacecore/space/base/_field.py new file mode 100644 index 0000000..65e3e74 --- /dev/null +++ b/spacecore/space/base/_field.py @@ -0,0 +1,99 @@ +"""Scalar fields, independent of finite coordinate representations.""" +from __future__ import annotations + +from abc import abstractmethod +from dataclasses import dataclass +from typing import ClassVar + +from ..._check_policy import CheckLevel +from ...contextual import Context +from ..checks import SpaceCheck +from ._inner_product import EuclideanInnerProduct, InnerProductSpace + + +@dataclass(frozen=True) +class ScalarShapeCheck(SpaceCheck): + """Require a single scalar at standard validation and above.""" + + name: str = "scalar_shape" + minimum_level: ClassVar[CheckLevel] = "standard" + + def validate(self, space, x, *, allow_leading): + # Scalars have no trailing axes; ordinary batched membership permits + # arbitrary leading axes. Exact (N,) checks remain at the caller. + return allow_leading or self.is_valid(space, x) + + def is_valid(self, space, x): + return tuple(getattr(x, "shape", ())) == () + + def error_message(self, space, x): + return f"Expected scalar output with shape (), got {tuple(getattr(x, 'shape', ()))}." + + +class Field(InnerProductSpace): + """Euclidean scalar field with no coordinate or batching capability. + + Field membership checks scalar shape at standard and strict levels. + Its context records the representation of coefficients, independently of + the storage dtype of a vector space using those coefficients. + """ + + checks = (ScalarShapeCheck(),) + geometry = EuclideanInnerProduct() + + @property + @abstractmethod + def declared_scalar_field(self): + """The real or complex coefficient field.""" + + def zeros(self): + """Return scalar zero in the field's context.""" + return self.ctx.asarray(0.) + + def ones(self): + """Return scalar one for deterministic probes.""" + return self.ctx.asarray(1.) + + def add(self, a, b): + """Return the sum of two scalars.""" + return a + b + + def scale(self, s, a): + """Return the product of two scalars.""" + return s * a + + def inner(self, a, b): + """Return ``conj(a) * b``.""" + return self.ops.conj(a) * b + + def _convert(self, new_ctx): + # Endpoint binding chooses a backend and precision, not a different + # mathematical field. In particular complex codomains survive binding + # to functionals whose domains use real storage. + dtype = (new_ctx.ops.real_dtype(new_ctx.dtype) if self.scalar_field == "real" + else new_ctx.ops.complex_dtype(new_ctx.dtype)) + return type(self)(Context(new_ctx.ops, dtype), check_level=self.check_level) + + +class RealField(Field): + """Real scalars, represented in a real floating dtype.""" + + declared_scalar_field = "real" + + def __init__(self, ctx=None, check_level=None): + super().__init__(ctx, check_level=check_level) + self._ctx = Context(self.ops, self.ops.real_dtype(self.dtype)) + + +class ComplexField(Field): + """Complex scalars; explicit contexts must use complex floating storage.""" + + declared_scalar_field = "complex" + + def __init__(self, ctx=None, check_level=None): + if ctx is None: + from ...contextual import normalize_context + + default = normalize_context(None) + ctx = Context(default.ops, default.ops.complex_dtype(default.dtype)) + super().__init__(ctx, check_level=check_level) diff --git a/spacecore/space/base/_space.py b/spacecore/space/base/_space.py index d65e8b0..433bfa9 100644 --- a/spacecore/space/base/_space.py +++ b/spacecore/space/base/_space.py @@ -33,6 +33,10 @@ def __init__( check_level: CheckLevel | bool | None = None, ) -> None: super().__init__(ctx, check_level=check_level) + if not self.ops.xp.isdtype(self.dtype, ("real floating", "complex floating")): + raise TypeError("Spaces require floating storage, not integer or Boolean dtype.") + if self.scalar_field == "complex" and not self.ops.is_complex_dtype(self.dtype): + raise TypeError("A complex scalar field requires complex floating storage.") # Lazy caches. Populated on first call to member_checks() / # _checks_for_level() and reused for every subsequent membership # validation. Spaces are immutable after construction (`convert` @@ -77,6 +81,20 @@ def scalar_field(self) -> Literal["real", "complex"]: """ return self.declared_scalar_field or self.field + @property + def scalars(self): + """Return the coefficient field on this backend without changing storage. + + Real coefficients over complex storage use the corresponding real + precision; entry and coefficient fields remain separate properties. + """ + from ._field import RealField, ComplexField + + if self.scalar_field == "real": + ctx = Context(self.ops, self.ops.real_dtype(self.dtype)) + return RealField(ctx, check_level=self.check_level) + return ComplexField(self.ctx, check_level=self.check_level) + def _check_scalar(self, a: Any) -> None: """Raise if ``a`` is not an admissible multiplier for this space.""" if self.scalar_field == "real" and is_recognizably_nonreal(a): diff --git a/tests/generators/spaces.py b/tests/generators/spaces.py index 39cf62e..2759f8f 100644 --- a/tests/generators/spaces.py +++ b/tests/generators/spaces.py @@ -591,4 +591,15 @@ def vector_space_law_cases(*, seed: int | None = DEFAULT_SEED) -> tuple[SpaceCas for case in jordan_space_cases(check_level="standard", seed=seed) if case.reference["kind"] in {"elementwise", "stacked", "tree"} ) - return tuple(dense) + tuple(vectors) + tuple(trees) + jordan + fields = tuple( + GeneratedCase( + obj=field, + reference={name: field.ctx.asarray(value) for name, value in + zip(("x", "y", "z", "a", "b"), (1., -2., 3., .5, -1.25))}, + capabilities=frozenset({"vector", "inner_product", field.field}), + id=f"field-{field.field}", + ) + for field in (sc.RealField(_context(np.float64)), + sc.ComplexField(_context(np.complex128))) + ) + return tuple(dense) + tuple(vectors) + tuple(trees) + jordan + fields diff --git a/tests/spaces/_generated_helpers.py b/tests/spaces/_generated_helpers.py index a98eaf8..11150ff 100644 --- a/tests/spaces/_generated_helpers.py +++ b/tests/spaces/_generated_helpers.py @@ -18,13 +18,13 @@ def tolerances(dtype: Any) -> tuple[float, float]: return (2e-5, 2e-6) if real_dtype == np.dtype(np.float32) else (1e-10, 1e-11) -def assert_allclose(space: sc.CoordinateSpace, actual: Any, expected: Any) -> None: +def assert_allclose(space: sc.VectorSpace, actual: Any, expected: Any) -> None: rtol, atol = tolerances(space.dtype) if not isinstance(space, sc.TreeSpace) and not space.ops.is_dense(expected): expected = space.ctx.asarray(expected) np.testing.assert_allclose( - to_numpy(space.flatten(actual)), - to_numpy(space.flatten(expected)), + to_numpy(space.flatten(actual) if isinstance(space, sc.CoordinateSpace) else actual), + to_numpy(space.flatten(expected) if isinstance(space, sc.CoordinateSpace) else expected), rtol=rtol, atol=atol, ) diff --git a/tests/spaces/test_fields.py b/tests/spaces/test_fields.py new file mode 100644 index 0000000..173c103 --- /dev/null +++ b/tests/spaces/test_fields.py @@ -0,0 +1,123 @@ +"""Scalar fields preserve coefficient mathematics independently of storage.""" +import numpy as np +import pytest + +import spacecore as sc +from spacecore.contextual import normalize_context +from spacecore._batching import _batched_inner +from spacecore.space._capabilities import probe_element, require + + +class RealCoefficients(sc.DenseCoordinateSpace): + declared_scalar_field = 'real' + + +class ComplexCoefficients(sc.DenseCoordinateSpace): + declared_scalar_field = 'complex' + + +@pytest.mark.parametrize('dtype', ['float32', 'float64', 'complex64', 'complex128']) +@pytest.mark.parametrize('declared', ['real', 'complex']) +def test_scalar_storage_table(dtype, declared): + ctx = sc.Context(sc.NumpyOps(), dtype=dtype) + cls = RealCoefficients if declared == 'real' else ComplexCoefficients + if declared == 'complex' and not dtype.startswith('complex'): + with pytest.raises(TypeError, match='complex floating storage'): + cls((2,), ctx) + return + space = cls((2,), ctx) + original_ctx = space.ctx + field = space.scalars + assert isinstance(field, sc.RealField if declared == 'real' else sc.ComplexField) + assert field.dtype == (ctx.ops.real_dtype(ctx.dtype) if declared == 'real' else ctx.dtype) + assert field.ops == ctx.ops + assert space.ctx is original_ctx + assert space.dtype == np.dtype(dtype) + if declared == 'real': + with pytest.raises(sc.SpaceValidationError): + space.check_scalar(1j) + + +@pytest.mark.parametrize('dtype', ['int32', 'int64', 'bool']) +@pytest.mark.parametrize('cls', [sc.RealField, sc.ComplexField, sc.DenseCoordinateSpace]) +def test_integer_boolean_storage_rejected(dtype, cls): + ctx = sc.Context(sc.NumpyOps(), dtype=dtype) + with pytest.raises(TypeError, match='floating storage'): + cls((2,), ctx) if cls is sc.DenseCoordinateSpace else cls(ctx) + + +@pytest.mark.parametrize('cls,dtype', [(sc.RealField, 'float64'), (sc.ComplexField, 'complex128')]) +def test_field_laws_geometry_probe_and_missing_coordinates(cls, dtype): + field = cls(sc.Context(sc.NumpyOps(), dtype=dtype)) + assert isinstance(field, sc.VectorSpace) + assert isinstance(field, sc.InnerProductSpace) + assert not isinstance(field, sc.CoordinateSpace) + for name in ('shape', 'size', 'flatten', 'stacked'): + assert not hasattr(field, name) + a = field.ctx.asarray(2 + 3j if cls is sc.ComplexField else 2.) + b = field.ctx.asarray(4.) + np.testing.assert_allclose(field.inner(a, b), np.conj(a) * b) + assert field.riesz(a) is a + assert field.riesz_inverse(a) is a + assert field.is_euclidean + assert field.zeros().shape == () + assert probe_element(field, field.ops, field.dtype) == 1 + np.testing.assert_allclose(field.axpy(2, a, b), 2 * a + b) + with pytest.raises(sc.CapabilityError, match='CoordinateSpace'): + require(field, sc.CoordinateSpace, 'flatten') + with pytest.raises(sc.CapabilityError, match='CoordinateSpace'): + _batched_inner(field, np.ones(2), np.ones(2)) + + +@pytest.mark.parametrize('level,raises', [('none', False), ('cheap', False), + ('standard', True), ('strict', True)]) +def test_scalar_member_check_levels(level, raises): + field = sc.RealField(check_level=level) + field.check_member(2.) + field.check_member(field.ones()) + if raises: + with pytest.raises(sc.SpaceValidationError, match='Expected scalar output'): + field.check_member(np.ones(2)) + else: + field.check_member(np.ones(2)) + + +def test_hermitian_scalars_do_not_mutate_storage(): + ctx = sc.Context(sc.NumpyOps(), dtype=np.complex128) + space = sc.HermitianSpace(2, ctx=ctx) + original_ctx = space.ctx + field = space.scalars + assert isinstance(field, sc.RealField) + assert field.dtype == np.dtype('float64') + assert space.dtype == np.dtype('complex128') + assert space.ctx is original_ctx + assert space.field == 'complex' and space.scalar_field == 'real' + + +@pytest.mark.parametrize('family', ['numpy', 'torch', 'jax']) +def test_fields_preserve_backend_and_precision(family): + if family != 'numpy': + pytest.importorskip(family) + ops = normalize_context(family).ops + ctx = sc.Context(ops, dtype='complex64') + space = sc.HermitianSpace(2, ctx=ctx) + assert space.scalars.ops == ops + assert space.scalars.dtype == ops.sanitize_dtype('float32') + assert space.scalars.ones().dtype == ops.sanitize_dtype('float32') + assert sc.ComplexField(ctx).ones().dtype == ops.sanitize_dtype('complex64') + + +def test_default_fields_and_conversion_keep_mathematical_field(): + assert sc.RealField().field == 'real' + assert sc.ComplexField().field == 'complex' + ctx = sc.Context(sc.NumpyOps(), dtype='float32') + assert sc.ComplexField().convert(ctx).dtype == np.dtype('complex64') + assert sc.RealField().convert(sc.Context(sc.NumpyOps(), 'complex64')).dtype == np.dtype('float32') + + +def test_linop_rejects_field_codomain_at_construction(): + ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) + dom = sc.DenseCoordinateSpace((2,), ctx) + for field in (sc.RealField(ctx), sc.ComplexField()): + with pytest.raises(TypeError, match='ADR-010'): + sc.MatrixFreeLinOp(lambda x: x.sum(), lambda y: np.ones(2) * y, dom, field) From aec845400710c178d852784f62496b99a2fcdeec Mon Sep 17 00:00:00 2001 From: Pavlo Pelikh Date: Sat, 12 Sep 2026 17:40:19 -0400 Subject: [PATCH 2/4] Give Functional a Field codomain and retire out_scalar `Functional` binds two endpoints, defaulting the codomain to `dom.scalars` so no existing constructor call changes. The codomain is independent of the domain's coefficient field: a functional on a real space may be complex-valued, and conversion preserves that rather than promoting the domain. With a codomain object, the 17 `out_scalar=True` decorators become `out_space="codomain"` and the parallel branch leaves `checked_method`. Scalar output is now validated by ordinary space membership through the field's `ScalarShapeCheck`. The 4 `out_batched_scalar=True` sites stay, deliberately. That flag asserts `shape == (N,)`; the codomain route goes through `_run_checks(..., allow_leading=True)` and accepts any leading dimensions, which is strictly weaker. A `Field` cannot say "exactly one leading axis" without `stacked`, which it must not have, and ADR-006 models the batch axis as a leading array axis rather than as a space. Output checks follow the functional's policy, not the field's, which `out_scalar` did implicitly by reading the functional's check level. That needs a copy of the codomain at the functional's level, and building it as `type(cod)(ctx, check_level=...)` breaks any `Field` subclass whose constructor takes more -- silently, by binding the context to the wrong parameter. `ContextBound._with_check_level` routes through the subclass's own `_convert` instead, which is the extension point that already has to know how to rebuild it. Co-Authored-By: Claude Opus 5 --- spacecore/_checks.py | 27 +--- spacecore/contextual/_bound.py | 13 ++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 575 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 737 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 637 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 2041 bytes .../__pycache__/_algebra.cpython-313.pyc | Bin 0 -> 41057 bytes .../__pycache__/_base.cpython-310.pyc | Bin 0 -> 5589 bytes .../__pycache__/_base.cpython-311.pyc | Bin 0 -> 8962 bytes .../__pycache__/_base.cpython-312.pyc | Bin 0 -> 6876 bytes .../__pycache__/_base.cpython-313.pyc | Bin 0 -> 13760 bytes .../_battery.cpython-313-pytest-9.0.2.pyc | Bin 0 -> 22085 bytes .../__pycache__/_battery.cpython-313.pyc | Bin 0 -> 22443 bytes .../__pycache__/_composed.cpython-310.pyc | Bin 0 -> 4129 bytes .../__pycache__/_composed.cpython-311.pyc | Bin 0 -> 6748 bytes .../__pycache__/_composed.cpython-312.pyc | Bin 0 -> 5731 bytes .../__pycache__/_composed.cpython-313.pyc | Bin 0 -> 9074 bytes ...least_squares.cpython-313-pytest-9.0.2.pyc | Bin 0 -> 5567 bytes .../_least_squares.cpython-313.pyc | Bin 0 -> 5453 bytes .../__pycache__/_linear.cpython-310.pyc | Bin 0 -> 11937 bytes .../__pycache__/_linear.cpython-311.pyc | Bin 0 -> 16792 bytes .../__pycache__/_linear.cpython-312.pyc | Bin 0 -> 16001 bytes .../__pycache__/_linear.cpython-313.pyc | Bin 0 -> 15645 bytes .../_proximal.cpython-313-pytest-9.0.2.pyc | Bin 0 -> 9590 bytes .../__pycache__/_proximal.cpython-313.pyc | Bin 0 -> 9885 bytes .../__pycache__/_quadratic.cpython-310.pyc | Bin 0 -> 7883 bytes .../__pycache__/_quadratic.cpython-311.pyc | Bin 0 -> 10960 bytes .../__pycache__/_quadratic.cpython-312.pyc | Bin 0 -> 11399 bytes .../__pycache__/_quadratic.cpython-313.pyc | Bin 0 -> 10190 bytes .../__pycache__/_realified.cpython-313.pyc | Bin 0 -> 12724 bytes spacecore/functional/_algebra.py | 68 +++++---- spacecore/functional/_base.py | 34 +++-- spacecore/functional/_composed.py | 4 +- spacecore/functional/_linear.py | 23 ++- spacecore/functional/_quadratic.py | 2 +- spacecore/functional/_realified.py | 4 +- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 1160 bytes .../__pycache__/_coordinate.cpython-313.pyc | Bin 0 -> 5597 bytes .../__pycache__/_entropy.cpython-313.pyc | Bin 0 -> 9959 bytes .../tools/__pycache__/_huber.cpython-313.pyc | Bin 0 -> 5686 bytes .../_least_squares.cpython-313.pyc | Bin 0 -> 5459 bytes .../tools/__pycache__/_norms.cpython-313.pyc | Bin 0 -> 8581 bytes .../__pycache__/_proximal.cpython-313.pyc | Bin 0 -> 12503 bytes .../__pycache__/_spectral.cpython-313.pyc | Bin 0 -> 14524 bytes spacecore/functional/tools/_entropy.py | 4 +- spacecore/functional/tools/_huber.py | 2 +- spacecore/functional/tools/_norms.py | 4 +- spacecore/functional/tools/_spectral.py | 4 +- tests/functional/test_field_codomain.py | 139 ++++++++++++++++++ tests/functional/test_functional_base.py | 13 +- 50 files changed, 252 insertions(+), 89 deletions(-) create mode 100644 spacecore/functional/__pycache__/__init__.cpython-310.pyc create mode 100644 spacecore/functional/__pycache__/__init__.cpython-311.pyc create mode 100644 spacecore/functional/__pycache__/__init__.cpython-312.pyc create mode 100644 spacecore/functional/__pycache__/__init__.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_algebra.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_base.cpython-310.pyc create mode 100644 spacecore/functional/__pycache__/_base.cpython-311.pyc create mode 100644 spacecore/functional/__pycache__/_base.cpython-312.pyc create mode 100644 spacecore/functional/__pycache__/_base.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_battery.cpython-313-pytest-9.0.2.pyc create mode 100644 spacecore/functional/__pycache__/_battery.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_composed.cpython-310.pyc create mode 100644 spacecore/functional/__pycache__/_composed.cpython-311.pyc create mode 100644 spacecore/functional/__pycache__/_composed.cpython-312.pyc create mode 100644 spacecore/functional/__pycache__/_composed.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_least_squares.cpython-313-pytest-9.0.2.pyc create mode 100644 spacecore/functional/__pycache__/_least_squares.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_linear.cpython-310.pyc create mode 100644 spacecore/functional/__pycache__/_linear.cpython-311.pyc create mode 100644 spacecore/functional/__pycache__/_linear.cpython-312.pyc create mode 100644 spacecore/functional/__pycache__/_linear.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_proximal.cpython-313-pytest-9.0.2.pyc create mode 100644 spacecore/functional/__pycache__/_proximal.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_quadratic.cpython-310.pyc create mode 100644 spacecore/functional/__pycache__/_quadratic.cpython-311.pyc create mode 100644 spacecore/functional/__pycache__/_quadratic.cpython-312.pyc create mode 100644 spacecore/functional/__pycache__/_quadratic.cpython-313.pyc create mode 100644 spacecore/functional/__pycache__/_realified.cpython-313.pyc create mode 100644 spacecore/functional/tools/__pycache__/__init__.cpython-313.pyc create mode 100644 spacecore/functional/tools/__pycache__/_coordinate.cpython-313.pyc create mode 100644 spacecore/functional/tools/__pycache__/_entropy.cpython-313.pyc create mode 100644 spacecore/functional/tools/__pycache__/_huber.cpython-313.pyc create mode 100644 spacecore/functional/tools/__pycache__/_least_squares.cpython-313.pyc create mode 100644 spacecore/functional/tools/__pycache__/_norms.cpython-313.pyc create mode 100644 spacecore/functional/tools/__pycache__/_proximal.cpython-313.pyc create mode 100644 spacecore/functional/tools/__pycache__/_spectral.cpython-313.pyc create mode 100644 tests/functional/test_field_codomain.py diff --git a/spacecore/_checks.py b/spacecore/_checks.py index 963ba6f..37b2963 100644 --- a/spacecore/_checks.py +++ b/spacecore/_checks.py @@ -46,7 +46,6 @@ def checked_method( arg_positions: int | tuple[int, ...] | None = None, in_batched: bool = False, out_batched: bool = False, - out_scalar: bool = False, out_batched_scalar: bool = False, ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: """ @@ -71,16 +70,12 @@ def checked_method( Validate inputs as leading-axis batches instead of single elements. out_batched : bool, optional Validate outputs as leading-axis batches instead of single elements. - out_scalar : bool, optional - Validate that the output is scalar-shaped (``shape == ()``). This is the - codomain check for a :class:`~spacecore.functional.Functional`, whose - codomain is the scalar field rather than a :class:`Space` object and so - cannot be expressed through ``out_space``. out_batched_scalar : bool, optional Validate that the output is a vector of scalars, one per input element: ``shape == (N,)`` for a leading batch of size ``N``. The batch size is read from the *input* named by ``in_space`` at the first checked - position, so this flag requires ``in_space``. + position, so this flag requires ``in_space``. Deliberately retained: + batched Field membership cannot enforce exactly one leading axis. Returns ------- @@ -101,9 +96,6 @@ def checked_method( hand-written ``_checks_at_least("standard")`` gating they replace. Shape is static under ``jax.jit``, so they are safe to run while tracing. """ - require_mutually_exclusive( - "out_scalar", out_scalar or None, "out_batched_scalar", out_batched_scalar or None - ) if out_batched_scalar and in_space is None: # TypeError, matching ``require_mutually_exclusive``: both are misuse of # the decorator's argument combination, not a bad runtime value. @@ -156,18 +148,11 @@ def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any: else: out_target._check_member(y) - if (out_scalar or out_batched_scalar) and check_level_at_least(level, "standard"): - from ._batching import _check_scalar_shape + if out_batched_scalar and check_level_at_least(level, "standard"): + from ._batching import _check_scalar_shape, _leading_batch_size - if out_scalar: - _check_scalar_shape(y, ()) - else: - from ._batching import _leading_batch_size - - batch_target = self if in_space == "self" else getattr(self, in_space) - _check_scalar_shape( - y, (_leading_batch_size(batch_target, args[batch_pos]),) - ) + batch_target = self if in_space == "self" else getattr(self, in_space) + _check_scalar_shape(y, (_leading_batch_size(batch_target, args[batch_pos]),)) return y diff --git a/spacecore/contextual/_bound.py b/spacecore/contextual/_bound.py index 294b938..0f25d16 100644 --- a/spacecore/contextual/_bound.py +++ b/spacecore/contextual/_bound.py @@ -115,6 +115,19 @@ def _bind_context( self._check_level = minimum_check_level(tuple(levels)) if levels else get_check_level() return tuple(child.convert(resolved) for child in children) + def _with_check_level(self, level: CheckLevel | bool) -> Self: + """Return a copy of this object validating at ``level``. + + Routes through the subclass's own :meth:`_convert` rather than calling + ``type(self)(...)``, so a subclass whose constructor takes more than a + context keeps working — ``_convert`` is the extension point that already + has to know how to rebuild it. Mirrors :meth:`convert`, which likewise + assigns ``_check_level`` onto a freshly built object. + """ + result = self._convert(self.ctx) + result._check_level = normalize_check_level(level) + return result + @property def ops(self) -> BackendOps: """Return backend operations associated with this object's context.""" diff --git a/spacecore/functional/__pycache__/__init__.cpython-310.pyc b/spacecore/functional/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..220f0cb64cd1b088f6f565a210470da3f128a002 GIT binary patch literal 575 zcmZ8d%SyyB6ixb=KBkpnkU?B^+l4dWT0{^A27HXnMzRU9iCRsXnzo{Tjyr#;TUY*q zE0Ye+R0BEWoZNfv;mSA;fj*1t$-~$J;M0))(Hc0>w_0kT009cA#i6i}1if!5yH3cZg!@tw8F(9j-FMi8^>DykRuCZHlbU zWkGnAkb)+e6s4?kBPI8YmrPaDFQ}uWunp&%;?!!#r(G$dTeaO2mAtq+(qc3O32tR;Czvn0HZop2iM^_~|#zdWi8i zzJzm(KY)rf@8CPBN|LcmDmL40X@+rL2yG3jo>aE!N~dn D$zGh5 literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/__init__.cpython-311.pyc b/spacecore/functional/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54ee258121b0b6a5713c3ae8ce92688063ed197e GIT binary patch literal 737 zcmZ`$&uiN-6qfuWwv%RzP|6q#a`j;~?9{>7B_&Y0?pGLWx(H3uRVL8O zE~EdAk3pxwPP+wq>uHihnsju}kI(OYe){zB{Bt<$0~<$g_dbn00Dik>9k%b{^0j?^ z2Mi#r?Afg(Rf{m$?rK)mAjZe8KxvuvAKx-z2ra!$NPCYZ4 zbfcVYSaVO3GmbZ!cqPYK>YKfzqTu?aR;;1*)|tD{@`4k+#oRw3wa&jxHRu0+bN5>A zMfs{BOp`jNQ>CRD-e}WU2iCFn9rPST4iX2k1Gj%2T6mt`F%ia0QH)K9-;;`Wr%ZC~ zd?n?E?WE|eZfQC>)5o9JZdR8#eWy!=!1w}O+7?_LXK$=}m6c>JRLMntcA6b3+DKm1 zRd&ed*{md#Pn70asi#ZRQ&qu@{VuGMz`r#iQ=^dW=`aL|H- mU*JJ|GZ$de-j)mSuKj;5z~lC|Tn5R&TY@#5NAA%hYvM16-rE@f literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/__init__.cpython-312.pyc b/spacecore/functional/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72876f8c56ceed92503b077ad20412971113bbdc GIT binary patch literal 637 zcmZ8ezi-qq7`5}`lHBzWI-v?7fo{z}BQb%5Sgty$qJ^RgiF&b&ZD^<+2PZjEHvS5B zHvX1Q9r1DlVqimbW5Qof?Hzo>%lBTMpFe#*ACHH~*WRn`cas32??A4~^S&+*{pT|h zh#-MQps)@|fT0gns3Q{Tn8ZDg)IcXB(J4uFMzUUu)liSfNNA99&elzv5*o}xcbi z;;3d^&Wx2s109EMy~~2qvMO6jcj_~jKGuGZN UjxW&h5yqJm5d3=uCs^DMIPj_twol!E!hRR zSI&1iL!7oo_fU`pBP>NKB$89ocMd=BQ6Z>&1c7hr)e!lJS>)g`_R%X|e^ zl%1_U;;XRA*IU-q|~xp#1v=QU9`gCV@F#fHB0Ad_H&= zb_dt`V0TcRo>ckhUvZY~MaOHrs2_x$=LB^l=$_r3L2ZG27|61`@I46e3~fnTdO(Ni zN!OOHB@c|h@OHG49vOW(OZjN}B^_9}Vc#1~eG6U#B}ll6bg#0J@J!X(b`!HDT!mV< zlQ1W~m*6Gr#f~_arV<$Qnazh=jTY2Q-(jL4L?D_Oo(Q^~XkIx3Md<Tn3|L@7n5 zq{16u8`DW7jf+z@N+Z$ySFS7J12UYb!b==#`D+n^eQ^v@4$o0u^nK&Mx-ZdVERCoz zU}DhBYbhMNX%Dc=fO1}zCZp-vU1=rK7Ny;iuIe%~z*>+|{=NY1A7nJCsw@#dUp+a( zaspgr$%Mana(IfQ20Z?dXX#Y7u)%uri@2l{!E>iw3)=#QRxH$1lQd4A4Nc5peu+(j z92;iP2HT}Zj0&o?%)8c#Ch+>HX-wC{YiLCov@cBOFv`V)XTkf@HcVnU!zjTB!3x0? z!6SlIf@y+Tf;EB}f^`B$kS3TTm?sz`$Pg?NY!GA#@Hy9DlVFR0-gzhz<`6z1&rzOuo#0OH$`h5B@Ol{{c$;Y(xM6 literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_algebra.cpython-313.pyc b/spacecore/functional/__pycache__/_algebra.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5af4cf8c74fd2f913f2160ccf5233038c566b1e0 GIT binary patch literal 41057 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 E2f`7PF#rGn literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_base.cpython-311.pyc b/spacecore/functional/__pycache__/_base.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f47cf35758a1640172c3fefe45248c43ba986c2f GIT binary patch literal 8962 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 literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_base.cpython-312.pyc b/spacecore/functional/__pycache__/_base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61b425569e54fca054b4bb94c2d9d3ec28c16fd2 GIT binary patch literal 6876 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 literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_base.cpython-313.pyc b/spacecore/functional/__pycache__/_base.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e33f91380a02f87f85c97d2502c8daf0025f2351 GIT binary patch literal 13760 zcmc&*TWlQHd7iy;xV$WHk|rk}b)tWr~sesr+DT(X4FW`Mla^{wL0F+81_#1$LmDhxGL1~dQm?f5CPQtBL0bn@kY@& z-Xxm%y$a`*?@wU^p&}4E~{}Ylg=4SxucnUI)Sy<72mB+VVIfJ zMI#!Q4We0*%!p)e8Jz-SH2F#6qLD(E;8-Rjuw6Z8L}*mYcOq$|5;t9z?_@IlLe}yh z%Pi{2G&=g`Xqq_T-+n+%dRz|#^XRN1ToFaMBQD{IxP`Yz=}{s(BA$ph>>{}GC(H?^#ry9`^8HfZJYK(*; z4GcBmYa>I=fSMQz;%hTQEr5aywc=|FLv4}4NGn6_kv+tZ7`X#bJ42fQbuhFU&?bhq zM1~@p8S0D-N47Au6;LNbU9*pYueV0J@blOq4`$p(_0B%GMZ%G8ZrL3fiS#hk6WJTt z&d_#18bcbOUWR%jeWGu{6WV75PUO>Z5&}K-{&pZvAMq+mAi|tAd{Iy3jf4iS)iP;K z*O+g@fj}%)>J^J=!pMSOjC9UK8)}U2+F|XCHvF_Uf$x`+5UG4l(@itK2+7d&^s;8= zGD2T40*heIR9Jg1r!6Lh$Ou!*oi_ldlZ$!^BQY0Y5aL9V&%^vZ!AzwkO{%|Wh@6oK zB-6PJe=IU@g8P8Ihcw=Z#`0lm^O;mCa|tccJ2Ur+5zp;5F_Dz16!CQ|HWbj)35_Kf z2r#HWs|uPixtL8Ei`Xu{%q?TfB+Ukz$6_SlSgS4KK-DQ-=#WQ4n1NyE31Im^5DQE| z!y{!x;upi5iN(T!cy0-8iV`^le2F0~Becm(+5nK1G~?b!i}M$S6~h?uy`#AtG<-g1d~|jztq#kD803~V za38O5N}2?c3d!z8b_)k4q3u0DKpd)rA1HIB4!<5!?E|dZ&ruyeXL=8isl=1^&A6t} zruv!j9VoU=yJo!cMnyq8d+f5tY416jg`XMkxuU}ZgI^rqb{Y~5igFIW@`4LM=0MMuQV$!NYPX@%U$pEfz1fhx28{2FxuAqR zlPmot-Rf|awwO0_+MEG4W?l@#oHmpxtWbm4gpsYfA~CJHB70eZq?t^U_{I& zUpY>i)@q8*CDRGp##LyA32tZ=Sh2-Bgc%3Bdi0*Ak`y|e26uyRLe_s=m?xje+}qrK9tk2fudkPW$j$`|xVwPWb7y@YCzACo551P%p?rP|7=0(W*LE$9JB!4(CYYw37>>DtoOpxB3+kGYfG$E0;; zib{or70Ydu`4nJc<|OerrYc{rUn)1NwT#lT>Ls)kPf)?jE6O?EC8C3*Z%9$RsVulc z?tBnPI4*6sQEh@W9S%uaGo&e}RmNh(++jHrYyU`oE}nwdVwiAiVCmy=Bb(FUQ3#lm zjG2@+c;teyd!|WN0FiCnY zFUX_|NH?^+o`Q|KWF!~P=Xjjld0MG3$VkypRVE;;y#W75B;nj+i)fnDFBovwz&^1U zR5`2)`QOqmlde{7dYMBk*OKMMl1cva}Wag%qX_*tEJPMaZG*OZMQH$#g%uT6*y3l?fVEMvylNw02>3RFYb;mS$m z6DnewX^>#%jMQ>CP$lV^qDb-41!4&sx{xx4v(N?hVF2Pf09Q1O!_X`GQkWMR#T-!{ zJQjM2w2Lzi+ixMaMCTh5gwwKm5;D&RozH~#R-Ah5Enmi%5MqCkMf81{bCE%mK?$z;7*Qn1vjQo&Nq65+onq>Z(Nbf(aE zdcFCjg8EWzWV4Wx<^O$zlzot6V{{T6o=4C~Iu-Chg;Ay;>11p23BWnrP!Fg!9DXTU z0J{kfd?l0 zL$IW9^#fpfljB$Q%s59L>1mejwix1&ZrO)Z8x*!ljN^x2zKG{BR|Q))M&%c|*Dd9H zE&Fhly3q+rQu)?ND6ZCYl$N~2n7048xB>n@(t=WL&XMX+lyLO z={*2&x8TBk0`3;lZcUkS_bF#w_7NQX=2`jgK+%|!8O!EVsbLBsKx2@}>;SeClAOIP|WU?893>PxU(NzeL|8>JxqdJi4z?u3xDziC2pmN zXDl}A90zQ>PjQ{%Nr9zdU^J{}v#I5Qr4T%maNvECKgdVtBK+{^yc6<*I4t7KU{IXB z-gyz^k%c@(hkTS@O_FKH4abq5`&n(JwS|jy-#|mN4aF7Z*V~l(gFg%IUiGdA_ZHN> zA3`${y>gn3yVIU=Pui;~=j!o^pBdLVzFDFdo0bFelcJ;5jEAVa-*MFYneHV$Hr{!a^PcxRz57@`Ma>U^sM!L2K zetl{lyfYAs?Jd3GTs@b9YM9F>Q#p9l5aB#x-$iZ_7ciipLrO>$8G>t(oCwHt7g>Tn zi=vvS(5SHIMAgbLm8`r)Jx-&jB%_}J~Kw>PbaUnuN8wch?> zq4C9PHE?V5di%jbA`Xta|1WmtqZQBOfRbeIQqE`PClD{z!Wu{eP{wC24V(h%ddj&2 z>U>9;tH^dgZ7j&!#8_-tYIbShan@q&F=E{ku~dOL2*tnoW5u}yVu!Idh#lAc1U-t5 z)GyWjKMU@>F}K>k9vmsCBOl^O{X2S&Tu0%9nf2XSz2r&t`O%VTfsjt$!Cm!oYI z8i*rSY7WumH+$j;tWiMnu%_F06${CWu(dj@Y=o6;$#s=i>r%|B(g^r;kijkfB?MvV zx;BxI!%?#BGj@e-^X(uZDdFQ4ooXO7Xi%A3!UnrhPPVj5& zDJT%SaX_3B`3?CLu4V=K(_$+X2^2RyY%7ZXXK6pkwBda-|K4<2+c!9^(rVd9pqmYr z4~x!ZMHdZS@?EadD@#bpT~+P%im%YuEUlxi%zE({x5qaj)^{&)PqA3oh)DLgSGNixyhl zx!(Rnq49}wJyWzQm`PC_J@W?<%5rh}{}9R(*kP5QJ6LX`I88#vwxdxd0+LX0J|>Z} zMU;g$T*ZdsPcgE%M#XDX{5gt^)FzuFNn2%exbi;r!7%{z{-xUc2x=cFo=ZM9wZ%cq zFpYdUHJO$PY`EJ_%OpD^VYMN_$bKoF`r_sM0re{F(6<1TUrfzFjh0_d)w+}_c@GCX z=#B>pSDErlxV)T#*HitVrE``peT%s9<2h8Jrujf~(Fy20I<&1lm(C)giflf(= zj2u8H$$`nC5Dq=Xa0l1MppB3mgO7_CkRsza91(L!C7jZifjV3slf=p+5E21_S$)Yc zEtSwpP{=RnKp@C}V^Ub2OxCo#l<_2gl@({o*3}^~={TU!VTY*}pt{tM#oHuU=jY9=Yva z4~`Yov5khAXoFwZgTnwG75lE4aN@*AHNH4r>7SyDKrKm^!gPmL# zgfNrUh@cbFF&7Qa7cKf9e~d5X=&+jTB3b03vbWV)vWek}FFqJxlWZ~~qiW;p>NWL_ z+P$WBU)TRb)i!RLoGsooxfWn-l=1;HxmJEx)EdYhgE7quBp4Z=%_B*Q?wBbB%eiy3 zliA=#^6e*#{qx0yJo`Gd%I)GxGQj4#w*u-$bV~S}}N%j0c<<$&}j$HCA=I z3M|9oo?6C@DX=V(9LB?qtTCTLh91)UDCVV$q)C~G7^m_k0+Zr87n?Gd7wLiw$!{sT z*MpZxR?c<`>5NuE6n`{od=_S`Jk>x})e{*=ZuzRTrN;@U5W?+;9Ck+jEPzU_GF7g9 zitX)dZMx(xU5!IOcQFMr+6t>A<)AW|m!W+_5i zEBP+Zktphi$RmXhWg12=K^t;0qB?SgE>$9ktJ(-byV2*0@{2a@M#tZE{#ECC+hCz} z&+37H>bdRxk@`b*ecyA1z2k-8L_wX9tXqyf!VqJx55S}DF6=Bc8HiF2 zYIH7>SgyQXajG}dYDq+{D5P3&-|*Vyg1T+vqKFi{sA*nQIhkyd)FLgB6oe$qRz3Cx z(8B$=7u)dUkD^IYFp)h?s>Bbjs>4o6If_jkA2BGSuNl6EW+>?``faD{(!xBR%RyxLH`ndKvN?%1u$*Y_* zw$jNLG@M0_`ngOdWq+nIR~k9ZtypFGrP8px)F0FSfM)1KVZ$^fA~vLR1^KFiVy6-! zOCurfMPENs%avd*k1Wh{$o3ebl#kDZykfG(SYEM!+LgN9tPFSa+J4)(yQJNBqa(+L zcwxMVf22hex54m+7Q9Sv*@n2^E$Ua@*76eeio;nRAUd>zxxos+7v$3i6kKC8N_t-= zj99^FbUrD}9Mrgx&fqf+$B9fFZr%u{uv9x&R_wP|I5CTC zkN6WRbSlnMVNyW>3GsOafi)SL?ZNAI{F>Wcit=UU19!Jiy?@N5>8%f=Kl$DgI$Wh`GMQ*>wMq=bf2L6 zYVEp?2X4Qwk=k|KC+J~Io$n>r!#36Tyz60y-*?9Ku*K~QKUCemVJfy$(L)95T73H+ z_N#d92%zY!N3pp9MVHsN_hDlPprIyT&j&pi{Q8+&{crDI^PPI&_V|vuXu=m=_ZfZ| zaQk{6sI=JuU*Jyc!3?%(*`zC#jsgt5s`Ke5Yvo^sKES!h6HcTcG(yM!F+VU=F3hyK;hs7LixGs*{)Eos@zvL zwjtfDI@5y5v;vyVVpgkdqqI7p9&H%ytd`!%&SW%dZC3M7)l`pQo_f@+-PvvBKc~&8 zqyB4u=ic{Ty((8hlu<{$4{?3pefRs^d(QdYb8aoISmBfKWH%pr>%SkCr2kDX=2Iym zj-@|wNYV#VNMcgR5poVV`kl-vQ`|M+Vs8G|(_f(>t-q3ajW1QK3SZmGdp4b=Wy4z}4S--z<7%9h_^llu0!zN0JFG&88yhB-7;Nw94f4get2u zF)bZSjLVZsWFnS8*`UvNwC`Md=k5b?Ymz{i6ooU z+IUskWHOnakhQm`6o#3gFKu!{9apGHpPERsWNNle9(YBLB7u#oiHO?9$4OIkYHAdd z6;EnfP(DAQ&dR7nj;iq(lF?9nR^FLNrgviOQB_U(B5_4Sw_^CnA``Q6d%LV+Zsi!w zP8xG8_wXz|Bj3?dN<@t$nHtQ~D|o^p-H1<{8Wq(=)iDq!DvzRd+aRyTG)yRlGZj}r zx8p=D+XTxKD!Ph>C)G5IMcS3C=sF*SJgvm1_$PaHSPdzrY8n;#|_R;XinG%g* zj%22$rjjKuO$E@5c)>mhshnG7( z6^X~9s*_`Bug;8jUYfYp0}{lA;Bl zQeupDlOpFwWliAK7#5*|<(FvBe-)$&`aUQ26L9F&N&>41D-+vwFyPc(M-#KUJECam z&%tsfq*V0r?(4q%$WU%|7#kcTNr(BW3afE-5>)%#DGD`&<0|Nw7!Qvs>BvM_i_NL} z+AtPGB0N2*q{3s^wxdeqN|@#n_0>iu)W{Vz8YW67qiDCXHrNgSIW*HI&mO@F02Rf)rv1~R$-JxWW9W|@OMr(Fdy0b7$yX=T1YT4PGZECQL2G=) zL@|}dGp=Hu(@gj9yG@k?x1-m)*MXDvU=3YgH6=NW&}5+f39h`eSH4#gY6&h zdblfBv+u_7f2^*%UH{&}jC=JGa!3&h%*T?$qe%Hcx+F0NWU({kV6Kppxi5Jj16}y% zKHv=1gghjfEcsSpeXR&pgeniXLn}hwf*h47Q^lpJH{@eg6o<1hW-;+~ilqjFPEuPsKu z05PQGXc}C|VtvHu!LnxyIU{i*E>H7=$um|yBV~D^&)18U&6Id-PSjG&5%U7DM~l<3hFeqPV{8?2Ycx1nW^1UVoA#i-z9bI{(l_F6+Aah)q-PaUUE{y)op1c%hI<=w z{_Rivd$RsLkNTc;pU!rl{%mWm`<0yk+Zpe-*;?d3A8<3`z-$Ww-3MB50jWmyYNCem zQa}$ca)RSImi|2=#S-o!zKEwZO)$d-)l8`;OUPMamdgJvNgwGP2WR!a+DcW z)4en^6bn?a8hm5b6mSx|k||AhlSaX|^HOXZ{^-sb%}!6YlM*+SCMR#R2N|?)A>j0^ z+PcuVaP{Y%y`T2|kAc4#$ea!3I?w;yJM`4w@czuLnfKqh_0IhHAHII?^+%4!M{mB9 z^&ff6a{gl(?=eo}0_s`2sSmZ=sb{C0x%x}(s#@qVdz@wt)d3ylplyFVSH6G;6ibM? zCOMh}cYxMsh)cP6Ujzd#yN5Z7a#Q<7V4O#b5F52FleX#WuqkvD) zD~Nnw`oih=c)w_pDjMg9AFa)LdX}6{&!A(;B~=_;Y>inc+)W&znn5m#3HK zxa?Tu@!$H~9x)ZX`Uq;a9$IL4982>EFPG5LLbW0PkkpMc6X;rAesSg<6^M%?+K+iLZq^n-?iUXR8YN)=SKHsXA1>Ub3{lnozxQLb4*Xg4GU5haJoh zCb3dq6B4-im99Y`$vmj|{3QCVM8MB{r*3+c zUC`F(omBRjBjI5ZdA{=#P+SbKXh3pQ7X^rpMP!niJW9x2tmz*Zmu~k!5Uk_cD2IAQ)-jQ_%(`zKHgZwztz$tBC0bE9sAydKZ@suD*x4rIaO%xKAmH2G z72L~vfF7AS#<)`QoI+MbRL`3MUj*CCdfbTN-LbR{ks}TWDeKdD*O2rkI7=QR%hI^? z&@l+z$)inSGmtolC1UAt7_6Bp&|M&q?!o}{s;~g;V4Bnh*31pTAdgc%xy*zF%?ihm zYpD*wmp7zecvsx2%zD?(H~fRQNw8W{03PD}6MqUMuB9(gc^n$kDF9-{&pG=P7#}Yh zOjIhPBMsM^T9he|+fjm_8w9L#gF;3`3r)dc7F2Wr8H2wTjs5Cd5LxYr>u&lgP&$oi znOKEhM8{|Prg9U;KgUp1Ye8^BT7*j4aQpn-;XA{3-@NnYj}AUae|+uXwZ}(u%|~;q zj%D+z3Zo-|g4rW|oG%7a5kG*2$2D@cQ1g4@Vx$2q|=k)^gKPtRZQr)I6^7HxxjOU0MIG2S;=MV8$C1^JSfTp%CUu z%oph;<#zz~g*AW3XguA{Zs#`Xq4Rg0C*pL5lz=3Lu*)9~@MCmw9*1>34(UW9HJK1= z;GvUi=Ij)z!|vRMKN|JTKvp&U7;j9}tZyqf74}?LQF@8AsTsrbXt|d^%F$Oz8d3z{0NWmcrj#I#a#2Jchr=W>~O9=F8 zew-~%yctO=M%<9W3W~!ELv}{gcpS3T!A>KU@1&Q74COOT0gX?ivzjk~GTa+H-rx2( zJk4K76jX3Xk@f5#v>~uW3!#l(hlMr_C{k}Bk@{y|r>EmL2zD(3lGJ|z=pobBdIBNx zZ+(PYej`!Hmm#Nl1npZ7Z9Y7Xr8$Hpp-4?=#dDxYZ7~!9bg48Bhyg=b)g>Q+4-0Hm zhx`OWV2-T`RWsBKuu%)HUZ>X;E6bmMA6-%|W2gA?S{oK|YGj1##fTcGzXj@XimS&d zsK=c9A-{YCkb~=?Ute2JL09HVCD({atB10T1Kxx3PCc7{Ju90jO-|Ihd{>`Q@#LD zFc8kEEJ-F!(lWS35w(y#@&o`05s=f5S8e6s$WQc|gEwjycs~(USkzusn@p9Thp-@6=5PPTSsp=a1i+Kl{YLCF|d^uzNxKaQ`n> zZp>^M{Os+|BAK)2GMhrVmFF`x=bzTE{6U4_^Q77`QZEVsgGF3AcY>x>5^N$B4 zB9#+|ECsqFse95en#WC4D|A~%B3@s;j4T|T{U>}g4w!+?R@A?L>K5qmw66ZH=Z@#L za#y{h&L92LiG^(+?YO@qySg=37r5zp>TjHHm{0$3`rh=tjamPWjCaR!#Sh~Xir-48 z+0U8(lFB}2?*$Y{xRHajr=n-L*G>gcz-7*M!h;fo=!%{pPsrKxF1Dm6zY#Cec|Lza z%M2BX3eaTl$>ekfl zvvhe%US=?jQ6Nmm39c~MxUT$IhF(*DR&4{8k``N z$`?J|E&`@(7|8${oEhCfT(C(xva;b!pc-0F_;Sc&9uuLzBgn$>2&`8Tibp9>DJUF+MjVVU z0b>n};a%TSdJGm&{24N71adi0T)*j7?ahkYdpJ~_kK|xWF^(_p6`Cd$RJL zM<;Xgp~um`9{cIor{g*QV8%PhDHm{vmIw|8yf!#^0}0~L<`l)=rhu>>BQV8E!@!j- z#q?--=>N+2&w_r8&UT6*A4^l(6aqWU<8wyA_vyoH2sq5!1ekZk<7xOpqM$Yp^W2^m z!fHTyh&`+Ed5$@Lv)1X^V?nuQ`pltRGeNmmIh1?VfO6DO>6JojwQ!|iPupl5a4P4( z0PLu0w~45Pz7MoS-id}+;#Ih?;rQ5qQ^k`ibkBb!y!s|_l29%zTZFH8Kpf$n7h=Xh z9^}=h!26BP6ipw^;$pC439ZDzH&4JRmM^VW`=$ZT-CbxDVt0`xL)pl&3|8%JQq9Ks z-i3yRNUnNk#%&y#;hXiiMeGj%wXYjAoMn#-Us;=x@c*Eb$|p`NG&7If8iG?Uyc`2C z>T%avh;jqN3kw&4(s*)Q_;Yf@Al!8oc$bnp6?bklt$C@W79%HJ2z@v*!3h-*7ln@y z#wCQQ%3PTvUto?geVi$AQysE)@>V)QUSyZGBrjkfSVHhztFQw`m1={#1e|zbMU2BI z7zX7+L^?8J^c)67+CV_4N#wLV-N>cc)=7-+>mNsall$cl7#x>0-kGay*^2xM<~t6N z5pZzaN#NL!jC{k>JqkV8rAIJ_J900^)68FHK0 zkT5~XmR-dynZ{OK_(@W|+sroZN>ie^X z2ayj4bN)R-$>-4E?u9!S=7%5b{&?@hy^k7ya`66lKXHE&`n35kYjdkk=KQBJ-cv#s z2LH7lqBmc^M-VBuMoMCT%K!%yhq4@{pL#x4D`tnSrpIp)A2i?^$x0=v-U z2l4vO@-tctwyJ4z*P#{y48jxa`mO8pN8kJILPOTSE#uuLW(V1<$C{meNcwuFIF5MO z8z|38qMRM&M_`9Le%kPtsTSJsrCAW+2o7gJ;N`}$Jd|Oh$XL8UIb3OdL1!%7(m>sr zu)1SkQP{P1?3i~zEsYjmaDrr=E0{RM#qLqFoa1pEQW}ChkB1sqWF0LCf`EiYT7eoj zJlyaka3~u%^!P$9aQxG&IsdB}@2i5t(gF~yJ4Lbm2$ox!HnfzTzZUiZl`F;vd@K}@ zW~5y}!12Kv;DfGGW+0E}Gfnhh*e*zA^VT@ zeFdD);EV=)P;ID&!RZb5pgORZmHO&|SNg0wS9!HEwi%REeumBJ8<-LoQ`*~KQDZPH zFxw?9Xj-KdOEPi5s*#4F0~#7^qI4)cqeguuKS#N=f_q+Rd`6VR8o~n0l~6;K6B=Zz z;WJ;LSK{WIaUn<{`(-QwpW1vwq8Vz$R9l1c1-b`h>j93gq4=Vkq|d^`tR41GO*Ss@ zz;_-hCawoeMJRCwcL1PmBD0B5%C1lNpOKPFm74Yx*n}YHY|~>Xv1av31g=M4Fr%*VlikZcY97MQ_n2^-w9LW|jY#Mm*t2nip z7sw9CR~d|P8tN~-idscqu?P8pIF>AKMwaTh7Swn3LsHKL#jS>fFpr3;1T zq*=be5x9uW;e*d2p_R;(9C$$@}{+W9G0Vf2o9K-5LS8#1PhH%#GtUT9ZP?Y zNEzc(v#Adj7@x|WYZm*M+7^<9uy4SQ(+O1BE6lpHy;L&#&NxUik}M-%;t--ns;V{I_{bc!ru{+m<0}uZJl>aEbL^)3 zPimf4`=3<-e#=Al2053pU zgvrWj(6FGr5M`AW0i!G> z8*MO*kvf9yz}0^a!8k!lwG>Cb-=i4ef@L_2Eg)6*aHqCn9haN<ke1x10oI=7f26G9CdUKhf+R@wv8m5fgz@l}HcWds{-25(_ zA~)RM0G2xOa3Uk`&-nLey!*u{X=LVO!K)^u6rw%ymY~mYku*vWC}#?i#0dunF|@ji zHwFiP+n?8Q3;7YA(`X9#4#~SVzBGUI&cTI>oWCvOZCk#!NoJx`jwNco&=OI~jkPp6 zN?Ee=_GG&~!?aob1g+Vqc7r znd?52IrnKQ$cPSYbVRtMGkx~8o-x28o__-q4~x02VgGo^PddKYy7b> zOsH}Ko5dGWQ@G1Rzk`zhSrOou0u(zD#^vg?LL3_i#ITQm$neKA@1(=9fgc$GFw9?| zN~pL@PYB0m;GjEnNHrYxaAO=l2LlXfHo)joJ^9YF%UzJ*LLOXa*X2WpVb4GpkO&A# zo`m!!x6-$E@u`DuILY`~6_wGikeD~6_`PoKcLA7g_XosfD8OEyi9|7GhVn+w@}nUC zb-I5I;A*zY-TxT1W*dICfvp^Gr#QsR?bTCvd@OA{mdC^ZF!f5-`m- zh{pbZGtLyPe{MVucD~Scu`{^Wa$1z-X1O!CyK67BmDW92b@&+Ybp`iz?Jb0pmh}8F zHh&p7`TO)`?D<;0j9eX_+g&tKK+wf*jAE(6;3+&A8QnuDK$X{#z}}(YI~05uf$rg@ zOhuYYr=QVBuHu|V%+eO@Myj<%y|EzUyCe?=Q0&WM|HZmhx79zbx#=RqVZt-qjT9H9aPKwz<-TszkD{lvC*5#w&rT?5ubE1ksiSiX%H|^U zWO84Fy|Y0y0a9^b#HsWy61Z845l#ZJw^70>3jPRz?uhCRMX$1$rd}cV{Box?j*eCp zla<4w_lTyKP+cJ`0=)0P)nDwY*0^SV>z$KuM;o|V1^?6e<^{*SmOEFn^-VV`3K7!T zd#^3Lo?Ep)=YJ{VeQCMUNMM(fb^(#!2Wdf|5@a(7)KHpyd4-dffJsDtF!Al@LaQDK zDY}H#YSAJ9@mnal_$eYzB4hCY{Uk;anK=^vW6XuQSuHZN&ydRgCjx7;+FcQa{*c!( zg2lM&10wLtC|=}=Sc1HU5%TRj)AMgHC?Ad8AA4~2!`S`yoWC>U?G!{rZPw#Pj1%$q z>PyJ?LU~ZOv3ObA(b#{W!OQ4op)29~`|BT^|9JS}aHe5j#=kG)-6sZz!B`JgfEe6n zL^vA5*RSbHXbqvXE8*snL$Gk!0u5pVdcn8o=B-)pdbkp5+?8;lIakL0*y{gRgJmx; z)|ESQHD=5-;knu0qMtSs9v^vWShifu7=5G6p_AQVK|` z*M<<`Fljvud@p%C8^{50gWUnJ(sO`t4D5S|JzMGV?EVdGeEThw!`%Zp%4s2#bB?2& za|X(xb^tXSX()U=oImi`YAY=dFZo0i->}O&QqHYk<}G=#k2_qw*%D~tH{|G-ImV{o zi$%XD;zAR;mu_R?2M;v8=H=9E*m_$>zX7LeX+sG{@XNErW#-QPtPEWI6>!^ z`t(Zr2|N6Xfw(6~zUtx{HyuEWtB_YIc9#Nfs3jCB)-FFuAxk{>_tw3K#P5?FVgCzh zptPhlAzXAh91i?+h{OFavgBy~wIsbOeXshL(#Bs)+y6=0^-JmaKS|quDRn%na5>gI ztJ&)C&G#=%XEz3)Nr*3=a!AgV&zuJxgO1GRJ&#UgHy>P*=nX5xxpI+zSiCMWop3l_ zb!67GJ$NmYe=WuRL4+#e?+4oxHidjx&yo zy!X*qRzAFh1H?0Ca}-@niRStoj*E`Wrmjc(vYUFAB>rkN$K#8GPF~qjhvS?hvwGKq U6WP^!mL&el^L^pualSGC9~AcWD*ylh literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_battery.cpython-313.pyc b/spacecore/functional/__pycache__/_battery.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9526f3dd1117f65498d3b24c8ee2b41533033a8 GIT binary patch literal 22443 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 T@xup89fciSHtYrc{tEjav^Dv1 literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_composed.cpython-311.pyc b/spacecore/functional/__pycache__/_composed.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b353a8a7798b0e41442a0781bc8de655b3026c84 GIT binary patch literal 6748 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 JZ5&0V`(GVPA(Q|B literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_composed.cpython-312.pyc b/spacecore/functional/__pycache__/_composed.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8406775882b93198ffea8f577abf971281f1275 GIT binary patch literal 5731 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 GH2(n^+7em- literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_composed.cpython-313.pyc b/spacecore/functional/__pycache__/_composed.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4822cfda0eefb6f49ab23cbaf7ccc838df59b5ac GIT binary patch literal 9074 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 JcMI99{|!Pyj3od7 literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_least_squares.cpython-313-pytest-9.0.2.pyc b/spacecore/functional/__pycache__/_least_squares.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c768a319c4ff0d006bdf934d9d08df64c55aa00 GIT binary patch literal 5567 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 Uf$kj+w-XRN2mT#otDKAf0#`E1+yDRo literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_least_squares.cpython-313.pyc b/spacecore/functional/__pycache__/_least_squares.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88373657390fd26ae80aed6412e2dd639d5833ce GIT binary patch literal 5453 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 E7n+r!#sB~S literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_linear.cpython-310.pyc b/spacecore/functional/__pycache__/_linear.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e664fe5d7286fc2f6934bf0719253c7ef4537784 GIT binary patch literal 11937 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 LkK4{KPTBtl)_coT literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_linear.cpython-311.pyc b/spacecore/functional/__pycache__/_linear.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab99e9f6590471f42eeddb107ae708fbb32a49e6 GIT binary patch literal 16792 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 WzPYimZudOIC+}yq?l>sRX8OOgcf*qa literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_linear.cpython-313.pyc b/spacecore/functional/__pycache__/_linear.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6da570fd0c3f2e6dd874651a8cf9aa1d7895245d GIT binary patch literal 15645 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�?fYWLxMXE75)Twb1L6H_rdv#ZNAN`s!z+UmW_f z`!|Pw{ZjM%h2?mz8O+^_M96#u646okDNcCC@V)UGYd3GFaa~CBLf&}ySY+eVlGGN` zkT1>#vcV%^1htUz9~9IaQir8-&(m>;X`lSILl1}M4!0$1MFPX&wH~7t$L_ZNw{S{( zyd?fiC+x*m0DW@~p~9w3Z$V$#OGL*vcy$r>+JwXuhN)F58|9(o1WsCPPOcfUm$<<> zE*Z#!NH3ew6fqA{y>vM5+4i4iLXOn`rvc8HvlZE39wGtZZj`VHVF(uh20ez;3UGqq zq=Fj)948Kw^4sA}M7DN_m~f`e+nU3lN9V8bF_7Ve1+66|5RUZ}WMf<#ZNA)YEvc|g z!Gah-KvFA!RFxIB&$a|%b6J|`Lg_t@ z`*9KI$#QJECrXct$0;4aX(W60upQ+^2XUAn<`nHzmx|hJC59JxK$K|d?1|b(5}@I} z?c1zKK3}R8aUv(@@n;7}GvRcOlXtM9lMStccx6}}&($yIy^~I>usT(CJ zGF+uUPEoCxS7k-qIvEdB_dNJBaRVz1!i*?ri%Nb5Co*(ACrv&*t1cdf|V1+S=%CoW8Zd*`nx~$2Yp-bCbnbryH7prd&T$da3AjC1^yF!=em3V literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_proximal.cpython-313.pyc b/spacecore/functional/__pycache__/_proximal.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32c4b3462ae9402da99ccb5a59dd5af92b7ffb48 GIT binary patch literal 9885 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 literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_quadratic.cpython-310.pyc b/spacecore/functional/__pycache__/_quadratic.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30d5014f5a75bc7ef9c53b69a8320bbb4b675875 GIT binary patch literal 7883 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 b%|$ef#d!SU&=vb3I0(#&v7?7 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�Z$mS?n6rjHv{Hw&_!mkpG)Q%Ks#*9k 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 Pz`wv=Y3CD*!q@X(U`gVG literal 0 HcmV?d00001 diff --git a/spacecore/functional/__pycache__/_realified.cpython-313.pyc b/spacecore/functional/__pycache__/_realified.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2163ac72050d637d1b27e25ddd469234a26cbcf GIT binary patch literal 12724 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 Any: """Return ``scalar * functional.value(x)``.""" return self._value_core(x, *args, **kwargs) @@ -171,7 +173,7 @@ def make_scaled_functional(scalar: Any, functional: Functional) -> Functional: unwrap_scaled=lambda f: ( (f.scalar, f.functional) if isinstance(f, ScaledFunctional) else None ), - make_zero=lambda: ZeroFunctional(functional.domain, functional.ctx), + make_zero=lambda: ZeroFunctional(functional.domain, functional.ctx, cod=functional.codomain), make_scaled_node=ScaledFunctional, ) @@ -202,7 +204,8 @@ def __init__(self, terms: Any, check_level: CheckLevel | bool | None = None) -> _require_same_domain(parts) if check_level is None: check_level = minimum_check_level(tuple(term.check_level for term in parts)) - super().__init__(parts[0].domain, parts[0].ctx, check_level=check_level) + super().__init__(parts[0].domain, parts[0].ctx, check_level=check_level, + cod=parts[0].codomain) self.terms = tuple(term.convert(self.ctx) for term in parts) @property @@ -210,7 +213,7 @@ def parts(self) -> tuple[Functional, ...]: """Return the summed terms in order.""" return self.terms - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any, *args: Any, **kwargs: Any) -> Any: """Return the sum of the term values at ``x``.""" return self._value_core(x, *args, **kwargs) @@ -302,7 +305,7 @@ def make_functional_sum(terms: Any) -> Functional: return finalize_sum( flat, is_zero=lambda f: isinstance(f, ZeroFunctional), - make_zero=lambda: ZeroFunctional(flat[0].domain, flat[0].ctx), + make_zero=lambda: ZeroFunctional(flat[0].domain, flat[0].ctx, cod=flat[0].codomain), make_sum_node=SumFunctional, ) @@ -330,17 +333,19 @@ def __init__( dom: Any, ctx: Context | str | None = None, check_level: CheckLevel | bool | None = None, + *, + cod: Field | None = None, ) -> None: - super().__init__(dom, ctx, check_level=check_level) + super().__init__(dom, ctx, check_level=check_level, cod=cod) - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any, *args: Any, **kwargs: Any) -> Any: """Return the scalar zero.""" return self._value_core(x, *args, **kwargs) def _value_core(self, x: Any, *args: Any, **kwargs: Any) -> Any: - """Check-free scalar zero in the domain dtype.""" - return self.ctx.asarray(0.0) + """Check-free scalar zero in the codomain dtype.""" + return self.codomain.ctx.asarray(0.0) def grad(self, x: Any, *args: Any, **kwargs: Any) -> Any: """Return the domain's zero element.""" @@ -354,21 +359,22 @@ def __eq__(self, other: Any) -> bool: """Return whether another zero functional has the same domain.""" if not self.same_math(other): return NotImplemented - return self.domain == other.domain + return self.domain == other.domain and self.codomain == other.codomain def tree_flatten(self): """Flatten this functional for pytree registration.""" - return (), (self.domain, self.ctx) + return (), (self.domain, self.ctx, self.codomain, self.check_level) @classmethod def tree_unflatten(cls, aux, children): """Rebuild this functional from pytree data.""" - dom, ctx = aux - return cls(dom, ctx) + dom, ctx, cod, check_level = aux + return cls(dom, ctx, check_level=check_level, cod=cod) def _convert(self, new_ctx: Context) -> "ZeroFunctional": """Convert the zero functional to ``new_ctx``.""" - return ZeroFunctional(self.domain.convert(new_ctx), new_ctx) + return ZeroFunctional(self.domain.convert(new_ctx), new_ctx, + check_level=self.check_level, cod=self.codomain) class ConstantFunctional(Functional): @@ -407,20 +413,22 @@ def __init__( constant: Any, ctx: Context | str | None = None, check_level: CheckLevel | bool | None = None, + *, + cod: Field | None = None, ) -> None: if not is_scalar_like(constant): raise TypeError(f"constant must be scalar-like, got {type(constant).__name__}.") - super().__init__(dom, ctx, check_level=check_level) + super().__init__(dom, ctx, check_level=check_level, cod=cod) self.constant = constant - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any, *args: Any, **kwargs: Any) -> Any: """Return the stored constant.""" return self._value_core(x, *args, **kwargs) def _value_core(self, x: Any, *args: Any, **kwargs: Any) -> Any: - """Check-free constant in the domain dtype (mirrors ``ZeroFunctional``).""" - return self.ctx.asarray(self.constant) + """Check-free constant in the codomain dtype (mirrors ``ZeroFunctional``).""" + return self.codomain.ctx.asarray(self.constant) def grad(self, x: Any, *args: Any, **kwargs: Any) -> Any: """Return the domain's zero element (a constant has zero derivative).""" @@ -434,22 +442,23 @@ def __eq__(self, other: Any) -> bool: """Return whether another constant functional has the same domain and value.""" if not self.same_math(other): return NotImplemented - return self.domain == other.domain and scalar_eq(self.constant, other.constant) + return self.domain == other.domain and self.codomain == other.codomain and scalar_eq(self.constant, other.constant) def tree_flatten(self): """Flatten this functional for pytree registration (constant is a traced child).""" - return (self.constant,), (self.domain, self.ctx) + return (self.constant,), (self.domain, self.ctx, self.codomain, self.check_level) @classmethod def tree_unflatten(cls, aux, children): """Rebuild this functional from pytree data.""" - dom, ctx = aux + dom, ctx, cod, check_level = aux (constant,) = children - return cls(dom, constant, ctx) + return cls(dom, constant, ctx, check_level=check_level, cod=cod) def _convert(self, new_ctx: Context) -> "ConstantFunctional": """Convert the constant functional to ``new_ctx``.""" - return ConstantFunctional(self.domain.convert(new_ctx), self.constant, new_ctx) + return ConstantFunctional(self.domain.convert(new_ctx), self.constant, new_ctx, + check_level=self.check_level, cod=self.codomain) def make_constant_functional( @@ -514,11 +523,12 @@ def __init__( raise TypeError(f"offset must be scalar-like, got {type(offset).__name__}.") if check_level is None: check_level = functional.check_level - super().__init__(functional.domain, functional.ctx, check_level=check_level) + super().__init__(functional.domain, functional.ctx, check_level=check_level, + cod=functional.codomain) self.functional = functional.convert(self.ctx) self.offset = offset - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any, *args: Any, **kwargs: Any) -> Any: """Return ``functional.value(x) + offset``.""" return self._value_core(x, *args, **kwargs) @@ -648,7 +658,7 @@ def __init__( # (mirrors SumFunctional). if check_level is None: check_level = minimum_check_level((left.check_level, right.check_level)) - super().__init__(left.domain, left.ctx, check_level=check_level) + super().__init__(left.domain, left.ctx, check_level=check_level, cod=left.codomain) self.left = left.convert(self.ctx) self.right = right.convert(self.ctx) @@ -657,7 +667,7 @@ def factors(self) -> tuple[Functional, Functional]: """Return the two factors in order.""" return (self.left, self.right) - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any, *args: Any, **kwargs: Any) -> Any: """Return ``left.value(x) * right.value(x)``.""" return self._value_core(x, *args, **kwargs) @@ -754,7 +764,7 @@ def make_functional_product(left: Functional, right: Functional) -> Functional: _require_same_domain((left, right), node="ProductFunctional") if isinstance(left, ZeroFunctional) or isinstance(right, ZeroFunctional): - return ZeroFunctional(left.domain, left.ctx) + return ZeroFunctional(left.domain, left.ctx, cod=left.codomain) if isinstance(left, ConstantFunctional): return make_scaled_functional(left.constant, right) if isinstance(right, ConstantFunctional): diff --git a/spacecore/functional/_base.py b/spacecore/functional/_base.py index 83995d0..9c96786 100644 --- a/spacecore/functional/_base.py +++ b/spacecore/functional/_base.py @@ -15,11 +15,11 @@ ) from .._checks import checked_method from ..backend import PyTreeNode -from .._repr import describe_space, field_symbol +from .._repr import describe_space from ..contextual import ContextBound -from ..contextual import Context +from ..contextual import Context, resolve_context_priority from .._check_policy import CheckLevel -from ..space import CoordinateSpace +from ..space import CoordinateSpace, Field if TYPE_CHECKING: from ..linop import LinOp @@ -48,6 +48,10 @@ class Functional(PyTreeNode, ContextBound, Generic[Domain]): default (see :func:`spacecore.get_check_level`) is used. Validation policy is a property of the functional, not of the ``Context``. + cod : Field or None, optional + Scalar codomain, defaulting to the converted domain's ``scalars``. + Its real or complex field is independent of the domain storage dtype. + Attributes ---------- dom : Space @@ -61,8 +65,24 @@ def __init__( dom: Domain, ctx: Context | str | None = None, check_level: CheckLevel | bool | None = None, + *, + cod: Field | None = None, ) -> None: - (self.dom,) = self._bind_context(ctx, dom, check_level=check_level) + resolved = resolve_context_priority(ctx, dom) + dom = dom.convert(resolved) + if cod is None: + cod = dom.scalars + if not isinstance(cod, Field): + raise TypeError("Functional codomain must be a Field.") + self.dom, self.cod = self._bind_context(resolved, dom, cod, check_level=check_level) + # Output checks follow the functional's policy, even when its domain + # or explicitly supplied field uses a different validation level. + self.cod = self.cod._with_check_level(self.check_level) + + @property + def codomain(self) -> Field: + """The independently bound scalar field of this functional's values.""" + return self.cod @property def domain(self) -> Domain: @@ -253,11 +273,7 @@ def __eq__(self, other: Any) -> bool: def _arrow(self) -> str: """Return the ``domain → scalar-field`` descriptor for this functional.""" - try: - codomain = field_symbol(self.dom.field) - except Exception: - codomain = "?" - return f"{describe_space(self.dom)} → {codomain}" + return f"{describe_space(self.dom)} → {describe_space(self.cod)}" def _repr_body(self) -> str: return self._arrow() diff --git a/spacecore/functional/_composed.py b/spacecore/functional/_composed.py index 3fb301c..d5a68fd 100644 --- a/spacecore/functional/_composed.py +++ b/spacecore/functional/_composed.py @@ -80,11 +80,11 @@ def __init__( _require_composable(F, A) if check_level is None: check_level = minimum_check_level((F.check_level, A.check_level)) - super().__init__(A.domain, A.ctx, check_level=check_level) + super().__init__(A.domain, A.ctx, check_level=check_level, cod=F.codomain) self.F = F.convert(A.ctx) self.A = A - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """ Evaluate ``F(A x)``. diff --git a/spacecore/functional/_linear.py b/spacecore/functional/_linear.py index 68ef2fb..9d16eb0 100644 --- a/spacecore/functional/_linear.py +++ b/spacecore/functional/_linear.py @@ -3,6 +3,7 @@ from abc import abstractmethod from typing import Any, Callable +from ..space import Field from ._base import Domain, Functional from .._batching import _check_scalar_shape from .._checks import checked_method @@ -118,7 +119,7 @@ def representer(self) -> Any: """Stored domain element ``c`` defining ``ell_c(x) = ``.""" return self._c - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """Return ``domain.inner(representer, x)``.""" return self._value_core(x) @@ -183,6 +184,10 @@ class MatrixFreeLinearFunctional(LinearFunctional[Domain]): Runtime validation policy for this object. When omitted, the ambient default (see :func:`spacecore.get_check_level`) is used. + cod : Field or None, optional + Scalar codomain, independent of the domain's coefficient field. + Defaults to ``dom.scalars``. + Returns ------- MatrixFreeLinearFunctional @@ -197,6 +202,8 @@ def __init__( ctx: Context | str | None = None, vvalue: Callable[[Any], Any] | None = None, check_level: CheckLevel | bool | None = None, + *, + cod: Field | None = None, ) -> None: """ Initialize a matrix-free linear functional. @@ -225,7 +232,7 @@ def __init__( raise TypeError(f"value must be callable, got {type(value).__name__}.") if vvalue is not None and not callable(vvalue): raise TypeError(f"vvalue must be callable, got {type(vvalue).__name__}.") - super().__init__(dom, ctx, check_level=check_level) + super().__init__(dom, ctx, check_level=check_level, cod=cod) self.value_fn = value self.vvalue_fn = vvalue @@ -245,7 +252,7 @@ def representer(self) -> Any: """ raise NotImplementedError(f"{type(self).__name__} does not store a Riesz representer.") - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """ Evaluate the scalar functional. @@ -298,7 +305,7 @@ def __eq__(self, other: Any) -> bool: """Return whether another matrix-free functional uses the same callables.""" if not self.same_math(other): # Tier 1: backend return NotImplemented - if self.domain != other.domain: # Tier 2: domain + if self.domain != other.domain or self.codomain != other.codomain: # Tier 2: endpoints return False # Callable identity: extensional equality of callables is undecidable. return self.value_fn is other.value_fn and self.vvalue_fn is other.vvalue_fn @@ -306,14 +313,14 @@ def __eq__(self, other: Any) -> bool: def tree_flatten(self): """Flatten this functional for pytree registration.""" children = () - aux = (self.value_fn, self.domain, self.ctx, self.vvalue_fn) + aux = (self.value_fn, self.domain, self.ctx, self.vvalue_fn, self.codomain, self.check_level) return children, aux @classmethod def tree_unflatten(cls, aux, children): """Rebuild this functional from pytree data.""" - value_fn, domain, ctx, vvalue_fn = aux - return cls(value_fn, domain, ctx, vvalue_fn) + value_fn, domain, ctx, vvalue_fn, cod, check_level = aux + return cls(value_fn, domain, ctx, vvalue_fn, check_level=check_level, cod=cod) def _convert(self, new_ctx: Context) -> MatrixFreeLinearFunctional: """ @@ -335,4 +342,6 @@ def _convert(self, new_ctx: Context) -> MatrixFreeLinearFunctional: self.domain.convert(new_ctx), new_ctx, self.vvalue_fn, + check_level=self.check_level, + cod=self.codomain, ) diff --git a/spacecore/functional/_quadratic.py b/spacecore/functional/_quadratic.py index 7696c40..37d0a56 100644 --- a/spacecore/functional/_quadratic.py +++ b/spacecore/functional/_quadratic.py @@ -127,7 +127,7 @@ def _check_hermitian_structure(Q: LinOp[Domain, Domain]) -> None: if result is False: raise ValueError("LinOpQuadraticForm requires Q to be Hermitian/self-adjoint.") - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """Return ``1/2 * + linear(x) + a``.""" return self._value_core(x) diff --git a/spacecore/functional/_realified.py b/spacecore/functional/_realified.py index 883f86c..77411b9 100644 --- a/spacecore/functional/_realified.py +++ b/spacecore/functional/_realified.py @@ -142,7 +142,7 @@ def __init__(self, base: Functional) -> None: dom = DenseCoordinateSpace( (2 * X.size,), real_ctx, check_level=X.check_level ) - super().__init__(dom, real_ctx, check_level=X.check_level) + super().__init__(dom, real_ctx, check_level=X.check_level, cod=base.codomain) self.base = base self.complex_space = X @@ -161,7 +161,7 @@ def from_real(self, w: Any) -> Any: # Output-only: the input is validated by ``base.value`` against the *complex* # domain after ``from_real``, so an ``in_space`` here would check ``w`` # against the wrong space. - @checked_method(out_scalar=True) + @checked_method(out_space="codomain") def value(self, w: Any, *args: Any, **kwargs: Any) -> Any: """Return ``base.value`` at the complex element ``w`` encodes.""" return self.base.value(self.from_real(w), *args, **kwargs) diff --git a/spacecore/functional/tools/__pycache__/__init__.cpython-313.pyc b/spacecore/functional/tools/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9945de02eed94bc6dfbb6d52ae33b8506dc39863 GIT binary patch literal 1160 zcmZ8g%We}f6!qlMBop$WP!K{OOKcd4bZA*2AtXdWXi|kj#M*Ud+{7@kr`MCTL2UR4 zK7j=v!ABr=mJkb8?0_!Vay@+pFOFaw=tN!U7WcLO#b_Cpqf2lpx(t`2662a)u4(T`3<4l?fXedIVxCj&W=E{2n_FY_^y zP$l*;8+?|}GeO5v0Ec)yU-f@tg-B4cSNfY9=mR66VsVz1y*3ad?xH<8l6YncJliSs zEnKyV#MyD;LD_aU*9Iu|>=E8%|J__27IjKC*B1>z#jcQoZ3LVcXa} zS1P8oQetFluA3}fvyG$8y4iEigK3OkLDG#t2(0ZR^=4lX!oIp{jLh%gAQ+8Setjv2FccHH};*e;@7 zVq@3$g3`9xLPd+v_(HkCIVZ(&+Xg!s>LVG83ebF#(mBzmvYaNKri^_ehZv057vU=- zz=Ro}r8*O-d@~L=bUdA)ViRtNSvYisdW>Tg7A1277Ve&YiO_v46EY^1%o$tF=C=M| zqLXPV9>NV|aHD7LA^a+Pp7*;{^(uec#Czs_Be%Yh;5&K#g*^XGw!V_Bzi2ES5`;hP MZnN~h2G`N=AJ9==LI3~& literal 0 HcmV?d00001 diff --git a/spacecore/functional/tools/__pycache__/_coordinate.cpython-313.pyc b/spacecore/functional/tools/__pycache__/_coordinate.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c3d90054b5a216eedc538f37949377c67bf1857 GIT binary patch literal 5597 zcmaJ_TWl298J^jj*R#gUEeQouoj?d)W3O#4B{r8}uxT&}YFWD_GB7*dJ>H!#p4pt4 zHC_iKKU7r-QXy(kO70=luWoUyoNeY|s?67ytabw||~il>g92y(NNyy80dlca)qWl$@H2oK?pnBEo1K zJsTa1iP%_N#PJ==#m^?j+C)5X3He5_zY>VUeL(za2v8mLV*;GA;xm?*0yvU{umovjI zvNE?ST)1r75v<@iqG;NN$9s!>+O$FEIgT~u%(3aJUGPlDHY_)*X~Qj~ElVt)KixH# zVaHgdY+{hlGt*^mg%`|elP;|>)5Ao;unfUw4XY}7&}g!Iv|6ytA~)>TvrED#n%wrf zadeSRWqqv3=5#Zk&$96{XEWRrW}&y>2*C>;J8yD#UaRk7g1=KWrMNXVIXrT{w|}7j zQWrYCDUV)oY)=>kFFRAr5WmipXGqx{yTE&xX=_6T%W&PHyndUR-Cx zv`fq@8{T@7u&bt5)*y{DgIltK7}iaD7CPW0)G0_S49lDk4&gXMiU#b7IS(?sSh6^S zin#4^%_!j@*ON=3kb@-Tpw4k>t!UKrP>WJRoL*thG^-eD8VnrT>2=m;50KCeM6gH1VAy9gVx z06mPOsUL<@^7#vS;OsiEA3~vVNnlh4>#o}7J5}BdU`wz%$$ogjWcd;d@@T1tm5=K< zMq0XI5d&p>eZKI4^aH6cxw{8q2WT2X7&T2|C5UGi*5I~FWFK3#29^NvJk$p)3f$Ft8%@h**`x&W5*yC4A7$A|_{tSwx@Ft2 zdsz2yrDBmQHSLcL!zpX7>2~+9{cvbr%=T!Ev0R1J%S8_x7<_f0hyC^U1KGo@>)W)H zk!bKa)Afik!i7!>u(i1h-DblqUQ8`v<`OczGtv4rLEzB}DLLa%C9;EOSa2q6Xjt zKOSl-Cv=z~NQAOySUo)S;5w3zTYLsaNy14(J3@Z~&xC?!jEYNUH)RyAAROYFdXPjU zd`$5*qxvbshS5lqcQX+`I&9bcSix|;#}Oik_=yqe@4nVV;c+xr+M-(=QO+*uQ-)V4 z>#jM^{cZYHL)iK(Zr7(_t#rFi0ST9N7RtPEg%@?mRd$L6I;Y+!>f>8oJ%Pa;Wm2s# zOd#=0DCdctHLac`?C3;vmvUkAp^)Zf@IC+fd1>E3M@NLz^mFHiV!Ih~35ae1Jx&iQIhYy^{T z3?1~t{PDgC7Xh-bV$51jg;z7am#C++7Hj_#AE&?SA%7}7@p(Cx$B zb=_qvHPMOVWw)Cy`!2c#<-Z+UZjUY=yT9j;`yT9D>GI{@Fv=zVYXF6^;z{$NIMbfO(Io?U(iydH>D^vCpB;^)Br{PCjU@KC7
    &Pcx1lK)zjjR$>e$<$9Gf_Y4R9rs_dS9)$ z#2#Q4Tk+RVS`}TUX7M6EGx&A)qFYe@o$9=G_{QPe2Yz|@{lkj~esT2P!19)tS5kZK z?|!I0*t?wCzZBaqo&)VyFoGO`MShOKH)7O416H^$UKjR%6H0arJILTs?I>|cSI+1AvA-YAja$jk9ATCa*i9P& zFEBh*D9GDX0BUPlmC0RjaMr>Pon;9Gwi*gO8~F>-ooY)Qf&@P zM+=O7GmO3IR65}-UItATzpjODL3y%KY45nzcBAcf^k&Dx@TZ{{47`8v=CQ@xy@9(w zUQX><7@_&W8-usU7l+@!u;~4E{13bDyT7@-k~$cyy?W!-+o!N&_+H`Hd+(lGPVKwz zJ&Hau{?xYIcVam;yc8P_dJ+B#C>Ir>;yDq?sUn(-h}gwA3jQd5VuO)f zM=nm!fJ5PCk!Y+Y=t(gc%WcRdze3T5J;8G#nbSmjDV6E;pIf)aeS8$B8lSq0#V@AGPMrCG2C^;Qon znzN=$Yi`Jli{K|Zh>EhQKjB z7?skZACsAg=))gBGUvAHQ&8mecdV~YdFPuTa9=|w_1XT?y{-4Ie%ycRqmjRz{ma>< zALds2$3IR^e45^JYwpI}t?M_gFOGls*4?)rs*i?$c6~X0{E=8mpIl0w4B~vlPjFzv zn?~@_s=Zdt)vbR$uj)i~e;`t);3_KjBQ5^1Uhr&&v9ATg?EEfPbP%8EAsobZw$?AQ zC?dC4A*rbQ)!Feh+fF(zp_5gYj4Sc*4={+m3ik& zpQX3l9=SRHNqX0Ede>6t?xo$MOC6_Iqmi`sM2WO(64(dIIk*pbYfu;@gnYrs4=fDU z7sPOVl+na-jD1blXYg{dz_s6@>+e(zE0DoY>-w}QT#x=aVLMoE)AgcLfOH*n2w9ah zpFpCk97M^bu@CC&wzdZeI+JHTLCxMcw^{vhxe&m47Lx|EcVGmWZk^JnM+42cBsu^`!c2=MGg{ Q9J@EWyfgcyg0VdP|AxZ(8UO$Q literal 0 HcmV?d00001 diff --git a/spacecore/functional/tools/__pycache__/_entropy.cpython-313.pyc b/spacecore/functional/tools/__pycache__/_entropy.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f36f64d9015f9c8d5d7a8661937e11f11fab035f GIT binary patch literal 9959 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 VljmOB^=R+>YX^QKiC3}Ue*-SDAaDQx literal 0 HcmV?d00001 diff --git a/spacecore/functional/tools/__pycache__/_huber.cpython-313.pyc b/spacecore/functional/tools/__pycache__/_huber.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26633375c6c0f68301593bcaa11ac04a9e7ea1ea GIT binary patch literal 5686 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�H5 z4~t`mF-TP8XT+o#TPm(tOWhLF7Ug0~1%8z`_*Zl^C47Ku4O_oc^Ua#;*KVcWz5LGQ zyFKq8zIS-}xt`^4&vKy0;tX-ad0Z{G7d7CF=;)+8W`j&0?#7MxLa*3;_==;Ws^w~J zS}}#s_L6axMUn3b(JD9>IP6{}e+7QcEIc$^At|k$v%(~)<~qP$em!4ZUUwL<`)Let zoBR|MOtKkV>u=wABXqs$VdKsl)z`QFBHZ})!P`wM;eE@2eVflVjm^*Y5(?#dzOpoo z62~??rITOzgnmXJaUFMi5V-?k%59P-I*5cZa&MXExO8-B(UQYvZCZ3ugXE-FBRKq>b$VfD3`JH4Kt#c5d5}kuTY#p$n znrnpXNY^;3!~N1f>=H-t|--TuH@RuuL{A9nVvg(QFTM|DkWetg3{ z3OBD+Qz;mK{Qk~@|JY-X$3Fm{Rj4|? z#*0q^KL4Qjq}JzuS^Raa$KMNzRa-vs`1~(Wt1W9(`W;>ee=XpH3k8?hB<+;*kWBES zNWwb(&>_@Wp)jK-;Ayo(x^ODg@GJBs|Jyr#a#jw*&|#XvSLsG#B>GmPX`OjL3c#} zKnW!$aGQg)laubNNQp&OP!g3o`pcsH$;qR9O=W(D4F*_nuao@1Eh$2Nt&5@Vg~n5E zx|hIbXuCfO`unHBcCizdv*p6L!Oi-_W}h1j$^2e6APX0F{<8ps9kkc9^S$;h-A{vg zEzGt;POcJS`?)}1z{ZvES@sO{2Miy1&s@tO3fY5lvBnizZg4EfgDlg=C?r(2ET*Hu zGBs+&$CB{LtD;c5?SpOmZYK-d23A6Y$n=MvyOq4#R0y3~_Sx>ItDVrt)oTRVtamZu z;!a__G4A2o%bPz=+H1tlg+?etrACod!j))TT+e3`nsm)dLnk!A?$?lbq)V5iF`TSw zM;C7oQ5;sYNVZdEm&{a(QfHQ8BfX*~l|1q|)YD15@?qe@^5j^OIs?cehr^iHgSbFG zo3pMag3cVj<%DZJ$ZR{Ab@zHIzl=0%Fp!6$O&jwaT$|x?Ed61_0Xpx69U|ro z>D&;vGIsV~5FqzjwlfigD4%sPyi4x1u7;EQRCe=OGlPgi>Bco!V<4M{{M&-7eabRm zo6^}|NZ(qTo8Qq_N+FmNNb?-)A5Y6USEBx(a$NjosU>ohTXPlp=U0>G*9$gXN40aR z<>tGK|Iq-b{y8MkrmZBIea6>cKX5W>R`P1v&Rwn7gxyvOtdtJxIDp*P#VAx^uiP0- zmWRFMTOs8mKCYy9%Ag|8$P8NIS^E;BSTW?Js zjub;>2pS6$xvB&i0FzO^uw*&;HgS%#^YntF(rwf}aLkH^`q^9fNn&UI5FfZQCyJkV z_K3dUoE8Of&##1CzY<>fP}u#U(8mAY_n{zvDD3@h)o$^W_~f)WFN(qCX8G=1q50qw zfqtzmdWGHl9=3O`)kMXY#fLTFH7^xD3OBC#ser&G`21QmF9w9hnAHxUOI7P{LcI2Z iXj7<;h*g{bzCWsYZq1KxbVnb0Ls3fWZ);HE3;SO None: super().__init__(dom, ctx, check_level=check_level) - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """Return ``sum_i x_i log x_i`` with ``0 log 0 = 0``.""" o = self.ops @@ -152,7 +152,7 @@ def target(self) -> Any: """Stored reference element ``t``.""" return self._target - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """Return ``sum_i x_i log(x_i / t_i)`` with ``0 log 0 = 0``.""" o = self.ops diff --git a/spacecore/functional/tools/_huber.py b/spacecore/functional/tools/_huber.py index 211e96b..25725de 100644 --- a/spacecore/functional/tools/_huber.py +++ b/spacecore/functional/tools/_huber.py @@ -74,7 +74,7 @@ def __init__( raise ValueError(f"HuberFunctional requires a finite delta > 0, got {delta}.") self.delta = delta - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """Return ``sum_i h_delta(x_i)``.""" o = self.ops diff --git a/spacecore/functional/tools/_norms.py b/spacecore/functional/tools/_norms.py index 9fde0d0..ec7c29b 100644 --- a/spacecore/functional/tools/_norms.py +++ b/spacecore/functional/tools/_norms.py @@ -52,7 +52,7 @@ def __init__( ) -> None: super().__init__(dom, ctx, check_level=check_level) - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """Return ``1/2 _X`` as a real scalar.""" return 0.5 * self.ops.real(_inner_core(self.domain, x, x)) @@ -128,7 +128,7 @@ def __init__( raise ValueError(f"LpNormFunctional requires a finite p >= 1, got {p}.") self.p = p - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """Return ``(sum_i |x_i|^p)^{1/p}``.""" return lp_value(self.ops, x, self.p) diff --git a/spacecore/functional/tools/_spectral.py b/spacecore/functional/tools/_spectral.py index c148a2d..3fb574c 100644 --- a/spacecore/functional/tools/_spectral.py +++ b/spacecore/functional/tools/_spectral.py @@ -143,7 +143,7 @@ def __init__( ctx: Context | str | None = None, check_level: CheckLevel | bool | None = None, ) -> None: - super().__init__(dom, ctx, check_level=check_level) + super().__init__(dom, ctx, check_level=check_level, cod=base.codomain) domain = cast(Any, self.domain) if not isinstance(domain, JordanAlgebraSpace): raise TypeError( @@ -170,7 +170,7 @@ def __init__( ) self.base = base - @checked_method(in_space="domain", out_scalar=True) + @checked_method(in_space="domain", out_space="codomain") def value(self, x: Any) -> Any: """Return ``f(lambda(x))``.""" return self.base.value(cast(Any, self.domain).spectrum(x)) diff --git a/tests/functional/test_field_codomain.py b/tests/functional/test_field_codomain.py new file mode 100644 index 0000000..d17a893 --- /dev/null +++ b/tests/functional/test_field_codomain.py @@ -0,0 +1,139 @@ +"""Functional fields remain independent across binding, algebra and round trips.""" +import numpy as np +import pytest + +import spacecore as sc +from spacecore._checks import checked_method + + +@pytest.fixture +def complex_valued(): + ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) + dom = sc.DenseCoordinateSpace((2,), ctx) + return sc.MatrixFreeLinearFunctional( + lambda x: (1 + 2j) * x.sum(), dom, + cod=sc.ComplexField(sc.Context(ctx.ops, dtype=np.complex128)), + ) + + +def test_real_domain_complex_codomain(complex_valued): + f = complex_valued + assert f.domain.dtype == np.dtype('float64') + assert f.ctx.dtype == np.dtype('float64') + assert isinstance(f.codomain, sc.ComplexField) + assert f.codomain.dtype == np.dtype('complex128') + assert f.value(np.array([1., 2.])) == 3 + 6j + assert 'ℝ' in repr(f) and 'ℂ' in repr(f) + + +@pytest.mark.parametrize('build', [lambda f: f, lambda f: 2 * f, lambda f: f + f, + lambda f: f + 1, lambda f: f * f, + lambda f: 0 * f, + lambda f: f.compose(sc.IdentityLinOp(f.domain))]) +def test_codomain_survives_algebra_conversion_and_pytree(complex_valued, build): + f = build(complex_valued) + x = np.array([1., 2.]) + children, aux = f.tree_flatten() + restored = type(f).tree_unflatten(aux, children) + converted = f.convert(sc.Context(sc.NumpyOps(), dtype=np.float32)) + for obj in (f, restored, converted): + assert isinstance(obj.codomain, sc.ComplexField) + assert obj.domain.field == 'real' + np.testing.assert_allclose(obj.value(obj.ctx.asarray(x)), f.value(x)) + assert converted.codomain.dtype == np.dtype('complex64') + assert converted.domain.dtype == np.dtype('float32') + + +def test_explicit_codomain_participates_in_equality(complex_valued): + f = complex_valued + real = sc.MatrixFreeLinearFunctional(f.value_fn, f.domain) + assert f != real + + +def test_default_hermitian_codomain_is_real(): + ctx = sc.Context(sc.NumpyOps(), dtype=np.complex128) + dom = sc.HermitianSpace(2, ctx=ctx) + f = sc.InnerProductFunctional(dom.zeros(), dom) + assert isinstance(f.codomain, sc.RealField) + assert f.codomain.dtype == np.dtype('float64') + assert f.domain.dtype == np.dtype('complex128') + + +def test_codomain_must_be_a_field(): + dom = sc.DenseCoordinateSpace((2,), sc.Context(sc.NumpyOps(), dtype=np.float64)) + with pytest.raises(TypeError, match='must be a Field'): + sc.MatrixFreeLinearFunctional(lambda x: x.sum(), dom, cod=dom) + + +@pytest.mark.parametrize('level,raises', [('none', False), ('cheap', False), + ('standard', True), ('strict', True)]) +def test_functional_policy_controls_field_output_check(level, raises): + # Deliberately different policies on both supplied endpoints. + ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) + dom = sc.DenseCoordinateSpace((2,), ctx, check_level='none') + cod = sc.RealField(ctx, check_level='strict') + f = sc.MatrixFreeLinearFunctional(lambda x: x, dom, cod=cod, check_level=level) + assert cod.check_level == 'strict' + for obj in (f, f.convert(sc.Context(sc.NumpyOps(), dtype=np.float32))): + if raises: + with pytest.raises(sc.SpaceValidationError, match='Expected scalar output'): + obj.value(obj.ctx.asarray([1., 2.])) + else: + obj.value(obj.ctx.asarray([1., 2.])) + + +def test_exact_batch_check_is_stronger_than_scalar_field_batch_membership(): + class InvalidBatch(sc.MatrixFreeLinearFunctional): + def value(self, x): + return x.sum() + + @checked_method(in_space='domain', in_batched=True, out_batched_scalar=True) + def vvalue(self, xs): + return self.ctx.asarray(np.ones((len(xs), 1))) + + ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) + f = InvalidBatch(lambda x: x.sum(), sc.DenseCoordinateSpace((2,), ctx)) + from spacecore.space.checks import _run_checks + + _run_checks(f.codomain, np.ones((3, 1)), allow_leading=True) + with pytest.raises(ValueError, match='scalar batch output'): + f.vvalue(np.ones((3, 2))) + + +def test_complex_constant_and_zero_round_trip(): + ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) + dom = sc.DenseCoordinateSpace((2,), ctx) + field = sc.ComplexField() + for f in (sc.ConstantFunctional(dom, 1j, cod=field), sc.ZeroFunctional(dom, cod=field)): + children, aux = f.tree_flatten() + restored = type(f).tree_unflatten(aux, children) + assert restored == f + assert isinstance(restored.codomain, sc.ComplexField) + np.testing.assert_allclose(restored.value(dom.zeros()), f.value(dom.zeros())) + + +def test_field_subclass_with_extra_constructor_args_survives_binding(): + """The codomain is rebuilt through `_convert`, not `type(cod)(ctx, ...)`.""" + + class TaggedField(sc.RealField): + """A public-Field subclass whose constructor takes more than a context.""" + + def __init__(self, tag, ctx=None, check_level=None): + super().__init__(ctx, check_level=check_level) + self.tag = tag + + def _convert(self, new_ctx): + return type(self)(self.tag, new_ctx, check_level=self.check_level) + + ctx = sc.Context(sc.NumpyOps(), dtype=np.float64) + cod = TaggedField('t', ctx, check_level='cheap') + f = sc.MatrixFreeLinearFunctional( + lambda x: x.sum(), sc.DenseCoordinateSpace((2,), ctx), + cod=cod, check_level='strict', + ) + + assert isinstance(f.codomain, TaggedField) + assert f.codomain.tag == 't' + assert f.codomain.check_level == 'strict' # follows the functional's policy + assert cod.check_level == 'cheap' # the caller's object is untouched + assert f.value(np.array([1., 2.])) == 3. diff --git a/tests/functional/test_functional_base.py b/tests/functional/test_functional_base.py index 10629db..803f533 100644 --- a/tests/functional/test_functional_base.py +++ b/tests/functional/test_functional_base.py @@ -284,7 +284,7 @@ def test_message_distinguishes_single_from_batched(self): # =========================================================================== -# Functional codomain contract: out_scalar / out_batched_scalar +# Functional codomain and exact batched scalar contracts # =========================================================================== class _NonScalar(sc.Functional): """Violates the ``F : X -> K`` contract by returning its input.""" @@ -308,12 +308,7 @@ def _convert(self, new_ctx): class TestScalarOutputContract: - """``out_scalar`` is the codomain check ``out_space`` cannot express. - - A ``Functional``'s codomain is the scalar field, reported only as a string - via ``domain.field`` — there is no ``Space`` object to bind ``out_space`` to, - which is why the output side went unchecked while every input was validated. - """ + """A Field codomain validates scalar outputs through ordinary space checks.""" def _domain(self, ctx, check_level=None): return sc.DenseCoordinateSpace((2,), ctx, check_level=check_level) @@ -367,10 +362,6 @@ def test_batched_output_must_be_one_scalar_per_element(self, numpy_ctx): class TestCheckedMethodScalarFlags: """Guards on the decorator itself.""" - def test_flags_are_mutually_exclusive(self): - with pytest.raises(TypeError): - checked_method(in_space="domain", out_scalar=True, out_batched_scalar=True) - def test_batched_scalar_requires_in_space(self): with pytest.raises(TypeError, match="requires in_space"): checked_method(out_batched_scalar=True) From 73e9072ff946164be664f0ab0313a9af0b94147e Mon Sep 17 00:00:00 2001 From: Pavlo Pelikh Date: Sat, 12 Sep 2026 17:40:32 -0400 Subject: [PATCH 3/4] Record the Field contract in ADR-010, ADR-015 and the changelog ADR-010 keeps its decision and gains what now enforces it: the codomain is a `Field`, the type bound no longer separates a functional from a linear operator into a one-dimensional codomain, and an explicit construction-time check does. It also records why exact batched scalar validation keeps its own flag. ADR-015 gains the object-valued accessor beside the two string ones. The changelog marks the storage-dtype rejection breaking: it lives on `Space.__init__`, so it binds every space rather than only the field-backed numerical ones, and a downstream space over an integer or Boolean dtype now raises at construction. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 17 +++++++++++++ docs/dev/adr/010_functional_contract.md | 18 +++++++++++--- .../adr/015_dtype_default_vs_scalar_field.md | 24 +++++++++++++++++-- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58514ee..2cba822 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,18 @@ and the project adheres to [Semantic Versioning](https://semver.org/). ### Changed +- Functionals now expose a `Field` codomain, defaulting to `domain.scalars`, + and validate scalar output through ordinary space membership. Explicit field + codomains can differ from the domain's coefficient field; exact batched + scalar checks remain unchanged. +- `LinOp` rejects a `Field` codomain at construction, preserving the separate + functional and Riesz-gradient contract from ADR-010. +- **Spaces reject integer/Boolean storage and complex coefficients over real + storage at construction** (breaking). The guard lives on `Space.__init__`, so + it applies to every space rather than only the field-backed numerical ones. A + space over an integer or Boolean dtype now raises; represent such data in a + floating dtype, or model it outside the vector-space hierarchy. + - **`LinOp` endpoints are bound to `VectorSpace`** rather than `CoordinateSpace`. A linear operator needs only `zeros`/`add`/`scale` from its endpoints; coordinates are a representation, not part of the contract. Operators can now @@ -38,6 +50,11 @@ and the project adheres to [Semantic Versioning](https://semver.org/). ### Added +- Public `Field`, `RealField`, and `ComplexField` scalar spaces with Euclidean + geometry, and `Space.scalars` for accessing the coefficient field on the + same backend. Real coefficients over complex storage use the corresponding + real dtype without changing the owning space's context. + - `CapabilityError`, exported from `spacecore` and `spacecore.space`, raised when a space lacks a capability the requested operation needs. diff --git a/docs/dev/adr/010_functional_contract.md b/docs/dev/adr/010_functional_contract.md index 0b514f0..bceaca1 100644 --- a/docs/dev/adr/010_functional_contract.md +++ b/docs/dev/adr/010_functional_contract.md @@ -10,11 +10,22 @@ Functionals are scalar-valued maps on spaces. They share context and validation ## Current design -`Functional` owns one domain space and a context. Subclasses implement `value(x)`. `vvalue(xs)` evaluates over a leading batch axis through backend `vmap` unless overridden. `LinearFunctional` adds a Riesz-gradient contract through `representer`, `grad`, and `vgrad`. `InnerProductFunctional` stores a representer and evaluates ``. `MatrixFreeLinearFunctional` trusts user evaluation callables and has no stored representer. `QuadraticForm` defines optional `grad`, `vgrad`, and `hess_apply`; `LinOpQuadraticForm` stores a Hermitian operator, optional linear term, and scalar offset. Pull-backs are built by composing with a LinOp and have specializations for inner-product and quadratic functionals. +`Functional` owns a domain space, a scalar `Field` codomain, and a context. The codomain defaults to `domain.scalars`; an explicit `cod=` may independently select a real or complex field. Subclasses implement `value(x)`. `vvalue(xs)` evaluates over a leading batch axis through backend `vmap` unless overridden. `LinearFunctional` adds a Riesz-gradient contract through `representer`, `grad`, and `vgrad`. `InnerProductFunctional` stores a representer and evaluates ``. `MatrixFreeLinearFunctional` trusts user evaluation callables and has no stored representer. `QuadraticForm` defines optional `grad`, `vgrad`, and `hess_apply`; `LinOpQuadraticForm` stores a Hermitian operator, optional linear term, and scalar offset. Pull-backs are built by composing with a LinOp and have specializations for inner-product and quadratic functionals. ## Decision -A functional is not a LinOp with a one-dimensional codomain. It owns a domain and scalar-valued evaluation, and gradients are space elements with respect to the domain geometry when implemented. +A functional is not a LinOp with a one-dimensional codomain. It owns a domain, a `Field` codomain and scalar-valued evaluation, and gradients are space elements with respect to the domain geometry when implemented. + +`Field` inherits `InnerProductSpace` (and therefore `VectorSpace`), but not +`CoordinateSpace`. Since `LinOp` now accepts `VectorSpace` endpoints, the type +bound alone cannot maintain this separation. `LinOp.__init__` explicitly rejects +a `Field` codomain with an error citing ADR-010. Use a `Functional` for that map. + +Scalar output validation uses `out_space="codomain"` and the field's +`ScalarShapeCheck`, active only at `standard` and `strict`. Exact batch output +validation deliberately retains `out_batched_scalar`: ordinary batched space +membership cannot require exactly `(N,)`. The matrix-free explicit batch +callback retains its existing check of all leading axes (ADR-006). ## Rationale @@ -30,7 +41,8 @@ New functionals must validate domain inputs and scalar output shape when checks ## Contributor invariants -- `value` returns a scalar-like backend value, not an element of a codomain space. +- `value` returns a scalar-like backend value in its `Field` codomain. +- Codomain conversion and pytree reconstruction preserve the mathematical field; a complex codomain must not promote a real domain to complex storage. - `grad` returns a domain element when implemented. - Matrix-free functionals do not imply a stored representer. - Functional composition requires `A.codomain == F.domain`. diff --git a/docs/dev/adr/015_dtype_default_vs_scalar_field.md b/docs/dev/adr/015_dtype_default_vs_scalar_field.md index 7f164f0..6e93df5 100644 --- a/docs/dev/adr/015_dtype_default_vs_scalar_field.md +++ b/docs/dev/adr/015_dtype_default_vs_scalar_field.md @@ -16,12 +16,31 @@ Some mathematical constraints are already dtype-sensitive. `EuclideanElementwise ## Decision -`Context.dtype` is a representation default, not the full mathematical scalar-field contract. `Space.field` describes whether membership is over a real or complex scalar field. Dtype checks remain representation checks. Field-level checks decide whether complex values are mathematically allowed, while exact dtype checks continue to decide representation compatibility. +`Context.dtype` is a representation default, not the full mathematical scalar-field contract. `Space.field` describes real or complex entries; `Space.scalar_field` describes the allowed coefficients. Dtype checks remain representation checks. Field-level checks decide whether complex values are mathematically allowed, while exact dtype checks continue to decide representation compatibility. For 0.4.0 Stage 1, `Space.field` is derived from `Context.dtype`; no constructor-level override is provided. `FieldCheck` makes scalar-field compatibility explicit while `DTypeCheck` continues to enforce exact representation dtype. Conversion rejects complex-to-real narrowing unless the caller explicitly extracts a real part first. Exact dtype equality and cross-precision compatibility remain strict until concrete workloads justify relaxing them. Solver workspaces should be operand-driven: real diagnostics for complex operators use `ops.real_dtype(ctx.dtype)`, while vector workspaces use the operator/context dtype. +`Space.scalars` returns a public `RealField` or `ComplexField` on the same +backend. It is separate from both string accessors and does not mutate the +owning space's context. Fields have Euclidean inner products and scalar shape +membership, but no coordinate or batching capability. + +| Coefficient field | Element storage | Scalar field storage | +| --- | --- | --- | +| real | real floating | same dtype | +| real | complex floating | corresponding real dtype | +| complex | real floating | rejected at construction | +| complex | complex floating | same dtype | + +Integer and Boolean space storage are rejected. For example, a +`HermitianSpace` over `complex128` has a `RealField` over `float64`, while the +Hermitian space retains `complex128`. A functional's explicit field codomain +is validated independently: a real domain can have complex-valued output. +Field conversion preserves its real/complex identity and uses the target +backend and corresponding precision. + ## Rationale Dtype and scalar field answer different questions. Dtype controls storage and backend kernels; field controls mathematical membership and valid operations. Conflating them makes complex membership and precision compatibility ambiguous. @@ -37,6 +56,7 @@ Using exact dtype as the only field proxy was rejected because real-vs-complex m ## Contributor invariants - `Context.dtype` controls representation defaults and constructors. -- `Space.field` is the mathematical contract for real vs complex membership and is derived from the context dtype in 0.4.0. +- `Space.field` describes entries and is derived from the context dtype. +- `Space.scalar_field` describes coefficients; `Space.scalars` is their object-valued field. - Current dtype membership remains exact and strict. - Solver scalar diagnostics should use real workspaces when mathematically real, especially for complex operators. From ed9f50e98c52970556230cf850fbb59b98f580f0 Mon Sep 17 00:00:00 2001 From: Pavlo Pelikh Date: Sat, 12 Sep 2026 17:47:27 -0400 Subject: [PATCH 4/4] Document the new cod and Field constructor parameters `scripts/docstring_audit.py --check` is a CI gate and it failed on eight numpydoc issues the change introduced: the `cod` parameter on the four functional subclasses that now accept it, `ctx` and `check_level` on the three field classes, and a summary line sharing the opening quotes on `Field`. Co-Authored-By: Claude Opus 5 --- .../__pycache__/_algebra.cpython-313.pyc | Bin 41057 -> 41391 bytes .../__pycache__/_linear.cpython-313.pyc | Bin 15645 -> 15812 bytes .../__pycache__/_quadratic.cpython-313.pyc | Bin 10190 -> 10358 bytes spacecore/functional/_algebra.py | 6 +++ spacecore/functional/_linear.py | 5 ++- spacecore/functional/_quadratic.py | 3 ++ spacecore/space/base/_field.py | 35 ++++++++++++++++-- 7 files changed, 45 insertions(+), 4 deletions(-) diff --git a/spacecore/functional/__pycache__/_algebra.cpython-313.pyc b/spacecore/functional/__pycache__/_algebra.cpython-313.pyc index 5af4cf8c74fd2f913f2160ccf5233038c566b1e0..b2412cc4d55e10f4d44fa9b2875e77a65ef3e6a0 100644 GIT binary patch delta 786 zcmd6l%S%*o6vyxP=XEem1;czSbmA z3Cr>Ev62;QpIzG@JbWjirx??8wK$W-IdvD`!b?1w*Cig7q;yt0Rj(T0*0J8IdVD$D zrMcBMuj%nMb5rMLi^l)nY%sW@7?Ib8qU7>2)8GM3^>RHB%dK$gt$wd|h`YmR?lHK> z=hFO|m3$`G-T#sdQxB-k7Fssj{hGMH<2ovZS~|n(Ad2$R#C-i4QCapHjly0&2bF%8 z%Ya6#;sk6SNRm+~I@8m|&88$Vv2y{s7^r*$gY>Ebpj`~_`Wc-R9kZBAL%V~B>$E zT06kfSVVfBC4JU6jSLd{dcaPjeg|w>%t952bE#RhDR3Of79ryY^2LZ5!e&z1Qh}|a z{lK`z%s;qMMiK2jG6pnJV#k9RoT2AkJ;0Et@BYMw!+q@JIAx79LeA66z-_8M+rdFw3aeV zdMyu$6e*f9ZM?LSL<^~~f`sbEjPOG6Lc+sJ=_35J_v(4z;rHVmn-&YNMcBI894hJ8 z`);s2`MN*Mn~J^sAT>@ z61YSsGQUFO=&U55mYmstjJu4cl>|=9c?h{4l2j7G%ek{5wA{R#>k94yy=-;~V3@<* zv4D>f3;)7T2i0HlbA4VpPl>99{#g}1R@4WK^Y?Szz!X)NEMc0}(qiBPO_nW2ZNsZx z>Q8Lml2(OXx1Qv{d5ZzF{OMu}u*OQcW`N+jiW?A7f+E~|sKKR>U3&{FFKE#~3~yd3 zgd8h$R1&pYFNiyfx~ z-p~P>!$&f92Jo3DIvv0_Ub^SmCTm?!Wz2I-za=E@IFN}BS5Vps!8DN#-9XXrR~!=G^VOQtdmC8rvKXU`_Z0S1 zYu`aiq}2`G(z2`EGPsoyJY}q_TDINfiG-O{bv17$5>Y;BnLIC4of~xRYBx0`zyuE^IE^x}o;x;q%YXuM|+ekN7SkW@Kz6dLT6a5a5I8=a^jPoK^_{iSx< zeu;F^<-j~JO5cMnV2plZQw$R{Iot}lYm5xB+Ag%5>|C_Hg*%H5nTt*bYc4REJ{Rr# G(WPH}|8(jA delta 249 zcmWlRy-LGi07bw1r7cZO6Dyb?DXsYbgIGi@MN||V>LP+j5oy;N9YpC81s8XN0SBQ_ zg>H5z_Ysut&c1+yU?iIe8qai=b6zf*t{$e-DG_nsYde)fSFfn@2CS>M;6tAQEV8S0 zfHl_i6i}vPD2QwEWRT@qe*@UzdnO;l5tHU1(C~BC8(e-{8Q_ZAP*viZ$F>J}{K*ag zcQkSvK$E-qga7(zI3e*!jue4d?>_~ggjCFHN5Gry_8WM|<-CJGSZyy~CYGb}Id w10}YWW57Dw#chc#2BmRCBXgxF?MaMzZfoMznhr(ep(5mHAS3(Q^XsdJf78oJrT_o{ diff --git a/spacecore/functional/__pycache__/_quadratic.cpython-313.pyc b/spacecore/functional/__pycache__/_quadratic.cpython-313.pyc index b6eb002e573d657936b7ab43346eb62acd2ad3bf..82708a08a8a22e873d741bd4d50322e90824db1e 100644 GIT binary patch delta 356 zcmX@-|1E&;GcPX}0}$|>Sems)b|YU26O%L3<`$;;jP>~`3RVhknW;G`3i(9}e))N+ zItuv(C7Jnoi8)*fKoFdqn3Gtfker{ApPQJOr=yUPnwD6aQ<9mNu27P%P?C|V0FhBI zR!B%F2CFVkNYGR8EGbqfN=?iGnhQ24IX|}`C$&N$4QyFvu|j5EN~&r>Dv-=8QOHk2 zHLAEIzbG*sXm&|uLF#4&mPAHIwaN3?e6xXkJtO1B%^MV?85y^4KBL&d$hdcNsPZe2fexzTOpN)HBh*bq$yC|aeg#pC*=)^dAzPdgDIAwHK delta 190 zcmewsaL%9aGcPX}0}#wQwlwRf%tpQvCZ;)zn_HOXGj2Y`6357>yjhb?nvqd`vL>6{ z=4AHOjEvfw`8l~5S$SAFzQ}Br<66bYXtenx_iRqan9Zr8rA&;~lW&WEXKdPhS>it< zWBcUGQok6dZayc?%E<@)enTjPo~dmQ`kAT(bGPd_5!My3OH=K=Ze3E>`MbWZbp+ qkMb*!fzMUNnHcjX|5rB=mS%KjoZvO1?4pR;7X}dLqs?S@4SfI*=0AD> diff --git a/spacecore/functional/_algebra.py b/spacecore/functional/_algebra.py index 9e09b92..719a5d0 100644 --- a/spacecore/functional/_algebra.py +++ b/spacecore/functional/_algebra.py @@ -326,6 +326,9 @@ class ZeroFunctional(Functional): check_level : {{"none", "cheap", "standard", "strict"}}, optional Runtime validation policy for this object. When omitted, the ambient default (see :func:`spacecore.get_check_level`) is used. + cod : Field or None, optional + Scalar codomain, defaulting to the domain's ``scalars``. Its real or + complex field is independent of the domain's storage dtype. """ def __init__( @@ -400,6 +403,9 @@ class ConstantFunctional(Functional): Runtime validation policy for this functional. When omitted, the ambient default (see :func:`spacecore.get_check_level`) is used. Validation policy is a property of the functional, not of the ``Context``. + cod : Field or None, optional + Scalar codomain, defaulting to the domain's ``scalars``. Its real or + complex field is independent of the domain's storage dtype. Attributes ---------- diff --git a/spacecore/functional/_linear.py b/spacecore/functional/_linear.py index 9d16eb0..9eedb8d 100644 --- a/spacecore/functional/_linear.py +++ b/spacecore/functional/_linear.py @@ -35,7 +35,7 @@ def _convert_space_element(space: Space, value: Any) -> Any: @core_kernels("functional-linear") class LinearFunctional(Functional[Domain]): - r""" + """ Represent a linear scalar-valued map. Parameters @@ -47,6 +47,9 @@ class LinearFunctional(Functional[Domain]): check_level : {{"none", "cheap", "standard", "strict"}}, optional Runtime validation policy for this object. When omitted, the ambient default (see :func:`spacecore.get_check_level`) is used. + cod : Field or None, optional + Scalar codomain, defaulting to the domain's ``scalars``. Its real or + complex field is independent of the domain's storage dtype. """ @property diff --git a/spacecore/functional/_quadratic.py b/spacecore/functional/_quadratic.py index 37d0a56..fd3872a 100644 --- a/spacecore/functional/_quadratic.py +++ b/spacecore/functional/_quadratic.py @@ -30,6 +30,9 @@ class QuadraticForm(Functional[Domain]): check_level : {{"none", "cheap", "standard", "strict"}}, optional Runtime validation policy for this object. When omitted, the ambient default (see :func:`spacecore.get_check_level`) is used. + cod : Field or None, optional + Scalar codomain, defaulting to the domain's ``scalars``. Its real or + complex field is independent of the domain's storage dtype. """ def hess_apply(self, x: Any) -> Any: diff --git a/spacecore/space/base/_field.py b/spacecore/space/base/_field.py index 65e3e74..5f18d2a 100644 --- a/spacecore/space/base/_field.py +++ b/spacecore/space/base/_field.py @@ -31,11 +31,20 @@ def error_message(self, space, x): class Field(InnerProductSpace): - """Euclidean scalar field with no coordinate or batching capability. + """ + Euclidean scalar field with no coordinate or batching capability. Field membership checks scalar shape at standard and strict levels. Its context records the representation of coefficients, independently of the storage dtype of a vector space using those coefficients. + + Parameters + ---------- + ctx : Context, str, or None, optional + Backend context specification. Default is the ambient context. + check_level : {"none", "cheap", "standard", "strict"}, optional + Runtime validation policy for this space. When omitted, the ambient + default (see :func:`spacecore.get_check_level`) is used. """ checks = (ScalarShapeCheck(),) @@ -76,7 +85,17 @@ def _convert(self, new_ctx): class RealField(Field): - """Real scalars, represented in a real floating dtype.""" + """ + Real scalars, represented in a real floating dtype. + + Parameters + ---------- + ctx : Context, str, or None, optional + Backend context specification. Default is the ambient context. + check_level : {"none", "cheap", "standard", "strict"}, optional + Runtime validation policy for this space. When omitted, the ambient + default (see :func:`spacecore.get_check_level`) is used. + """ declared_scalar_field = "real" @@ -86,7 +105,17 @@ def __init__(self, ctx=None, check_level=None): class ComplexField(Field): - """Complex scalars; explicit contexts must use complex floating storage.""" + """ + Complex scalars; explicit contexts must use complex floating storage. + + Parameters + ---------- + ctx : Context, str, or None, optional + Backend context specification. Default is the ambient context. + check_level : {"none", "cheap", "standard", "strict"}, optional + Runtime validation policy for this space. When omitted, the ambient + default (see :func:`spacecore.get_check_level`) is used. + """ declared_scalar_field = "complex"