Source code for pypesto.objective.roadrunner.road_runner

"""Objective function for RoadRunner models.

Currently does not support sensitivities.
"""

from __future__ import annotations

from collections import OrderedDict
from collections.abc import Sequence

import numpy as np

from ...C import MODE_FUN, MODE_RES, ROADRUNNER_INSTANCE, X_NAMES, ModeType
from ..base import ObjectiveBase
from .roadrunner_calculator import RoadRunnerCalculator
from .utils import ExpData, SolverOptions

try:
    from petab.v1 import Problem as PetabProblem
    from petab.v1.parameter_mapping import ParMappingDictQuadruple
except ImportError:
    petab = None
try:
    import roadrunner
except ImportError:
    roadrunner = None


[docs] class RoadRunnerObjective(ObjectiveBase): """Objective function for RoadRunner models. Currently does not support sensitivities. """
[docs] def __init__( self, rr: roadrunner.RoadRunner, edatas: Sequence[ExpData] | ExpData, parameter_mapping: list[ParMappingDictQuadruple], petab_problem: PetabProblem, calculator: RoadRunnerCalculator | None = None, x_ids: Sequence[str] | None = None, x_names: Sequence[str] | None = None, solver_options: SolverOptions | None = None, ): """Initialize the RoadRunner objective function. Parameters ---------- rr: RoadRunner instance for simulation. edatas: The experimental data. If a list is passed, its entries correspond to multiple experimental conditions. parameter_mapping: Mapping of optimization parameters to model parameters. Format as created by `petab.get_optimization_to_simulation_parameter_mapping`. The default is just to assume that optimization and simulation parameters coincide. petab_problem: The corresponding PEtab problem. Needed to calculate NLLH. Might be removed later. calculator: The calculator to use. If None, a new instance is created. x_ids: IDs of Roadrunner parameters. Includes fixed parameters as well. x_names: Names of optimization parameters. """ self.roadrunner_instance = rr # make sure edatas are a list if isinstance(edatas, ExpData): edatas = [edatas] self.edatas = edatas self.parameter_mapping = parameter_mapping self.petab_problem = petab_problem if calculator is None: calculator = RoadRunnerCalculator() self.calculator = calculator if solver_options is None: solver_options = SolverOptions() self.solver_options = solver_options if x_ids is None: x_ids = list(rr.model.getGlobalParameterIds()) self.x_ids = x_ids if x_names is None: x_names = x_ids super().__init__(x_names=x_names)
[docs] def get_config(self) -> dict: """Return basic information of the objective configuration.""" info = super().get_config() info["solver_options"] = repr(self.solver_options) info[X_NAMES] = self.x_names info[ROADRUNNER_INSTANCE] = self.roadrunner_instance.getInfo() return info
# TODO: Check whether we need some sort of pickling
[docs] def __call__( self, x: np.ndarray, sensi_orders: tuple[int, ...] = (0,), mode: ModeType = MODE_FUN, return_dict: bool = False, **kwargs, ) -> float | np.ndarray | dict: """See :class:`ObjectiveBase` documentation.""" return super().__call__(x, sensi_orders, mode, return_dict, **kwargs)
[docs] def call_unprocessed( self, x: np.ndarray, sensi_orders: tuple[int, ...], mode: ModeType, return_dict: bool, edatas: Sequence[ExpData] | None = None, parameter_mapping: list[ParMappingDictQuadruple] | None = None, ) -> dict: """ Call objective function without pre- or post-processing and formatting. Returns ------- result: A dict containing the results. """ # fill in values if not passed if edatas is None: edatas = self.edatas if parameter_mapping is None: parameter_mapping = self.parameter_mapping # convert x to dictionary x = OrderedDict(zip(self.x_ids, x, strict=True)) ret = self.calculator( x_dct=x, mode=mode, roadrunner_instance=self.roadrunner_instance, edatas=edatas, x_ids=self.x_names, parameter_mapping=parameter_mapping, petab_problem=self.petab_problem, solver_options=self.solver_options, ) return ret
[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
[docs] def check_mode(self, mode: ModeType) -> bool: """See `ObjectiveBase` documentation.""" return mode in [MODE_FUN, MODE_RES]