def _set_initial_concentration(condition_id, species_id, init_par_id, par_map, scale_map): value = petab.to_float_if_float( self.petab_problem.condition_df.loc[ condition_id, species_id]) if isinstance(value, float): # numeric initial state par_map[init_par_id] = value scale_map[init_par_id] = ptc.LIN else: # parametric initial state try: # try find in mapping par_map[init_par_id] = par_map[value] scale_map[init_par_id] = scale_map[value] except KeyError: # otherwise look up in parameter table if (self.petab_problem.parameter_df.loc[ value, ptc.ESTIMATE] == 0): par_map[init_par_id] = \ self.petab_problem.parameter_df.loc[ value, ptc.NOMINAL_VALUE] else: par_map[init_par_id] = value if (ptc.PARAMETER_SCALE not in self.petab_problem.parameter_df or not self.petab_problem.parameter_df.loc[ value, ptc.PARAMETER_SCALE]): scale_map[init_par_id] = ptc.LIN else: scale_map[init_par_id] = \ self.petab_problem.parameter_df.loc[ value, ptc.PARAMETER_SCALE]
def write_measurements(self): """ Write measurements to hdf5 dataset """ # create inverse mapping for faster lookup observable_id_to_index = { name: idx for idx, name in enumerate(self.observable_ids)} measurement_df = self.petab_problem.measurement_df if ptc.NOISE_PARAMETERS not in measurement_df: measurement_df[ptc.NOISE_PARAMETERS] = np.nan for sim_idx, (preeq_cond_idx, sim_cond_idx) \ in enumerate(self.condition_map): # print("Condition", sim_idx, (preeq_cond_idx, sim_cond_idx)) cur_mes_df = self._get_measurements_for_condition( sim_idx, preeq_cond_idx, sim_cond_idx) # get the required, possibly non-unique (replicates) timepoints grp = cur_mes_df.groupby([ptc.OBSERVABLE_ID, ptc.TIME]).size() \ .reset_index().rename(columns={0: 'sum'}) \ .groupby([ptc.TIME])['sum'].agg(['max']).reset_index() timepoints = np.sort(np.repeat(grp[ptc.TIME], grp['max'])).tolist() mes = np.full(shape=(len(timepoints), self.ny), fill_value=np.nan) sd = mes.copy() # write measurements from each row in measurementDf for index, row in cur_mes_df.iterrows(): observable_idx = observable_id_to_index[row[ptc.OBSERVABLE_ID]] time_idx = timepoints.index(row.time) while not np.isnan(mes[time_idx, observable_idx]): time_idx += 1 if timepoints[time_idx] != row.time: raise AssertionError( "Replicate handling failed for " f'{row[ptc.SIMULATION_CONDITION_ID]} - ' f'{row[ptc.OBSERVABLE_ID]}' f' time {row[ptc.TIME]}\n' + str(cur_mes_df)) mes[time_idx, observable_idx] = float(row[ptc.MEASUREMENT]) sigma = to_float_if_float(row[ptc.NOISE_PARAMETERS]) if isinstance(sigma, float): sd[time_idx, observable_idx] = sigma # write to file g = self.f.require_group("measurements") g.create_dataset(name=f"y/{sim_idx}", data=mes, dtype='f8', compression=self.compression) g.create_dataset(name=f"ysigma/{sim_idx}", data=sd, dtype='f8', compression=self.compression) g.create_dataset(name=f"t/{sim_idx}", data=timepoints, dtype='f8', compression=self.compression)
def _set_initial_concentration(condition_id, species_id, init_par_id, par_map, scale_map): value = petab.to_float_if_float( petab_problem.condition_df.loc[condition_id, species_id]) par_map[init_par_id] = value if isinstance(value, float): # numeric initial state scale_map[init_par_id] = petab.LIN else: # parametric initial state scale_map[init_par_id] = petab_problem.parameter_df.loc[ value, PARAMETER_SCALE]
def _set_initial_concentration(condition_id, species_id, init_par_id, par_map, scale_map): value = petab.to_float_if_float( petab_problem.condition_df.loc[condition_id, species_id]) if pd.isna(value): value = float( get_species_initial( petab_problem.sbml_model.getSpecies(species_id))) logger.debug( f'The species {species_id} has no initial value ' f'defined for the condition {condition_id} in ' 'the PEtab conditions table. The initial value is ' f'now set to {value}, which is the initial value ' 'defined in the SBML model.') par_map[init_par_id] = value if isinstance(value, float): # numeric initial state scale_map[init_par_id] = petab.LIN else: # parametric initial state scale_map[init_par_id] = petab_problem.parameter_df.loc[ value, PARAMETER_SCALE]
def _generate_simulation_to_optimization_parameter_mapping(self) -> None: """ Create dataset n_parameters_simulation x n_conditions with indices of respective parameters in parameters_optimization """ # get list of tuple of parameters dicts for all conditions self.parameter_mapping = self.petab_problem \ .get_optimization_to_simulation_parameter_mapping( warn_unmapped=False, scaled_parameters=False) variable_par_ids = self.amici_model.getParameterIds() fixed_par_ids = self.amici_model.getFixedParameterIds() # Translate parameter ID mapping to index mapping # create inverse mapping for faster lookup print(self.problem_parameter_ids) optimization_parameter_name_to_index = { name: idx for idx, name in enumerate(self.problem_parameter_ids)} self.optimization_parameter_name_to_index = \ optimization_parameter_name_to_index # use in-memory matrix, don't write every entry to file directly num_model_parameters = self.amici_model.np() mapping_matrix = np.zeros( shape=(num_model_parameters, self.condition_map.shape[0]), dtype='<i4') override_matrix = np.full(shape=mapping_matrix.shape, fill_value=np.nan) pscale_matrix = np.full(shape=mapping_matrix.shape, dtype='<i4', fill_value=amici.ParameterScaling_none) # AMICI fixed parameters self.nk = len(fixed_par_ids) print(Fore.CYAN + "Number of fixed parameters:", len(fixed_par_ids)) # TODO: can fixed parameter differ between same condition vector used # in different sim x preeq combinations? fixed_parameter_matrix = np.full( shape=(self.nk, self.num_condition_vectors), fill_value=np.nan) # For handling initial states species_in_condition_table = [ col for col in self.petab_problem.condition_df if self.petab_problem.sbml_model.getSpecies(col) is not None] # Merge and preeq and sim parameters, filter fixed parameters for condition_idx, \ (condition_map_preeq, condition_map_sim, condition_scale_map_preeq, condition_scale_map_sim) \ in enumerate(self.parameter_mapping): preeq_cond_idx, sim_cond_idx = self.condition_map[condition_idx] preeq_cond_id = self.condition_ids[preeq_cond_idx] \ if preeq_cond_idx != NO_PREEQ_CONDITION_IDX else None sim_cond_id = self.condition_ids[sim_cond_idx] print(preeq_cond_idx, preeq_cond_id, sim_cond_idx, sim_cond_id) if len(condition_map_preeq) != len(condition_scale_map_preeq) \ or len(condition_map_sim) != len(condition_scale_map_sim): raise AssertionError( "Number of parameters and number of parameter " "scales do not match.") if len(condition_map_preeq) \ and len(condition_map_preeq) != len(condition_map_sim): # logger.debug( # f"Preequilibration parameter map: {condition_map_preeq}") # logger.debug(f"Simulation parameter map: {condition_map_sim}") raise AssertionError( "Number of parameters for preequilibration " "and simulation do not match.") # TODO: requires special handling of initial concentrations if species_in_condition_table: # set indicator fixed parameter for preeq # (we expect here, that this parameter was added during AMICI # model import and that it was not added by the user with a # different meaning...) if preeq_cond_idx != NO_PREEQ_CONDITION_IDX: condition_map_preeq[PREEQ_INDICATOR_ID] = 1.0 condition_scale_map_preeq[PREEQ_INDICATOR_ID] = ptc.LIN condition_map_sim[PREEQ_INDICATOR_ID] = 0.0 condition_scale_map_sim[PREEQ_INDICATOR_ID] = ptc.LIN def _set_initial_concentration(condition_id, species_id, init_par_id, par_map, scale_map): value = petab.to_float_if_float( self.petab_problem.condition_df.loc[ condition_id, species_id]) if isinstance(value, float): # numeric initial state par_map[init_par_id] = value scale_map[init_par_id] = ptc.LIN else: # parametric initial state try: # try find in mapping par_map[init_par_id] = par_map[value] scale_map[init_par_id] = scale_map[value] except KeyError: # otherwise look up in parameter table if (self.petab_problem.parameter_df.loc[ value, ptc.ESTIMATE] == 0): par_map[init_par_id] = \ self.petab_problem.parameter_df.loc[ value, ptc.NOMINAL_VALUE] else: par_map[init_par_id] = value if (ptc.PARAMETER_SCALE not in self.petab_problem.parameter_df or not self.petab_problem.parameter_df.loc[ value, ptc.PARAMETER_SCALE]): scale_map[init_par_id] = ptc.LIN else: scale_map[init_par_id] = \ self.petab_problem.parameter_df.loc[ value, ptc.PARAMETER_SCALE] for species_id in species_in_condition_table: # for preequilibration init_par_id = f'initial_{species_id}_preeq' # need to set dummy value for preeq parameter anyways, as it # is expected below (set to 0, not nan, because will be # multiplied with indicator variable in initial assignment) condition_map_sim[init_par_id] = 0.0 condition_scale_map_sim[init_par_id] = ptc.LIN if preeq_cond_idx != NO_PREEQ_CONDITION_IDX: _set_initial_concentration( preeq_cond_id, species_id, init_par_id, condition_map_preeq, condition_scale_map_preeq) # enable state reinitialization self.f['/fixedParameters/simulationConditions'][condition_idx, 2] = 1 # for simulation init_par_id = f'initial_{species_id}_sim' _set_initial_concentration( sim_cond_id, species_id, init_par_id, condition_map_sim, condition_scale_map_sim) print(condition_map_preeq, condition_map_sim) # split into fixed and variable parameters: condition_map_preeq_var = condition_scale_map_preeq_var = None condition_map_preeq_fix = None if condition_map_preeq: condition_map_preeq_var, condition_map_preeq_fix = \ subset_dict(condition_map_preeq, variable_par_ids, fixed_par_ids) condition_scale_map_preeq_var, _ = \ subset_dict(condition_scale_map_preeq, variable_par_ids, fixed_par_ids) condition_map_sim_var, condition_map_sim_fix = \ subset_dict(condition_map_sim, variable_par_ids, fixed_par_ids) condition_scale_map_sim_var, condition_scale_map_sim_fix = \ subset_dict(condition_scale_map_sim, variable_par_ids, fixed_par_ids) if condition_map_preeq: # merge after having removed potentially fixed parameters # which may differ between preequilibration and simulation petab.merge_preeq_and_sim_pars_condition( condition_map_preeq_var, condition_map_sim_var, condition_scale_map_preeq_var, condition_scale_map_sim_var, condition_idx) print(self.problem_parameter_ids) # mapping for each model parameter for model_parameter_idx, model_parameter_id \ in enumerate(variable_par_ids): mapped_parameter = condition_map_sim[model_parameter_id] mapped_parameter = to_float_if_float(mapped_parameter) try: (mapped_idx, override) = self.get_index_mapping_for_par( mapped_parameter, optimization_parameter_name_to_index) mapping_matrix[model_parameter_idx, condition_idx] = \ mapped_idx override_matrix[model_parameter_idx, condition_idx] = \ override except IndexError as e: print(Fore.RED + "Error in parameter mapping:", e) print(model_parameter_idx, mapped_parameter) print(self.parameter_mapping) raise e # Set parameter scales for simulation pscale_matrix[:, condition_idx] = list( petab_scale_to_amici_scale(condition_scale_map_sim[amici_id]) for amici_id in variable_par_ids) fixed_parameter_matrix[:, self.condition_map[condition_idx, 1]] = \ np.array([condition_map_sim_fix[par_id] for par_id in fixed_par_ids]) if condition_map_preeq: fixed_parameter_matrix[:, self.condition_map[condition_idx, 0]] = \ np.array([condition_map_preeq_fix[par_id] for par_id in fixed_par_ids]) # write to file self.create_fixed_parameter_dataset_and_write_attributes( fixed_par_ids, fixed_parameter_matrix) self.f.require_dataset('/parameters/pscaleSimulation', shape=pscale_matrix.T.shape, dtype="<i4", data=pscale_matrix.T) write_parameter_map(self.f, mapping_matrix, override_matrix, num_model_parameters, self.compression) # for cost function parameters petab_opt_par_scale = \ self.petab_problem.get_optimization_parameter_scales() pscale_opt_par = np.array( list(petab_scale_to_amici_scale(petab_opt_par_scale[par_id]) for par_id in self.problem_parameter_ids)) self.f.require_dataset('/parameters/pscaleOptimization', shape=pscale_opt_par.shape, dtype="<i4", data=pscale_opt_par) self.f.flush()
def get_model_for_condition( petab_problem: "petab.Problem", sim_condition_id: str = None, preeq_condition_id: Optional[str] = None, ) -> Tuple[libsbml.SBMLDocument, libsbml.Model]: """Create an SBML model for the given condition. Creates a copy of the model and updates parameters according to the PEtab files. Estimated parameters are set to their ``nominalValue``. Observables defined in the observables table are not added to the model. :param petab_problem: PEtab problem :param sim_condition_id: Simulation ``conditionId`` for which to generate a model :param preeq_condition_id: Preequilibration ``conditionId`` of the settings for which to generate a model. This is only used to determine the relevant output parameter overrides. Preequilibration is not encoded in the resulting model. :return: The generated SBML document, and SBML model """ condition_dict = {petab.SIMULATION_CONDITION_ID: sim_condition_id} if preeq_condition_id: condition_dict[petab.PREEQUILIBRATION_CONDITION_ID] = \ preeq_condition_id cur_measurement_df = petab.measurements.get_rows_for_condition( measurement_df=petab_problem.measurement_df, condition=condition_dict, ) parameter_map, scale_map = \ petab.parameter_mapping.get_parameter_mapping_for_condition( condition_id=sim_condition_id, is_preeq=False, cur_measurement_df=cur_measurement_df, sbml_model=petab_problem.sbml_model, condition_df=petab_problem.condition_df, parameter_df=petab_problem.parameter_df, warn_unmapped=True, scaled_parameters=False, fill_fixed_parameters=True, # will only become problematic once the observable and noise terms # are added to the model allow_timepoint_specific_numeric_noise_parameters=True, ) # create a copy of the model sbml_doc = petab_problem.sbml_model.getSBMLDocument().clone() sbml_model = sbml_doc.getModel() # fill in parameters def get_param_value(parameter_id: str): """Parameter value from mapping or nominal value""" mapped_value = parameter_map.get(parameter_id) if not isinstance(mapped_value, str): return mapped_value # estimated parameter, look up in nominal parameters return petab_problem.parameter_df.loc[mapped_value, petab.NOMINAL_VALUE] def remove_rules(target_id: str): if sbml_model.removeRuleByVariable(target_id): warn("An SBML rule was removed to set the component " f"{target_id} to a constant value.") sbml_model.removeInitialAssignment(target_id) for parameter in sbml_model.getListOfParameters(): new_value = get_param_value(parameter.getId()) if new_value: parameter.setValue(new_value) # remove rules that would override that value remove_rules(parameter.getId()) # set concentrations for any overridden species for component_id in petab_problem.condition_df: sbml_species = sbml_model.getSpecies(component_id) if not sbml_species: continue # remove any rules overriding that species' initials remove_rules(component_id) # set initial concentration/amount new_value = petab.to_float_if_float( petab_problem.condition_df.loc[sim_condition_id, component_id]) if not isinstance(new_value, Number): # parameter reference in condition table new_value = get_param_value(new_value) if sbml_species.isSetInitialAmount() \ or (sbml_species.getHasOnlySubstanceUnits() and not sbml_species.isSetInitialConcentration()): sbml_species.setInitialAmount(new_value) else: sbml_species.setInitialConcentration(new_value) # set compartment size for any compartments in the condition table for component_id in petab_problem.condition_df: sbml_compartment = sbml_model.getCompartment(component_id) if not sbml_compartment: continue # remove any rules overriding that compartment's size remove_rules(component_id) # set initial concentration/amount new_value = petab.to_float_if_float( petab_problem.condition_df.loc[sim_condition_id, component_id]) if not isinstance(new_value, Number): # parameter reference in condition table new_value = get_param_value(new_value) sbml_compartment.setSize(new_value) return sbml_doc, sbml_model