Source code for pycif.plugins.obsoperators.onnx.onnx_operator
"""Zero-dependency ONNX inference wrapper.
Loads a serialised model from an ONNX file and exposes a ``forward`` method
that maps a 1-D state vector to a 1-D observation vector. The only required
dependency is ``onnxruntime`` — no PyTorch, JAX, or other ML framework needed.
Example::
from pycif.plugins.obsoperators.onnx.onnx_operator import OnnxSurrogateOperator
op = OnnxSurrogateOperator("surrogate.onnx")
y = op.forward(x) # x: np.ndarray float64, y: np.ndarray float64
"""
from __future__ import annotations
import numpy as np
from typing import List
[docs]
class OnnxSurrogateOperator:
"""Drop-in replacement for a CIF obs operator backed by ONNX Runtime.
Args:
onnx_path: Path to the ``.onnx`` model file.
"""
def __init__(self, onnx_path: str) -> None:
try:
import onnxruntime as ort
except ImportError as exc:
raise ImportError(
"onnxruntime is required for zero-dependency inference: "
"pip install onnxruntime"
) from exc
providers: List[str] = ["CUDAExecutionProvider", "CPUExecutionProvider"]
self._session = ort.InferenceSession(onnx_path, providers=providers)
self._input_name: str = self._session.get_inputs()[0].name
self._output_name: str = self._session.get_outputs()[0].name
[docs]
def forward(self, x: np.ndarray) -> np.ndarray:
"""Run inference.
Args:
x: Input array of shape ``(N_state,)`` for a single sample or
``(batch, N_state)`` for a batch. A 1-D input is temporarily
promoted to batch size 1 and squeezed back on output.
Returns:
float64 output array — shape ``(N_obs,)`` for a 1-D input,
``(batch, N_obs)`` for batched input.
"""
squeeze = x.ndim == 1
if squeeze:
x = x[np.newaxis, :]
result: np.ndarray = self._session.run(
[self._output_name],
{self._input_name: x.astype(np.float32)},
)[0]
result = result.astype(np.float64)
return result.squeeze(0) if squeeze else result