@@ -706,6 +706,10 @@ def __init__(
706706 self ._fixed_pid_to_idx : dict [str , int ] = {
707707 id_ : i for i , id_ in enumerate (self ._fixed_parameter_ids )
708708 }
709+ # maps PEtab observable IDs to petab Observable instances
710+ self ._petab_id_to_obs : dict [str , v2 .Observable ] = {
711+ obs .id : obs for obs in self ._petab_problem .observables
712+ }
709713
710714 # create a new model instance from the model module from which
711715 # we can get the default parameters
@@ -926,8 +930,7 @@ def apply_parameters(
926930
927931 Update the parameter-dependent values of the given `ExpData` instance
928932 according to the provided problem parameters (i.e., values of the
929- parameters in the PEtab
930- :external+petab:ref:`parameter table <v2_parameter_table>`).
933+ parameters in the PEtab parameter table).
931934
932935 This assumes that:
933936
@@ -941,10 +944,11 @@ def apply_parameters(
941944 In case of errors, the state of `edata` is undefined.
942945 :param problem_parameters: Problem parameters to be applied.
943946 """
947+ # FIXME: xref
948+ # :external+petab:label:`parameter table <v2_parameter_table>`).
944949 # TODO: support ndarray in addition to dict?
945950 # TODO: use the original or the current parameters
946951 # if only a subset is provided?
947- # TODO: must handle output overrides here, or add them to the events
948952
949953 # get the original parameter values
950954 # (model parameters set during model creation)
@@ -984,24 +988,43 @@ def apply_parameters(
984988 fixed_par_vals [p_idx ] = p_val
985989 edata .fixed_parameters_pre_equilibration = fixed_par_vals
986990
987- # apply problem parameter values to identical model parameters
988- # any other parameter mapping is handled by events
991+ # Apply problem parameter values to identical model parameters.
992+ # Any other parameter mapping, except for output parameter
993+ # placeholders, is handled by events.
989994 for k , v in problem_parameters .items ():
990995 if (idx := pid_to_idx .get (k )) is not None :
991996 par_vals [idx ] = v
992997
998+ # Handle measurement-specific mappings to placeholders
999+ # TODO: must handle output overrides here, or add them to the events
9931000 # TODO handle placeholders
9941001 # check that all periods use the same overrides (except for numeric sigmas)
9951002 # see do_import() for details
996- # TODO extract function
9971003 measurements = self ._petab_problem .get_measurements_for_experiment (
9981004 experiment
9991005 )
1000- # encountered placeholders and their overrides
1006+
1007+ def apply_override (placeholder : str , override : sp .Basic ):
1008+ """Apply a single placeholder override."""
1009+ if (idx := pid_to_idx .get (placeholder )) is not None :
1010+ if override .is_Number :
1011+ par_vals [idx ] = float (override )
1012+ elif override .is_Symbol :
1013+ par_vals [idx ] = problem_parameters [str (override )]
1014+ else :
1015+ raise AssertionError (
1016+ f"Unexpected override type: { override } for { placeholder } in experiment { experiment_id } "
1017+ )
1018+ else :
1019+ raise NotImplementedError (
1020+ f"Cannot handle override `{ placeholder } ' => '{ override } '"
1021+ )
1022+
1023+ # tracks encountered placeholders and their overrides
10011024 # (across all observables -- placeholders IDs are globally unique)
10021025 overrides = {}
10031026 for m in measurements :
1004- obs : Observable = self ._petab_problem [m .observable_id ]
1027+ obs = self ._petab_id_to_obs [m .observable_id ]
10051028 if obs .observable_placeholders :
10061029 for placeholder , override in zip (
10071030 obs .observable_placeholders ,
@@ -1016,21 +1039,7 @@ def apply_parameters(
10161039 "Timepoint-specific observable placeholder "
10171040 "overrides are not supported"
10181041 )
1019-
1020- if (idx := pid_to_idx .get (placeholder )) is not None :
1021- if override .is_Number :
1022- par_vals [idx ] = float (override )
1023- elif override .is_Symbol :
1024- par_vals [idx ] = problem_parameters [str (override )]
1025- else :
1026- raise AssertionError (
1027- f"Unexpected override type: { override } for { placeholder } in experiment { experiment_id } "
1028- )
1029- else :
1030- raise NotImplementedError (
1031- f"Cannot handle override `{ placeholder } ' => '{ override } '"
1032- )
1033-
1042+ apply_override (placeholder , override )
10341043 # TODO: set sigmas via parameters or .sigmay
10351044 if obs .noise_placeholders :
10361045 for placeholder , override in zip (
@@ -1045,22 +1054,13 @@ def apply_parameters(
10451054 "Timepoint-specific observable placeholder "
10461055 "overrides are not supported"
10471056 )
1048- if (idx := pid_to_idx .get (placeholder )) is not None :
1049- if override .is_Number :
1050- par_vals [idx ] = float (override )
1051- else :
1052- par_vals [idx ] = problem_parameters [str (override )]
1053- else :
1054- raise NotImplementedError (
1055- f"Cannot handle override `{ placeholder } ' => '{ override } '"
1056- )
1057- # print("ExperimentManager.apply_parameters:")
1058- # print(m)
1059- # print(dict(zip(self._parameter_ids, map(float, par_vals))))
1057+ apply_override (placeholder , override )
1058+
10601059 # TODO: set all unused placeholders to NaN to make it easier to spot problems?
10611060 edata .parameters = par_vals
10621061
10631062 # TODO debug, remove
1063+ # print("ExperimentManager.apply_parameters:")
10641064 # print(
10651065 # f"Parameters: {dict(zip(self._parameter_ids, map(float, par_vals)))}"
10661066 # )
0 commit comments