Source code for pycif.plugins.modes.training.backends.base

"""Abstract base class for surrogate backends.

Every concrete backend must implement four methods:
    build_default_model – construct a default MLP in the backend's framework
    wrap_operator       – return a differentiable callable (framework-native)
    train               – run the training loop
    export_onnx         – serialise the trained model to ONNX
"""
from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Any, Callable, List, Tuple


[docs] class SurrogateBackend(ABC): """Abstract base for PyTorch / JAX surrogate backends.""" def __init__(self, obs_operator: Any, run_mode: str = "fwd") -> None: self.obs_operator = obs_operator self.run_mode = run_mode
[docs] @abstractmethod def build_default_model( self, n_state: int, n_obs: int, hidden_size: int, n_hidden_layers: int, ) -> Any: """Return a default untrained model for this backend's framework. The model must accept a 1-D state vector of length *n_state* and return a 1-D observation vector of length *n_obs*. """
[docs] @abstractmethod def wrap_operator(self, run_mode: str = "fwd", reload_results: bool = False) -> Callable: """Return a differentiable callable wrapping the CIF operator. Args: run_mode: ``"fwd"`` calls ``obs_operator.forward``; ``"tl"`` calls ``obs_operator.tangent_linear``. The backward pass always delegates to ``obs_operator.adjoint`` regardless of *run_mode*. reload_results: Forwarded to every operator call; if ``True``, already-computed CIF simulations are reloaded from disk. """
[docs] @abstractmethod def train( self, model: Any, sample_fn: Callable, n_steps: int, *, run_mode: str, reload_results: bool, adjoint_consistency: bool, lambda_adj: float, ) -> Tuple[Any, List[float]]: """Run the training loop and return ``(trained_model, losses)``."""
[docs] @abstractmethod def export_onnx(self, path: str, model: Any, input_shape: Tuple[int, ...]) -> None: """Export *model* to ONNX at *path*."""