Source code for SFI.statefunc.basis

"""Basis façade: dictionary of deterministic functions."""

from .psf import PSF
from .stateexpr import StateExpr


# ============================================================================
#  BASIS  – deterministic dictionary
# ============================================================================
[docs] class Basis(StateExpr): """Deterministic dictionary façade (no parameters).""" def __init__(self, root): if root.param_suite is not None: raise ValueError("Basis root must be parameter-free") super().__init__(root) def __call__(self, x, *, v=None, mask=None, extras=None): """Evaluate the basis on a single or batched input. Parameters ---------- x : array If `particles_input=False`: shape `batch · dim`. If `particles_input=True`: shape `batch · P · dim`. `batch` can be empty (single input). v : array | None Optional velocity matching `x.shape` (required if `needs_v=True`). mask : array | scalar | None Broadcasts to the prefix of `x` **including** the particle axis. Boolean or numeric masks are accepted. extras : dict | None Optional per-batch metadata. Presence is enforced according to the aggregated node requirements; values must broadcast over **batch only**. Returns ------- array Shape `batch · [P]^pdepth · (dim)^rank · n_features`. """ self._validate_extras_presence(extras) return self._caller(x, v, mask, extras, None) # convenience @property def labels(self): _, labs, _ = self.root.flatten() return labs def __len__(self): return self.n_features # Upgrade-aware dispatch: if a derived node carries parameters, # the result is a PSF (e.g. Basis * PSF, PSF - Basis, …). def _with_node(self, new_root): if new_root.param_suite is not None: return PSF(new_root) return Basis(new_root) # factory → linear-coeff PSF
[docs] def to_psf(self, coeff_key: str = "coeff", drop_features=True): """ Return a **parametric state function** whose value is a linear combination of this Basis' features: F(x; θ) = Σ_j θ_j · f_j(x) Note that use cases are rare within SFI, since the PSF's features axis is typically used for nonlinearities and/or vector/tensor components. But this can be useful for quick prototyping of linear models, benchmark comparisons of linear vs nonlinear solvers, or as a building block for more complex PSFs. Parameters ---------- coeff_key : str Key name for the coefficient vector in the parameter dict. drop_features: bool Whether to remove the trailing size-1 feature axis (default True). Notes ----- The resulting `PSF` shares the same spatial contract (rank/dim/pdepth, particles_input) as this `Basis`, and does not have a features axis. """ from .nodes import CoeffNode return PSF(CoeffNode(self.root, coeff_key=coeff_key), drop_features=drop_features)