"""Defines top-level interfaces of tensorwaves."""
from abc import ABC, abstractmethod
from typing import (
Any,
Callable,
Dict,
FrozenSet,
Mapping,
Optional,
Sequence,
Tuple,
Union,
)
import numpy as np
from expertsystem.amplitude.kinematics import ReactionInfo
# Data classes from the expertsystem do not work with jax and jit
# https://github.com/google/jax/issues/3092
# https://github.com/google/jax/issues/4416
FourMomentum = Tuple[float, float, float, float]
MomentumSample = Mapping[int, Sequence[FourMomentum]]
DataSample = Mapping[str, np.ndarray]
"""Input data for a `Function`."""
[docs]class Function(ABC):
"""Interface of a callable function.
The parameters of the model are separated from the domain variables. This
follows the mathematical definition, in which a function defines its domain
and parameters. However specific points in the domain are not relevant.
Hence while the domain variables are the argument of the evaluation (see
:func:`~Function.__call__`), the parameters are controlled via a getter and
setter (see :func:`~Function.parameters`). The reason for this separation
is to facilitate the events when parameters have changed.
"""
[docs] @abstractmethod
def __call__(self, dataset: DataSample) -> np.ndarray:
"""Evaluate the function.
Args:
dataset: a `dict` with domain variable names as keys.
Return:
Result of the function evaluation. Type depends on the input type.
"""
@property
@abstractmethod
def parameters(self) -> Dict[str, Union[float, complex]]:
"""Get `dict` of parameters."""
[docs] @abstractmethod
def update_parameters(
self, new_parameters: Mapping[str, Union[float, complex]]
) -> None:
"""Update the collection of parameters."""
[docs]class Model(ABC):
"""Interface of a model which can be lambdified into a callable."""
[docs] @abstractmethod
def lambdify(self, backend: Union[str, tuple, dict]) -> Callable:
"""Lambdify the model into a Callable.
Args:
backend: Choice of backend for fast evaluations.
The arguments of the Callable are union of the variables and parameters.
The return value of the Callable is Any. In theory the return type
should be a value type depending on the model. Currently, there no
typing support is implemented for this.
"""
@property
@abstractmethod
def parameters(self) -> Dict[str, Union[float, complex]]:
"""Get mapping of parameters to suggested initial values."""
@property
@abstractmethod
def variables(self) -> FrozenSet[str]:
"""Expected input variable names."""
@property
def argument_order(self) -> Tuple[str, ...]:
"""Order of arguments of lambdified function signature."""
[docs]class Estimator(ABC):
"""Estimator for discrepancy model and data."""
[docs] @abstractmethod
def __call__(
self, parameters: Mapping[str, Union[float, complex]]
) -> float:
"""Evaluate discrepancy."""
[docs] @abstractmethod
def gradient(
self, parameters: Mapping[str, Union[float, complex]]
) -> Dict[str, Union[float, complex]]:
"""Calculate gradient for given parameter mapping."""
[docs]class Optimizer(ABC):
"""Optimize a fit model to a data set."""
[docs] @abstractmethod
def optimize(
self,
estimator: Estimator,
initial_parameters: Mapping[str, Union[float, complex]],
) -> Dict[str, Any]:
"""Execute optimization."""
[docs]class PhaseSpaceGenerator(ABC):
"""Abstract class for generating phase space samples."""
[docs] @abstractmethod
def setup(self, reaction_info: ReactionInfo) -> None:
"""Hook for initialization of the PhaseSpaceGenerator.
Called before any generate calls.
"""
[docs] @abstractmethod
def generate(
self, size: int, rng: UniformRealNumberGenerator
) -> Tuple[MomentumSample, np.ndarray]:
"""Generate phase space sample.
Returns a `tuple` of a mapping of final state IDs to `numpy.array` s
with four-momentum tuples.
"""