From 79ab9c74f63364b5c6da3b1780ef295e53b6021d Mon Sep 17 00:00:00 2001 From: Kwabena Amponsah Date: Wed, 15 Jul 2026 11:36:18 +0100 Subject: [PATCH 1/7] #376 Support sympy 1.13 and 1.14 --- RELEASE.md | 1 + pyproject.toml | 4 ++-- requirements/base.in | 2 +- requirements/base.txt | 2 +- requirements/dev.txt | 2 +- requirements/test.txt | 2 +- tests/test_transpiler.py | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 66c65a56..8be20590 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -2,6 +2,7 @@ - Migrated to Pint >= 0.24 (the `ScaleConverter` and `UnitDefinition` classes moved to `pint.facets.plain` and changed argument signatures). This reverses the `Pint < 0.20` pin introduced in 0.3.5 and restores compatibility with numpy 2.x, which older Pint versions could not import. - Added support for Python 3.14. - Dropped support for Python 3.9 as Pint >= 0.24 requires Python >= 3.10. +- Added support for sympy 1.13 and 1.14. # Release 0.3.7 - Added support for Python 3.12 and 3.13. diff --git a/pyproject.toml b/pyproject.toml index 7dc687b6..37306e51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,9 +27,9 @@ classifiers = [ dependencies = [ "lxml>=4.7", "networkx>=2.1", - "Pint>=0.24, <0.26", + "Pint>=0.24, <0.26", # Occasional breaking changes in minor versions "rdflib>=4", - "sympy>=1.4, <1.13", + "sympy>=1.10, <1.15", # Occasional breaking changes in minor versions ] dynamic = ["version"] diff --git a/requirements/base.in b/requirements/base.in index bfe658df..16c4a694 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -2,4 +2,4 @@ lxml>=4.7 pint>=0.24, <0.26 rdflib>=4 networkx>=2.1 -sympy>=1.4, <1.13 +sympy>=1.10, <1.15 diff --git a/requirements/base.txt b/requirements/base.txt index 3ec581cf..8d301a54 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -24,7 +24,7 @@ pyparsing==3.3.2 # via rdflib rdflib==7.6.0 # via -r base.in -sympy==1.12.1 +sympy==1.14.0 # via -r base.in typing-extensions==4.16.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 4eb5f097..7f661190 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -71,7 +71,7 @@ pytest-cov==7.1.0 # via -r test.in rdflib==7.6.0 # via -r base.in -sympy==1.12.1 +sympy==1.14.0 # via -r base.in tomli==2.4.1 # via diff --git a/requirements/test.txt b/requirements/test.txt index 63a91dd9..62a76433 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -54,7 +54,7 @@ pytest-cov==7.1.0 # via -r test.in rdflib==7.6.0 # via -r base.in -sympy==1.12.1 +sympy==1.14.0 # via -r base.in tomli==2.4.1 # via diff --git a/tests/test_transpiler.py b/tests/test_transpiler.py index 0670bb8b..6b5ea776 100644 --- a/tests/test_transpiler.py +++ b/tests/test_transpiler.py @@ -34,7 +34,7 @@ def test_symbol(self): def test_number(self): self.assert_equal('1', - [sympy.Number(1)]) + [sympy.Number(1.0)]) def test_ignore_comment(self): self.assert_equal('', []) From f01cd9624cacc142f7777a131ffc681d4b976031 Mon Sep 17 00:00:00 2001 From: Kwabena Amponsah Date: Wed, 15 Jul 2026 11:51:02 +0100 Subject: [PATCH 2/7] #376 Fix reciprocal singularity detection for sympy 1.13+ (Float/Integer equality change) --- cellmlmanip/_singularity_fixes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cellmlmanip/_singularity_fixes.py b/cellmlmanip/_singularity_fixes.py index 696dead1..a8e704d6 100644 --- a/cellmlmanip/_singularity_fixes.py +++ b/cellmlmanip/_singularity_fixes.py @@ -293,7 +293,7 @@ def _fix_expr_parts(expr, V, U_offset, exp_function): expr_parts.append(_generate_piecewise(ex, V, sp, Vmin, Vmax) if sp is not None else ex) return (None, None, None, Add(*expr_parts), is_piecewise) - elif isinstance(expr, Pow) and expr.args[1] == -1.0 and len(expr.args) == 2: # 1/A + elif isinstance(expr, Pow) and expr.args[1] in (-1, -1.0) and len(expr.args) == 2: # 1/A # Find singularities in A and adjust result to represent 1 / A Vmin, Vmax, sp, ex, has_piecewise = _fix_expr_parts(expr.args[0], V, U_offset, exp_function) has_piecewise = has_piecewise or Vmin is not None From 1d2e04d3fcba56a3cc8583d6ec28f817179735d3 Mon Sep 17 00:00:00 2001 From: Kwabena Amponsah Date: Wed, 15 Jul 2026 12:04:27 +0100 Subject: [PATCH 3/7] #376 Update sympy version release notes Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- RELEASE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index 8be20590..539aed38 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -2,7 +2,7 @@ - Migrated to Pint >= 0.24 (the `ScaleConverter` and `UnitDefinition` classes moved to `pint.facets.plain` and changed argument signatures). This reverses the `Pint < 0.20` pin introduced in 0.3.5 and restores compatibility with numpy 2.x, which older Pint versions could not import. - Added support for Python 3.14. - Dropped support for Python 3.9 as Pint >= 0.24 requires Python >= 3.10. -- Added support for sympy 1.13 and 1.14. +- Added support for sympy 1.13 and 1.14 (minimum supported sympy version is now 1.10). # Release 0.3.7 - Added support for Python 3.12 and 3.13. From f97eb27c30722e62384a070b62379d6f24284d6e Mon Sep 17 00:00:00 2001 From: Kwabena Amponsah Date: Wed, 15 Jul 2026 16:40:39 +0100 Subject: [PATCH 4/7] #376 Raise networkx and rdflib floors to versions that support Python 3.10 --- pyproject.toml | 4 ++-- requirements/base.in | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 37306e51..c7236a62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,9 +26,9 @@ classifiers = [ ] dependencies = [ "lxml>=4.7", - "networkx>=2.1", + "networkx>=2.7", "Pint>=0.24, <0.26", # Occasional breaking changes in minor versions - "rdflib>=4", + "rdflib>=6.2", "sympy>=1.10, <1.15", # Occasional breaking changes in minor versions ] dynamic = ["version"] diff --git a/requirements/base.in b/requirements/base.in index 16c4a694..79bc28a5 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -1,5 +1,5 @@ lxml>=4.7 pint>=0.24, <0.26 -rdflib>=4 -networkx>=2.1 +rdflib>=6.2 +networkx>=2.7 sympy>=1.10, <1.15 From 06d2c00c4a3d0f841042be6308dc2faa49545c23 Mon Sep 17 00:00:00 2001 From: Kwabena Amponsah Date: Wed, 15 Jul 2026 16:40:39 +0100 Subject: [PATCH 5/7] #376 Remove dead code and stale comments in units.py --- cellmlmanip/units.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cellmlmanip/units.py b/cellmlmanip/units.py index 2fa25844..3656e1b3 100644 --- a/cellmlmanip/units.py +++ b/cellmlmanip/units.py @@ -136,7 +136,6 @@ def __init__(self, store=None): self._calculator = UnitCalculator(self) # Expose the Unit and Quantity classes - # TODO Might not be needed in 0.10 anymore self.Unit = self._registry.Unit self.Quantity = self._registry.Quantity @@ -164,7 +163,6 @@ def add_unit(self, name, expression): # Add prefixes inside expression expression = _WORD.sub(self._prefix_expression, expression) - # Dimensionless units can't be created using a string expression. # To test if this is a dimensionless unit, parse the string as a Quantity and check if it's dimensionless quantity = self._registry.parse_expression(expression) if quantity.units == self._registry.dimensionless: @@ -277,10 +275,6 @@ def get_conversion_factor(self, from_unit, to_unit): cf = self.convert(1 * from_unit, to_unit).magnitude if isinstance(cf, numbers.Number) and math.isclose(cf, 1.0): return 1 - elif isinstance(cf, sympy.Mul) and 1.0 in cf.args: # pragma: no cover - # We get an ugly artefact whereby pint gives us a 1.0*cf as conversion factor; remove the 1.0 - # But only on older pint, e.g. 3.9 - return sympy.Mul(*[a for a in cf.args if a != 1.0]) return cf def convert(self, quantity, unit): From a732fd5a4b7598bea993cdcc8ebacd79a0e33aa6 Mon Sep 17 00:00:00 2001 From: Kwabena Amponsah Date: Thu, 16 Jul 2026 12:40:29 +0100 Subject: [PATCH 6/7] #376 Make 1/A comparison robust in singularity fixes --- cellmlmanip/_singularity_fixes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cellmlmanip/_singularity_fixes.py b/cellmlmanip/_singularity_fixes.py index a8e704d6..7eb76326 100644 --- a/cellmlmanip/_singularity_fixes.py +++ b/cellmlmanip/_singularity_fixes.py @@ -10,6 +10,7 @@ And, Eq, Float, + Integer, Le, Mul, Or, @@ -293,7 +294,8 @@ def _fix_expr_parts(expr, V, U_offset, exp_function): expr_parts.append(_generate_piecewise(ex, V, sp, Vmin, Vmax) if sp is not None else ex) return (None, None, None, Add(*expr_parts), is_piecewise) - elif isinstance(expr, Pow) and expr.args[1] in (-1, -1.0) and len(expr.args) == 2: # 1/A + # 1/A + elif isinstance(expr, Pow) and len(expr.args) == 2 and expr.args[1].is_number and float(expr.args[1]) == -1.0: # Find singularities in A and adjust result to represent 1 / A Vmin, Vmax, sp, ex, has_piecewise = _fix_expr_parts(expr.args[0], V, U_offset, exp_function) has_piecewise = has_piecewise or Vmin is not None From c09e4aa0e2aa5b31223e1edb5e83d67980b74f89 Mon Sep 17 00:00:00 2001 From: Kwabena Amponsah Date: Thu, 16 Jul 2026 12:45:17 +0100 Subject: [PATCH 7/7] #376 Fix flake import error --- cellmlmanip/_singularity_fixes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cellmlmanip/_singularity_fixes.py b/cellmlmanip/_singularity_fixes.py index 7eb76326..447df455 100644 --- a/cellmlmanip/_singularity_fixes.py +++ b/cellmlmanip/_singularity_fixes.py @@ -10,7 +10,6 @@ And, Eq, Float, - Integer, Le, Mul, Or,