"""Objective function for PEtab models using the PEtab simulator."""
from __future__ import annotations
try:
import petab.v1 as petab
from petab.v1.simulate import Simulator as PetabSimulator
except ImportError:
petab = None
from collections import OrderedDict
from collections.abc import Sequence
import numpy as np
from ..C import FVAL, MODE_FUN, MODE_RES, RES, ModeType
from .base import ObjectiveBase, ResultDict
[docs]
class PetabSimulatorObjective(ObjectiveBase):
"""Objective function for PEtab models using the PEtab simulator."""
[docs]
def __init__(
self,
simulator: PetabSimulator,
x_names: Sequence[str] | None = None,
):
"""Initialize the PEtab simulator objective function.
Parameters
----------
petab_problem:
The PEtab problem.
simulator:
The PEtab simulator.
x_names:
Names of optimization parameters.
"""
if petab is None:
raise ImportError(
"The `petab` package is required for this objective function."
)
self.simulator = simulator
self.petab_problem = self.simulator.petab_problem
if x_names is None:
x_names = list(self.petab_problem.get_x_ids())
super().__init__(x_names=x_names)
[docs]
def replace_parameters(self, x: np.ndarray):
"""Replace the parameters in the PEtab problem with the given values.
Parameters
----------
x:
Parameter vector for optimization.
"""
x_dict = OrderedDict(zip(self._x_names, x, strict=True))
x_unscaled = self.petab_problem.unscale_parameters(x_dict)
par_df = self.petab_problem.parameter_df
par_df["nominalValue"] = par_df.index.map(x_unscaled)
self.simulator.set_parameters(x_unscaled)
[docs]
def call_unprocessed(
self,
x: np.ndarray,
sensi_orders: tuple[int, ...],
mode: ModeType,
return_dict: bool,
**kwargs,
) -> ResultDict:
"""See :meth:`ObjectiveBase.call_unprocessed`."""
self.replace_parameters(x)
sim_df = self.simulator.simulate(noise=False, as_measurement=False)
result = {}
result["simulations"] = sim_df
if mode == MODE_FUN:
result[FVAL] = -petab.calculate_llh(
measurement_dfs=self.petab_problem.measurement_df,
simulation_dfs=sim_df,
observable_dfs=self.petab_problem.observable_df,
parameter_dfs=self.petab_problem.parameter_df,
)
elif mode == MODE_RES:
result[RES] = petab.calculate_residuals(
measurement_dfs=self.petab_problem.measurement_df,
simulation_dfs=sim_df,
observable_dfs=self.petab_problem.observable_df,
parameter_dfs=self.petab_problem.parameter_df,
)
return result
[docs]
def check_sensi_orders(
self,
sensi_orders: tuple[int, ...],
mode: ModeType,
) -> bool:
"""See :class:`ObjectiveBase` documentation."""
if not sensi_orders:
return True
sensi_order = max(sensi_orders)
max_sensi_order = 0
return sensi_order <= max_sensi_order