Source code for petab_sciml.problem_utils.neural_ode

from pathlib import Path
from typing import Iterable

import pandas as pd
from libsbml import SBMLDocument, parseL3Formula, writeSBML
from ruamel.yaml import YAML

from petab_sciml.standard.nn_model import NNModelStandard


[docs] def create_neural_ode( species_all: Iterable[str] | dict, model_filename: str = "model.xml", network_name: str = "net1", save_directory: str | Path = ".", ) -> None: """Generate the PEtab files for a neural ODE problem with the given species. This function will generate the model, hybridization, mapping and parameter files. Args: species_all: List of species names to include in the model, or a dictionary mapping species names to their initial values. model_filename: Name of the SBML file to create. network_name: Name of the neural network to reference in the PEtab files. save_directory: Directory where the generated files will be saved. Returns: None """ save_directory = _ensure_directory(save_directory) model_path = save_directory / model_filename document = _write_sbml(species_all, model_path) _write_petab(document, network_name, save_directory)
def _write_sbml( species_all: Iterable[str] | dict, model_filename: Path ) -> SBMLDocument: """ Write the SBML model file with the given filename for a neural ODE PEtab problem and return the created SBML document. """ document = SBMLDocument(3, 1) model = document.createModel() c1 = model.createCompartment() c1.setConstant(True) c1.setSize(1) if isinstance(species_all, dict): species_items = species_all.items() else: species_items = ((species, 0.0) for species in species_all) for species, initial_amount in species_items: s = model.createSpecies() s.setId(species) s.setConstant(False) s.setInitialAmount(initial_amount) s.setBoundaryCondition(False) s.setHasOnlySubstanceUnits(True) param_name = species + "_param" k = model.createParameter() k.setId(param_name) k.setConstant(False) k.setValue(0.0) rule_id = species + "_reaction" r = model.createRateRule() r.setId(rule_id) r.setVariable(species) math_ast = parseL3Formula(param_name) r.setMath(math_ast) writeSBML(document, str(model_filename)) return document def _write_petab( document: SBMLDocument, network_name: str, save_directory: Path, ) -> None: """ Write the PEtab files for a neural ODE PEtab problem with the given SBML document and referencing the network provided. """ model = document.getModel() # hybridization species = [sp.id for sp in model.species] params = [param.id for param in model.parameters] target_ids = [f"{network_name}_input{s}" for s, _ in enumerate(species)] target_values = [f"{network_name}_output{s}" for s, _ in enumerate(params)] hybridization = { "targetId": target_ids + params, "targetValue": species + target_values, } pd.DataFrame(hybridization).to_csv( save_directory / "hybridization.tsv", sep="\t", index=False ) # mapping inputs = [f"{network_name}.inputs[0][{s}]" for s, _ in enumerate(target_ids)] outputs = [f"{network_name}.outputs[0][{s}]" for s, _ in enumerate(target_values)] mapping = { "petabEntityId": target_ids + target_values + [f"{network_name}_ps"], "modelEntityId": inputs + outputs + [f"{network_name}.parameters"], } pd.DataFrame(mapping).to_csv(save_directory / "mapping.tsv", sep="\t", index=False) # parameters parameters = { "parameterId": [f"{network_name}_ps"], "parameterScale": ["lin"], "lowerBound": ["-inf"], "upperBound": ["inf"], "nominalValue": [None], "estimate": [1], } pd.DataFrame(parameters).to_csv( save_directory / "parameters.tsv", sep="\t", index=False )
[docs] def create_neural_ode_problem( model_filename: str, measurements_filename: str, observables_filename: str, network_filename: str, array_filenames: Iterable[str], mapping_filenames: Iterable[str] = ["mapping.tsv"], parameters_filename: str = "parameters.tsv", hybridization_filenames: Iterable[str] = ["hybridization.tsv"], save_directory: str | Path = ".", ) -> None: """Write the PEtab files needed for the neural ODE PEtab problem. The mappings, parameters and hybridization files can be created using the create_neural_ode function. This function will create the conditions and problem.yaml files. The measurements and observables files need to be provided by the user. Args: model_filename: Name of the SBML file to be referenced in the PEtab problem. measurements_filename: Name of the measurements TSV file to be referenced in the PEtab problem. This file should be located in the same directory where the remaining PEtab files are to be generated. observables_filename: Name of the observables TSV file to be created. This file should be located in the same directory where the remaining PEtab files are to be generated. network_filename: Name of the neural network YAML file defining the network architecture. array_filenames: List of names of array files to be referenced in the PEtab problem. mapping_filenames: List of names of mapping files to be referenced in the PEtab problem. parameters_filename: Name of the parameters TSV file to be referenced in the PEtab problem. hybridization_filenames: List of names of hybridization files to be referenced in the PEtab problem. save_directory: Directory where the generated files will be saved. Returns: None """ save_directory = _ensure_directory(save_directory) measurements = pd.read_csv(save_directory / measurements_filename, sep="\t") condition_ids = measurements["simulationConditionId"].unique() # conditions conditions = {"conditionId": condition_ids} pd.DataFrame(conditions).to_csv( save_directory / "conditions.tsv", sep="\t", index=False ) # get network name from network yaml file network_name = NNModelStandard.load_data(save_directory / network_filename).nn_model_id # problem.yaml problem = { "format_version": "2.0.0", "model_files": { "model": { "location": model_filename, "language": "sbml", }, }, "measurement_files": [measurements_filename], "observable_files": [observables_filename], "parameter_files": [parameters_filename], "mapping_files": mapping_filenames, "extensions": { "sciml": { "version": "0.1.0", "required": True, "neural_nets": { network_name: { "location": network_filename, "pre_initialization": False, "format": "YAML", }, }, "array_files": array_filenames, "hybridization_files": hybridization_filenames, }, }, } with open((save_directory / "problem.yaml"), "w") as file: yaml = YAML() yaml.indent(mapping=2, sequence=4, offset=2) yaml.dump(problem, file)
def _ensure_directory(path: str | Path) -> Path: """Create a directory if needed and return it as a Path.""" directory = Path(path) directory.mkdir(parents=True, exist_ok=True) return directory