From e9da62a3c9630aad8ac6fc267f13c2083578cd66 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Mon, 4 Mar 2024 21:04:13 +0100 Subject: [PATCH] Fix petab import: initial assignment targets as constant parameters During PEtab import, parameters that are targets of initial assignments have so far not been turned into constant parameters, because they didn't exist in the amici model (see #2304). Now that those parameters remain in the model, they should be turned into constant parameters, unless specified otherwise. --- python/sdist/amici/petab/sbml_import.py | 27 +++++++++++++++++--- python/sdist/amici/sbml_import.py | 2 +- python/sdist/setup.cfg | 2 +- python/tests/test_petab_import.py | 34 ++++++++++++++++++++++--- 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/python/sdist/amici/petab/sbml_import.py b/python/sdist/amici/petab/sbml_import.py index 6388d6f8b0..725cc8c2f6 100644 --- a/python/sdist/amici/petab/sbml_import.py +++ b/python/sdist/amici/petab/sbml_import.py @@ -518,14 +518,33 @@ def _get_fixed_parameters_sbml( petab_problem, non_estimated_parameters_as_constants ) - # exclude targets of rules or initial assignments + # exclude targets of rules or initial assignments that are not numbers sbml_model = petab_problem.model.sbml_model + parser_settings = libsbml.L3ParserSettings( + sbml_model, + libsbml.L3P_PARSE_LOG_AS_LOG10, + libsbml.L3P_EXPAND_UNARY_MINUS, + libsbml.L3P_NO_UNITS, + libsbml.L3P_AVOGADRO_IS_CSYMBOL, + libsbml.L3P_COMPARE_BUILTINS_CASE_INSENSITIVE, + None, + libsbml.L3P_MODULO_IS_PIECEWISE, + ) + for fixed_parameter in fixed_parameters.copy(): # check global parameters - if sbml_model.getInitialAssignmentBySymbol( - fixed_parameter - ) or sbml_model.getRuleByVariable(fixed_parameter): + if sbml_model.getRuleByVariable(fixed_parameter): fixed_parameters.remove(fixed_parameter) + continue + if ia := sbml_model.getInitialAssignmentBySymbol(fixed_parameter): + sym_math = sp.sympify( + libsbml.formulaToL3StringWithSettings( + ia.getMath(), parser_settings + ) + ) + if not sym_math.is_Number: + fixed_parameters.remove(fixed_parameter) + continue return list(sorted(fixed_parameters)) diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 55d6dad903..c3f557c9b1 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -187,7 +187,7 @@ def __init__( self._reset_symbols() - # http://sbml.org/Software/libSBML/5.18.0/docs/python-api/classlibsbml_1_1_l3_parser_settings.html#abcfedd34efd3cae2081ba8f42ea43f52 + # https://sbml.org/software/libsbml/5.18.0/docs/formatted/python-api/classlibsbml_1_1_l3_parser_settings.html#ab30d7ed52ca24cbb842d0a7fed7f4bfd # all defaults except disable unit parsing self.sbml_parser_settings = sbml.L3ParserSettings( self.sbml, diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg index 0d27a0918e..d34d42f98f 100644 --- a/python/sdist/setup.cfg +++ b/python/sdist/setup.cfg @@ -47,7 +47,7 @@ zip_safe = False # Don't include any URLs here - they are not supported by PyPI: # HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/ # Invalid value for requires_dist. Error: Can't have direct dependency: ... -petab = petab>=0.2.1 +petab = petab>=0.2.9 pysb = pysb>=1.13.1 test = benchmark_models_petab @ git+https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab.git@master#subdirectory=src/python diff --git a/python/tests/test_petab_import.py b/python/tests/test_petab_import.py index 7a476f272d..fca319e11f 100644 --- a/python/tests/test_petab_import.py +++ b/python/tests/test_petab_import.py @@ -36,8 +36,30 @@ def simple_sbml_model(): return document, model +@pytest.fixture() +def get_fixed_parameters_model(): + """Create test SBML model for test_get_fixed_parameters""" + ant_model = """ + p1 = 1 + p2 = 2 + p3 = 3 + p4 = 4 + p5 = 5 + p6 = 3^2 + p7 = p6 + p8 = 8 + p8' = 1 + p9 := p8 + """ + from amici.antimony_import import antimony2sbml + + sbml_str = antimony2sbml(ant_model) + sbml_doc = libsbml.SBMLReader().readSBMLFromString(sbml_str) + return sbml_doc, sbml_doc.getModel() + + @skip_on_valgrind -def test_get_fixed_parameters(simple_sbml_model): +def test_get_fixed_parameters(get_fixed_parameters_model): """Check for correct identification of fixed parameters: p1: fixed (via condition table) @@ -45,13 +67,18 @@ def test_get_fixed_parameters(simple_sbml_model): p3: fixed (via parameter table `estimate=0`) p4: not fixed (via parameter table `estimate=1`) p5: fixed (implicitly, because not listed as estimated) + p6: fixed (implicitly, because not listed as estimated + initial assignment is a number) + p7: not fixed (initial assignment is not a number) + p8: not fixed (rate rule target) + p9: not fixed (assignment rule target) """ from amici.petab.sbml_import import ( _get_fixed_parameters_sbml as get_fixed_parameters, ) from petab.models.sbml_model import SbmlModel - sbml_doc, sbml_model = simple_sbml_model + sbml_doc, sbml_model = get_fixed_parameters_model condition_df = petab.get_condition_df( pd.DataFrame( { @@ -77,13 +104,14 @@ def test_get_fixed_parameters(simple_sbml_model): "p1", "p3", "p5", + "p6", } assert set( get_fixed_parameters( petab_problem, non_estimated_parameters_as_constants=False ) - ) == {"p1", "p5"} + ) == {"p1", "p5", "p6"} @skip_on_valgrind