v1.3.0: modernise for Python 3.10–3.13 and bundle an updated PyDSTool - #417
Open
jarmarshall wants to merge 9 commits into
Open
jarmarshall wants to merge 9 commits into
jarmarshall wants to merge 9 commits into
Conversation
Add the subset of PyDSTool 0.91.0 (https://github.com/robclewley/pydstool, BSD licence) that MuMoT needs for equilibrium continuation, exactly as released on PyPI, so that the modernisation in the next commit can be reviewed as a diff against upstream. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
- Bundle a trimmed PyDSTool as mumot._vendor.pydstool, updated for Python >= 3.10, NumPy 2 and current SciPy: relative imports, removed AUTO/compiled integrators/Toolbox/distutils, NumPy 2 name aliases, and a replacement (_cst.py) for the removed stdlib `parser` module. See mumot/_vendor/pydstool/README.md for all changes. - Fix PyCont branch switching at branch points, which depended on floating-point rounding, and make the Moore-Penrose corrector fall back to least squares when the bordered Jacobian is singular. - Add mumot.continuation, a backend-independent equilibrium continuation API (SymPy symbols in, NumPy arrays out); the bifurcation view uses only this, so PyDSTool can later be replaced by a native implementation. - Remove the circular import between mumot.utils and the package (new mumot._version) and move symbolic derivations out of views into mumot.equations; the internal import graph is now acyclic. - Adapt to current SymPy (latex() of strings, simplify() evaluating derivatives, strict subs()), Matplotlib (3D axes, tick labels, Arrow3D), IPython 9 and Notebook 7 (ipympl backend); fix the broken chmod calls and an undefined variable in utils. - Move packaging to pyproject.toml (Python >= 3.10, unpinned dependencies except antlr4 4.11 required by SymPy's LaTeX parser); update tox, CI, Read the Docs, Binder, docs and CHANGELOG. - Add unit tests for the continuation API and the vendored parser shim. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
Unbounded requirements let resolvers (or existing environments) pick ancient releases, e.g. `--resolution lowest-direct` tried to build ipython 0.10. Give each direct dependency the oldest version with the APIs MuMoT relies on, and add a CI job that installs exactly those versions, checks consistency and runs the unit tests, so the floors stay honest. No upper bounds are added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
- Clamp the cosine used for the noise ellipse angle to a real value in
[-1, 1]; round-off made the angle complex, which Matplotlib rejects.
- Use finite temporary slider maxima instead of float('inf'): non-finite
floats in widget state are not valid JSON (deprecated by jupyter_client).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
tests/symbolic_reference.json holds srepr dumps of the model getters' results (ODEs, van Kampen ODEs, stoichiometry, master equation, van Kampen expansion, Fokker-Planck and noise equations/solutions) produced with the previous release on Python 3.8 and SymPy 1.4. The test checks the current results are mathematically equal. It fails if SymPy's default simplify() (which evaluates derivatives) is used again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
PyDSTool defined generated functions with exec(code) and read them back via locals()[name]. Since Python 3.13 locals() in a function returns a new snapshot on every call, so the names were lost (KeyError '_auxfn_getbound' in every bifurcation diagram). Exec into an explicit namespace and read from it instead; behaviour on 3.10-3.12 is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
- nbval (>= 0.10) executes cells with store_history=False, so `In` stays empty and the user manual's documented `%%model` + `mumot.parseModel(In[2])` pattern raised IndexError in every tox job. A root conftest.py makes nbval execute cells with history, as Jupyter does; the manual and API are unchanged. - minimum_versions_job: astral-sh/setup-uv already creates and activates .venv when python-version is set, so drop the failing `uv venv` step and run pytest from that environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
tox runs the test notebooks under branch coverage, which makes the symbolic noise calculations about 4x slower: MuMoTtest_NoiseFixedPoints cell 52 (noiseCorrelations) takes ~26 s normally but ~102 s under coverage locally, so on a slower runner it exceeded the 120 s limit (3.12 job; the same cell passed in the previous run). Without coverage it is no slower than with the previous release (28 s). The timeout only guards against hung cells, so give it headroom. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
- Point the README badge and all Getting Started Binder links at v1.3.0 (README badge now uses the same urlpath=tree/... form as the docs). - CHANGELOG: "Unreleased" becomes v1.3.0 and lists the later fixes. - about.rst: credit the bundled PyDSTool (BSD licence); fix the broken European Research Council link. - Read the Docs: fetch git tags after checkout so setuptools_scm reports the release version rather than 0.1.devN from the shallow clone. - Update the docs copyright years and the Binder URL example in the release instructions. The v1.3.0 tag itself is to be created on master after this PR is merged (pushing a v* tag triggers the PyPI upload in CI). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings MuMoT up to date with current Python (3.10–3.13) and the current scientific Python stack (NumPy 2, SciPy, SymPy 1.14, Matplotlib, IPython 9, Notebook 7/JupyterLab), unpins dependencies, and removes the dependency on the unmaintained PyDSTool package. Also prepares the v1.3.0 release (see Release below).
PyDSTool
mumot._vendor.pydstool. The first commit (f7c8a55) adds the upstream files unmodified, sogit diff f7c8a55 -- mumot/_vendorshows every change;mumot/_vendor/pydstool/README.mdlists them.distutilsuse._cst.py) for the stdlibparsermodule removed in Python 3.10, and Python 3.13locals()semantics (PEP 667). The_cst.pyoutput was checked node-for-node against the realparsermodule on 180,000 random expressions.Continuation API
mumot/continuation.py: a backend-independent equilibrium-continuation API (SymPy symbols in, NumPy arrays out).MuMoTbifurcationViewuses only this API and no longer does any PyDSTool name mangling. A native continuation implementation could replace PyDSTool by implementing two methods.Dependencies and packaging
pyproject.toml(PEP 621), requiring Python ≥ 3.10.minimum_versions_jobCI job installs exactly those minimum versions and runs the unit tests.pip check) and no dependency cycles (pipdeptree -w fail).antlr4-python3-runtime==4.11.*, which SymPy's LaTeX parser requires. It is tracked in Remove the exact antlr4-python3-runtime==4.11.* pin #418.Code structure
mumot.utilsand the package; previously an uninstalled checkout could not be imported.viewsto a newmumot.equationsmodule, so the internal import graph is now acyclic.Library compatibility fixes
latex()on strings,simplify()evaluating derivatives, andsubs()with non-numeric values. Without these fixes the van Kampen ODEs came out wrong for multi-species models.Arrow3Dupdated for current APIs.ipymplbackend.chmodcalls and an undefined variable inutils.CI, docs, Binder
%%model+mumot.parseModel(In[n])pattern under test only. A rootconftest.pymakes nbval record history as Jupyter does.Release
v1.3.0.about.rst: credits the bundled PyDSTool; the broken ERC link is fixed.v1.3.0tag still needs to be created onmasterafter this PR is merged, not before. Pushing av*tag triggers the Test PyPI and PyPI upload in CI, and the Binder links only work once the tag exists.docs/source/about.rst.Behaviour changes
showNoiseEquations()andshowNoiseSolutions()now render moments as ⟨η⟩, as the code intended. SymPy 1.4 silently skipped that substitution.--ServerApp.iopub_msg_rate_limitwhen starting Jupyter instead.Testing
tests/test_symbolic_regression.py); it fails if SymPy's defaultsimplifyis used again.minimum_versions_jobinstalls the lowest allowed version of every dependency (Python 3.10) and passes the unit tests. Locally, the bifurcation results also match the reference on that set.requirements.txt,postBuild, JupyterLab 4.6 / Notebook 7). In Chromium,integrate(),stream()andbifurcation()rendered interactive ipympl figures and widgets with no errors, and a slider change redrew the figure. The real mybinder.org image build has not been tested.🤖 Generated with Claude Code
https://claude.ai/code/session_01VPcTBfCTpRyi5y8kDcXFWw