From 0ea64d1b6badd9abc82e12ed3a478b1a6f6e4f43 Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Wed, 23 Oct 2024 15:56:47 -0400 Subject: [PATCH 01/10] Fix typo --- mira/sources/sbml/processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mira/sources/sbml/processor.py b/mira/sources/sbml/processor.py index fe877d5fc..f90c178d0 100644 --- a/mira/sources/sbml/processor.py +++ b/mira/sources/sbml/processor.py @@ -63,7 +63,7 @@ def _lookup_concepts_filtered(species_ids) -> List[Concept]: and "cumulative" not in species_id ] - # Iterate thorugh all reactions and piecewise convert to templates + # Iterate through all reactions and piecewise convert to templates templates: List[Template] = [] # see docs on reactions # https://sbml.org/software/libsbml/5.18.0/docs/formatted/python-api/ From 92c07250e0d9c686f27145946271f73c50562c5f Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 00:34:39 -0400 Subject: [PATCH 02/10] Implemen getting distributions --- mira/sources/sbml/processor.py | 54 +++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/mira/sources/sbml/processor.py b/mira/sources/sbml/processor.py index f90c178d0..b3518de0f 100644 --- a/mira/sources/sbml/processor.py +++ b/mira/sources/sbml/processor.py @@ -7,6 +7,8 @@ import copy import math +import libsbml +from sbmlmath import SBMLMathMLParser from typing import Dict, Iterable, List, Mapping, Tuple from mira.sources.sbml.utils import * @@ -75,6 +77,7 @@ def _lookup_concepts_filtered(species_ids) -> List[Concept]: "value": parameter.value, "description": parameter.name, "units": self.get_object_units(parameter), + "distribution": get_distribution(parameter), } for parameter in self.sbml_model.parameters } @@ -171,10 +174,12 @@ def _lookup_concepts_filtered(species_ids) -> List[Concept]: # Some rate laws define parameters locally and so we need to # extract them and add them to the global parameter list for parameter in rate_law.parameters: + param_dist = get_distribution(parameter) all_parameters[parameter.id] = { "value": parameter.value, "description": parameter.name if parameter.name else None, "units": self.get_object_units(parameter), + "distribution": param_dist, } parameter_symbols[parameter.id] = sympy.Symbol(parameter.id) @@ -355,10 +360,30 @@ def _lookup_concepts_filtered(species_ids) -> List[Concept]: init_value = species.initial_concentration else: init_value = 0.0 - initials[key] = Initial( - concept=concepts[key], - expression=SympyExprStr(sympy.Float(init_value)), - ) + initial_distr = get_distribution(species) + # If we have an initial distribution, do the following + # - introduce a new parameter with the concept name + _init + # - set the initial expression to this parameter as a symbol + # - add the distribution to the parameter + if initial_distr: + init_param_name = f"{key}_init" + all_parameters[init_param_name] = { + "value": init_value, + "description": f"Initial value for {key}", + "units": self.get_object_units(species), + "distribution": initial_distr, + } + parameter_symbols[init_param_name] = sympy.Symbol(init_param_name) + initial_expr = sympy.Symbol(init_param_name) + initials[key] = Initial( + concept=concepts[key], + expression=initial_expr, + ) + else: + initials[key] = Initial( + concept=concepts[key], + expression=SympyExprStr(sympy.Float(init_value)), + ) param_objs = { k: Parameter( @@ -366,6 +391,7 @@ def _lookup_concepts_filtered(species_ids) -> List[Concept]: value=v["value"], description=v["description"], units=v["units"], + distribution=v.get("distribution"), ) for k, v in all_parameters.items() } @@ -777,3 +803,23 @@ def _extract_all_copasi_attrib( assert value != "{}" resources.append((key, value)) return resources + + +def get_distribution(obj): + distr_tag = obj.getPlugin("distrib") + if distr_tag: + for uncertainty in distr_tag.getListOfUncertainties(): + for param_uncertainty in uncertainty.getListOfUncertParameters(): + mathml_str = libsbml.writeMathMLToString(param_uncertainty.getMath()) + expr = SBMLMathMLParser().parse_str(mathml_str) + if expr.func.name == 'uniform': + distr = Distribution( + type='Uniform1', + parameters={ + 'minimum': expr.args[0], + 'maximum': expr.args[1], + } + + ) + return distr + return None From 624c726b2629b2fd60d137e6f9c0984d03b9588a Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 08:27:05 -0400 Subject: [PATCH 03/10] Install sbmlmath separately --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5090847ef..49e6d26c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,6 +19,7 @@ jobs: run: | sudo apt-get install graphviz libgraphviz-dev pip install --upgrade pip setuptools wheel + pip install --ignore-requires-python sbmlmath pip install "tox<4.0.0" - name: Test with pytest run: | From f62493c61c5c0a4eee0a103a097c89cff4c332cc Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 08:35:13 -0400 Subject: [PATCH 04/10] Extend tox.ini for ignore python version --- .github/workflows/tests.yml | 1 - tox.ini | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 49e6d26c6..5090847ef 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,7 +19,6 @@ jobs: run: | sudo apt-get install graphviz libgraphviz-dev pip install --upgrade pip setuptools wheel - pip install --ignore-requires-python sbmlmath pip install "tox<4.0.0" - name: Test with pytest run: | diff --git a/tox.ini b/tox.ini index 6dbbbce7c..f6018dc71 100644 --- a/tox.ini +++ b/tox.ini @@ -21,6 +21,9 @@ commands = coverage run -p -m pytest --durations=20 {posargs:tests} -m "not slow" ; coverage combine ; coverage xml +commands_pre = + pip install --ignore-requires-python . + [testenv:docs] extras = docs From 51ecd637fa538a91cc7afc39c851c6bf5cce3a36 Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 08:38:19 -0400 Subject: [PATCH 05/10] Add sbmlmath as tox dependency --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index f6018dc71..351aa3104 100644 --- a/tox.ini +++ b/tox.ini @@ -17,6 +17,7 @@ extras = dkg-construct deps = anyio<4 + sbmlmath commands = coverage run -p -m pytest --durations=20 {posargs:tests} -m "not slow" ; coverage combine From b84c45d7ce21babf35b49d6a5be2a738c86dda4e Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 08:41:14 -0400 Subject: [PATCH 06/10] Try just installing sbmlmath as pre --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 351aa3104..3e22ca222 100644 --- a/tox.ini +++ b/tox.ini @@ -17,13 +17,12 @@ extras = dkg-construct deps = anyio<4 - sbmlmath commands = coverage run -p -m pytest --durations=20 {posargs:tests} -m "not slow" ; coverage combine ; coverage xml commands_pre = - pip install --ignore-requires-python . + pip install --ignore-requires-python sbmlmath [testenv:docs] extras = From 34b53a02662115f04a8b436496b76a914a19e0d5 Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 08:47:55 -0400 Subject: [PATCH 07/10] Bump Python versions --- .github/workflows/tests.yml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5090847ef..aa4c509bf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ "3.8", "3.10" ] + python-version: [ "3.9", "3.12" ] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/setup.cfg b/setup.cfg index 2bdac783e..ea94b3282 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,7 +26,7 @@ install_requires = zip_safe = false include_package_data = True -python_requires = >=3.8 +python_requires = >=3.9 # Where is my code packages = find: From 985d77a91a932ed044c556e9f9142f2904f2637b Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 08:54:40 -0400 Subject: [PATCH 08/10] Add test for distribution from SBML --- tests/ABCD_model.xml | 312 +++++++++++++++++++++++++++++++++++++++++++ tests/test_sbml.py | 14 +- 2 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 tests/ABCD_model.xml diff --git a/tests/ABCD_model.xml b/tests/ABCD_model.xml new file mode 100644 index 000000000..1a286cfaa --- /dev/null +++ b/tests/ABCD_model.xml @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + uniform + 0 + 10 + + + + + + + + + + + + + uniform + 0 + 10 + + + + + + + + + + + + + uniform + 0 + 10 + + + + + + + + + + + + + uniform + 0 + 10 + + + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + uniform + 0.1 + 3 + + + + + + + + + + + + + + + + + + + + + + 1 + + + GeneA + n_GeneA_to_GeneB + + + + + + + K_GeneA_to_GeneB + n_GeneA_to_GeneB + + + + GeneA + n_GeneA_to_GeneB + + + + + + + + + + + + + + + + + + + + 1 + + + GeneB + n_GeneB_to_GeneD + + + + + + + K_GeneB_to_GeneD + n_GeneB_to_GeneD + + + + GeneB + n_GeneB_to_GeneD + + + + + + + + + + + + + + + + + + 1 + + + 1 + + + + + GeneC + n_GeneC_to_GeneB + + 1 + + + + + + + + + diff --git a/tests/test_sbml.py b/tests/test_sbml.py index 9d3102642..d8d585d1a 100644 --- a/tests/test_sbml.py +++ b/tests/test_sbml.py @@ -1,7 +1,9 @@ +import os import sympy from mira.sources.sbml.processor import parse_assignment_rule, \ process_unit_definition +from mira.sources.sbml import template_model_from_sbml_file def test_parse_expr(): @@ -31,4 +33,14 @@ def __init__(self, units): day = MockUnit(28, 86400, -1, 0) person = MockUnit(12, 1, -1, 0) res = process_unit_definition(MockUnitDefinition([day, person])) - assert res == 1 / (sympy.Symbol('day') * sympy.Symbol('person')) \ No newline at end of file + assert res == 1 / (sympy.Symbol('day') * sympy.Symbol('person')) + + +def test_distr_processing(): + HERE = os.path.dirname(os.path.abspath(__file__)) + model_file = os.path.join(HERE, 'ABCD_model.xml') + tm = template_model_from_sbml_file(model_file) + + for p, v in tm.parameters.items(): + assert v.distribution is not None + assert v.distribution.type == 'Uniform1' From e0daefa4e5a36e7fa9a36a4dd85bfda48e70b0f7 Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 08:58:37 -0400 Subject: [PATCH 09/10] Adjust test --- tests/test_sbml.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_sbml.py b/tests/test_sbml.py index d8d585d1a..c59e14165 100644 --- a/tests/test_sbml.py +++ b/tests/test_sbml.py @@ -42,5 +42,7 @@ def test_distr_processing(): tm = template_model_from_sbml_file(model_file) for p, v in tm.parameters.items(): + if 'compartment' in p: + continue assert v.distribution is not None assert v.distribution.type == 'Uniform1' From 6e1cd7a6d62d89382848b15e67bf08323b14fd14 Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Tue, 29 Oct 2024 09:08:31 -0400 Subject: [PATCH 10/10] Hide sbmlmath import to be as needed --- mira/sources/sbml/processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mira/sources/sbml/processor.py b/mira/sources/sbml/processor.py index b3518de0f..1996fc77b 100644 --- a/mira/sources/sbml/processor.py +++ b/mira/sources/sbml/processor.py @@ -8,7 +8,6 @@ import copy import math import libsbml -from sbmlmath import SBMLMathMLParser from typing import Dict, Iterable, List, Mapping, Tuple from mira.sources.sbml.utils import * @@ -808,6 +807,7 @@ def _extract_all_copasi_attrib( def get_distribution(obj): distr_tag = obj.getPlugin("distrib") if distr_tag: + from sbmlmath import SBMLMathMLParser for uncertainty in distr_tag.getListOfUncertainties(): for param_uncertainty in uncertainty.getListOfUncertParameters(): mathml_str = libsbml.writeMathMLToString(param_uncertainty.getMath())