Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 2 additions & 23 deletions petab/v1/models/sbml_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import libsbml
import sympy as sp
from sympy.abc import _clash
from sbmlmath import sbml_math_to_sympy

from ..._utils import _generate_path
from ..sbml import (
Expand Down Expand Up @@ -285,28 +285,7 @@ def sympify_sbml(sbml_obj: libsbml.ASTNode | libsbml.SBase) -> sp.Expr:
-------
The sympy expression corresponding to ``sbml_obj``.
"""
ast_node = (
sbml_obj
if isinstance(sbml_obj, libsbml.ASTNode)
else sbml_obj.getMath()
)

parser_settings = libsbml.L3ParserSettings(
ast_node.getParentSBMLObject().getModel(),
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,
)

formula_str = libsbml.formulaToL3StringWithSettings(
ast_node, parser_settings
)

return sp.sympify(formula_str, locals=_clash)
return sbml_math_to_sympy(sbml_obj)


def antimony2sbml(ant_model: str | Path) -> str:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dependencies = [
"jsonschema",
"antlr4-python3-runtime==4.13.1",
"pydantic>=2.10",
"sbmlmath>=0.4.0",
]
license = "MIT"
authors = [
Expand Down Expand Up @@ -65,7 +66,6 @@ doc = [
"ipython>=7.21.0, !=8.7.0",
"pysb",
"antimony>=2.14.0",
"sbmlmath>=0.4.0",
]
vis = [
"matplotlib>=3.6.0",
Expand Down
21 changes: 20 additions & 1 deletion tests/v1/test_sbml.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pandas as pd
import pytest

from petab.v1.models.sbml_model import SbmlModel
from petab.v1.models.sbml_model import SbmlModel, sympify_sbml

sys.path.append(os.getcwd())
import petab
Expand Down Expand Up @@ -150,3 +150,22 @@ def test_sbml_from_to_ant():

# convert back to antimony
assert "R1: S1 -> S2; k1*S1" in petab_model.to_antimony()


def test_sympify_sbml_modulo():
"""SBML's ``%`` follows C semantics (result has the sign of the
dividend), unlike Python's ``%`` (result has the sign of the divisor).

Naively converting the SBML math to a string and handing it to
``sympy.sympify`` therefore silently produces a wrong result (gh-283).
"""
sbml_document = libsbml.SBMLDocument(3, 2)
sbml_model = sbml_document.createModel()
parameter = sbml_model.createParameter()
parameter.setId("e")
parameter.setConstant(False)
initial_assignment = sbml_model.createInitialAssignment()
initial_assignment.setSymbol("e")
initial_assignment.setMath(libsbml.parseL3Formula("-5 % 3"))

assert sympify_sbml(initial_assignment).doit() == -2
Comment on lines +155 to +171

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to sbmlmath?

@dweindl dweindl Aug 12, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be added there as well, but I think it makes sense to have it here. This tests exactly what changed in this PR. Would have failed before, passes now.