2121import re
2222from collections .abc import Sequence
2323from itertools import chain
24- from typing import Any , Union
24+ from typing import Any
2525from collections .abc import Collection , Iterator
2626
2727import amici
3636 PARAMETER_SCALE ,
3737 PREEQUILIBRATION_CONDITION_ID ,
3838 SIMULATION_CONDITION_ID ,
39+ NOMINAL_VALUE ,
40+ ESTIMATE ,
3941)
4042from petab .v1 .models import MODEL_TYPE_PYSB , MODEL_TYPE_SBML
4143from sympy .abc import _clash
5254
5355logger = logging .getLogger (__name__ )
5456
55- SingleParameterMapping = dict [str , Union [ numbers .Number , str ] ]
57+ SingleParameterMapping = dict [str , numbers .Number | str ]
5658SingleScaleMapping = dict [str , str ]
5759
5860
5961class ParameterMappingForCondition :
6062 """Parameter mapping for condition.
6163
6264 Contains mappings for free parameters, fixed parameters, and fixed
63- preequilibration parameters, both for parameters and scales.
65+ pre-equilibration parameters, both for parameters and scales.
6466
65- In the scale mappings, for each simulation parameter the scale
67+ In the scale mappings, for each simulation parameter, the scale
6668 on which the value is passed (and potentially gradients are to be
6769 returned) is given. In the parameter mappings, for each simulation
6870 parameter a corresponding optimization parameter (or a numeric value)
@@ -76,9 +78,9 @@ class ParameterMappingForCondition:
7678 :param scale_map_sim_var:
7779 Scales for free simulation parameters.
7880 :param map_preeq_fix:
79- Mapping for fixed preequilibration parameters.
81+ Mapping for fixed pre-equilibration parameters.
8082 :param scale_map_preeq_fix:
81- Scales for fixed preequilibration parameters.
83+ Scales for fixed pre-equilibration parameters.
8284 :param map_sim_fix:
8385 Mapping for fixed simulation parameters.
8486 :param scale_map_sim_fix:
@@ -177,7 +179,7 @@ def __len__(self):
177179 def append (
178180 self , parameter_mapping_for_condition : ParameterMappingForCondition
179181 ):
180- """Append a condition specific parameter mapping."""
182+ """Append a condition- specific parameter mapping."""
181183 self .parameter_mappings .append (parameter_mapping_for_condition )
182184
183185 def __repr__ (self ):
@@ -307,9 +309,10 @@ def unscale_parameters_dict(
307309
308310def create_parameter_mapping (
309311 petab_problem : petab .Problem ,
310- simulation_conditions : pd .DataFrame | list [dict ],
312+ simulation_conditions : pd .DataFrame | list [dict ] | None ,
311313 scaled_parameters : bool ,
312314 amici_model : AmiciModel | None = None ,
315+ fill_fixed_parameters : bool = True ,
313316 ** parameter_mapping_kwargs ,
314317) -> ParameterMapping :
315318 """Generate AMICI specific parameter mapping.
@@ -325,11 +328,14 @@ def create_parameter_mapping(
325328 are assumed to be in linear scale.
326329 :param amici_model:
327330 AMICI model.
331+ :param fill_fixed_parameters:
332+ Whether to fill in nominal values for fixed parameters
333+ (estimate=0 in the parameters table).
334+ To allow changing fixed PEtab problem parameters,
335+ use ``fill_fixed_parameters=False``.
328336 :param parameter_mapping_kwargs:
329337 Optional keyword arguments passed to
330338 :func:`petab.get_optimization_to_simulation_parameter_mapping`.
331- To allow changing fixed PEtab problem parameters (``estimate=0``),
332- use ``fill_fixed_parameters=False``.
333339 :return:
334340 List of the parameter mappings.
335341 """
@@ -377,6 +383,7 @@ def create_parameter_mapping(
377383 mapping_df = petab_problem .mapping_df ,
378384 model = petab_problem .model ,
379385 simulation_conditions = simulation_conditions ,
386+ fill_fixed_parameters = fill_fixed_parameters ,
380387 ** dict (
381388 default_parameter_mapping_kwargs , ** parameter_mapping_kwargs
382389 ),
@@ -388,7 +395,11 @@ def create_parameter_mapping(
388395 simulation_conditions .iterrows (), prelim_parameter_mapping , strict = True
389396 ):
390397 mapping_for_condition = create_parameter_mapping_for_condition (
391- prelim_mapping_for_condition , condition , petab_problem , amici_model
398+ prelim_mapping_for_condition ,
399+ condition ,
400+ petab_problem ,
401+ amici_model ,
402+ fill_fixed_parameters = fill_fixed_parameters ,
392403 )
393404 parameter_mapping .append (mapping_for_condition )
394405
@@ -400,8 +411,9 @@ def create_parameter_mapping_for_condition(
400411 condition : pd .Series | dict ,
401412 petab_problem : petab .Problem ,
402413 amici_model : AmiciModel | None = None ,
414+ fill_fixed_parameters : bool = True ,
403415) -> ParameterMappingForCondition :
404- """Generate AMICI specific parameter mapping for condition .
416+ """Generate AMICI- specific parameter mapping for a PEtab simulation .
405417
406418 :param parameter_mapping_for_condition:
407419 Preliminary parameter mapping for condition.
@@ -412,10 +424,12 @@ def create_parameter_mapping_for_condition(
412424 Underlying PEtab problem.
413425 :param amici_model:
414426 AMICI model.
415-
427+ :param fill_fixed_parameters:
428+ Whether to fill in nominal values for fixed parameters
429+ (estimate=0 in the parameters table).
416430 :return:
417431 The parameter and parameter scale mappings, for fixed
418- preequilibration , fixed simulation, and variable simulation
432+ pre-equilibration , fixed simulation, and variable simulation
419433 parameters, and then the respective scalings.
420434 """
421435 (
@@ -436,10 +450,10 @@ def create_parameter_mapping_for_condition(
436450 if len (condition_map_preeq ) and len (condition_map_preeq ) != len (
437451 condition_map_sim
438452 ):
439- logger .debug (f"Preequilibration parameter map: { condition_map_preeq } " )
453+ logger .debug (f"Pre-equilibration parameter map: { condition_map_preeq } " )
440454 logger .debug (f"Simulation parameter map: { condition_map_sim } " )
441455 raise AssertionError (
442- "Number of parameters for preequilbration "
456+ "Number of parameters for pre-equilbration "
443457 "and simulation do not match."
444458 )
445459
@@ -451,8 +465,8 @@ def create_parameter_mapping_for_condition(
451465 # During model generation, parameters for initial concentrations and
452466 # respective initial assignments have been created for the
453467 # relevant species, here we add these parameters to the parameter mapping.
454- # In absence of preequilibration this could also be handled via
455- # ExpData.x0, but in the case of preequilibration this would not allow for
468+ # In the absence of pre-equilibration this could also be handled via
469+ # ExpData.x0, but in the case of pre-equilibration this would not allow for
456470 # resetting initial states.
457471
458472 if states_in_condition_table := get_states_in_condition_table (
@@ -485,10 +499,11 @@ def create_parameter_mapping_for_condition(
485499 condition_map_preeq ,
486500 condition_scale_map_preeq ,
487501 preeq_value ,
502+ fill_fixed_parameters = fill_fixed_parameters ,
488503 )
489- # need to set dummy value for preeq parameter anyways, as it
504+ # need to set a dummy value for preeq parameter anyways, as it
490505 # is expected below (set to 0, not nan, because will be
491- # multiplied with indicator variable in initial assignment)
506+ # multiplied with the indicator variable in initial assignment)
492507 condition_map_sim [init_par_id ] = 0.0
493508 condition_scale_map_sim [init_par_id ] = LIN
494509
@@ -503,6 +518,7 @@ def create_parameter_mapping_for_condition(
503518 condition_map_sim ,
504519 condition_scale_map_sim ,
505520 value ,
521+ fill_fixed_parameters = fill_fixed_parameters ,
506522 )
507523 # set dummy value as above
508524 if condition_map_preeq :
@@ -549,11 +565,11 @@ def create_parameter_mapping_for_condition(
549565 condition_scale_map_sim_fix = {}
550566
551567 logger .debug (
552- "Fixed parameters preequilibration : " f"{ condition_map_preeq_fix } "
568+ "Fixed parameters pre-equilibration : " f"{ condition_map_preeq_fix } "
553569 )
554570 logger .debug ("Fixed parameters simulation: " f"{ condition_map_sim_fix } " )
555571 logger .debug (
556- "Variable parameters preequilibration : " f"{ condition_map_preeq_var } "
572+ "Variable parameters pre-equilibration : " f"{ condition_map_preeq_var } "
557573 )
558574 logger .debug ("Variable parameters simulation: " f"{ condition_map_sim_var } " )
559575
@@ -579,21 +595,46 @@ def create_parameter_mapping_for_condition(
579595
580596
581597def _set_initial_state (
582- petab_problem ,
583- condition_id ,
584- element_id ,
585- init_par_id ,
586- par_map ,
587- scale_map ,
588- value ,
589- ):
598+ petab_problem : petab .Problem ,
599+ condition_id : str ,
600+ element_id : str ,
601+ init_par_id : str ,
602+ par_map : petab .ParMappingDict ,
603+ scale_map : petab .ScaleMappingDict ,
604+ value : str | float ,
605+ fill_fixed_parameters : bool = True ,
606+ ) -> None :
607+ """
608+ Update the initial value for a model entity in the parameter mapping
609+ according to the PEtab conditions table.
610+
611+ :param petab_problem: The PEtab problem
612+ :param condition_id: The current condition ID
613+ :param element_id: Element for which to set the initial value
614+ :param init_par_id: The parameter ID that refers to the initial value
615+ :param par_map: Parameter value mapping
616+ :param scale_map: Parameter scale mapping
617+ :param value: The initial value for `element_id` in `condition_id`
618+ :param fill_fixed_parameters:
619+ Whether to fill in nominal values for fixed parameters
620+ (estimate=0 in the parameters table).
621+ """
590622 value = petab .to_float_if_float (value )
623+ # NaN indicates that the initial value is to be taken from the model
624+ # (if this is the pre-equilibration condition, or the simulation condition
625+ # when no pre-equilibration condition is set) or is not to be reset
626+ # (if this is the simulation condition following pre-equilibration)-
627+ # The latter is not handled here.
591628 if pd .isna (value ):
592629 if petab_problem .model .type_id == MODEL_TYPE_SBML :
593630 value = _get_initial_state_sbml (petab_problem , element_id )
594631 elif petab_problem .model .type_id == MODEL_TYPE_PYSB :
595632 value = _get_initial_state_pysb (petab_problem , element_id )
596-
633+ else :
634+ raise NotImplementedError (
635+ f"Model type { petab_problem .model .type_id } not supported."
636+ )
637+ # the initial value can be a numeric value or a sympy expression
597638 try :
598639 value = float (value )
599640 except (ValueError , TypeError ):
@@ -614,14 +655,24 @@ def _set_initial_state(
614655 f"defined for the condition { condition_id } in "
615656 "the PEtab conditions table. The initial value is "
616657 f"now set to { value } , which is the initial value "
617- "defined in the SBML model."
658+ "defined in the original model."
618659 )
660+
619661 par_map [init_par_id ] = value
620662 if isinstance (value , float ):
621663 # numeric initial state
622664 scale_map [init_par_id ] = petab .LIN
623665 else :
624666 # parametric initial state
667+ if (
668+ fill_fixed_parameters
669+ and petab_problem .parameter_df is not None
670+ and value in petab_problem .parameter_df .index
671+ and petab_problem .parameter_df .loc [value , ESTIMATE ] == 0
672+ ):
673+ par_map [init_par_id ] = petab_problem .parameter_df .loc [
674+ value , NOMINAL_VALUE
675+ ]
625676 scale_map [init_par_id ] = petab_problem .parameter_df [
626677 PARAMETER_SCALE
627678 ].get (value , petab .LIN )
@@ -638,7 +689,7 @@ def _subset_dict(
638689 Collections of keys to be contained in the different subsets
639690
640691 :return:
641- subsetted dictionary
692+ Subsetted dictionary
642693 """
643694 for keys in args :
644695 yield {key : val for (key , val ) in full .items () if key in keys }
@@ -647,6 +698,11 @@ def _subset_dict(
647698def _get_initial_state_sbml (
648699 petab_problem : petab .Problem , element_id : str
649700) -> float | sp .Basic :
701+ """Get the initial value of an SBML model entity.
702+
703+ Get the initial value of an SBML model entity (species, parameter, ...) as
704+ defined in the model (not considering any condition table overrides).
705+ """
650706 import libsbml
651707
652708 element = petab_problem .sbml_model .getElementBySId (element_id )
@@ -688,9 +744,15 @@ def _get_initial_state_sbml(
688744def _get_initial_state_pysb (
689745 petab_problem : petab .Problem , element_id : str
690746) -> float | sp .Symbol :
747+ """Get the initial value of a PySB model entity.
748+
749+ Get the initial value of an PySB model entity as defined in the model
750+ (not considering any condition table overrides).
751+ """
752+ from pysb .pattern import match_complex_pattern
753+
691754 species_idx = int (re .match (r"__s(\d+)$" , element_id )[1 ])
692755 species_pattern = petab_problem .model .model .species [species_idx ]
693- from pysb .pattern import match_complex_pattern
694756
695757 value = next (
696758 (
0 commit comments