Skip to content

Import SBML reaction networks as verified graphs (Track E) - #4

Merged
orda-aska merged 2 commits into
mainfrom
auto/E-sbml-import
Nov 5, 2026
Merged

orda-aska merged 2 commits into
mainfrom
auto/E-sbml-import

Conversation

@zero-aska

@zero-aska zero-aska commented Nov 5, 2026 •

Copy link
Copy Markdown

This PR adds Track E's SBML import. It is a from-scratch reader that turns curated BioModels into executable reaction networks and typed graphs. Their structural claims are proven in exact rational arithmetic, and their dynamics are checked against libRoadRunner, an independent SBML engine.

What's added

  • vh/graph/sbml.py reads SBML Level 2 (versions 1–5) and Level 3 (versions 1–2) core. It supports:
    • compartments, species, global and local parameters
    • function definitions, initial assignments and assignment rules
    • reactions with MathML kinetic laws
  • Everything outside that subset is refused by name, never silently dropped: events, rate and algebraic rules, constraints, delays, fast reactions, stoichiometryMath, conversion factors, variable compartments and required packages.
  • Security: documents with a DOCTYPE or entity declarations are refused, and MathML is compiled to closures over a fixed operator table, never passed to eval.
  • The model exposes trajectories, steady states on the conservation-reduced system, and reduced-Jacobian eigenvalues.
  • vh/graph/stoichiometry.py does the exact algebra with Fractions:
    • canonical conservation laws, as the RREF of the left nullspace;
    • positive-mass (stoichiometric) consistency after Gevorgyan et al. (2008). Every answer comes with an exact certificate: a positive mass vector, or a reaction combination that provably creates the unconserved species from nothing (Farkas' lemma).
  • SBMLModel.to_graph() builds species, reaction, compartment and moiety nodes. sbml_schema() recomputes the stoichiometry from the edges alone and checks four invariants: stoichiometry, located, conservation and stoichiometric consistency.
  • data/sbml/ holds three BioModels whose notes state CC0, vendored unmodified, with their SHA-256 values and PubMed IDs in sources.json: Huang & Ferrell 1996, Kholodenko 2000 and the Elowitz & Leibler 2000 repressilator. I left out Markevich 2004 because its file carries an older BioModels copyright notice rather than a CC0 statement.
  • Also added: scripts/validate_sbml.py, tests/test_sbml.py (47 tests), the report in auto-research/2026-11-05-E-sbml-import/, doc and roadmap updates, and renderer styles for the new node types.
  • libroadrunner is added to the dev extras as a test-only referee.

Validation

The authorities are exact rational algebra and closed-form solutions for the structure, and libRoadRunner 2.11.0 (libSBML + CVODE) for the dynamics. No parameter was fitted.

Result
Conservation laws (3 models) Exact l·N = 0; rank(N) + laws = number of dynamic species; same moiety space as libRoadRunner. Kholodenko totals are 100, 300 and 300.
Trajectories ≤ 6.9e-10 of each species' range
Drift of conserved totals ≤ 1.1e-13 relative
Steady states Huang (stable) 1.7e-12; Kholodenko and repressilator (unstable fixed points) ≤ 1.5e-15
Reduced eigenvalues ≤ 1.3e-9 relative; the stability classification agrees
Limit-cycle periods Kholodenko 1299.77, repressilator 125.354, both matching to ≤ 9e-12 relative
Survey of BIOMD0000000001–060 49 read and 11 refused by name. Against libRoadRunner at rtol 1e-10: median 1.7e-9, 47 of 49 within 1e-6, and the two calcium oscillators fall below 1e-6 at rtol 1e-12.

The new invariant catches real inconsistencies. Positive-mass consistency fails, with exact certificates, in 8 of the 49 surveyed models that were read.

  • In Chassagnole 2002 (BIOMD0000000051), the phosphotransferase reaction is cglcex + 65 cpep → 65 cg6p + 65 cpyr. The model's notes say its stoichiometries were adjusted to encode a biomass-to-volume ratio, so a unit conversion is hiding in the stoichiometry. A second certificate runs through vPGDH, which is written without CO₂.
  • In the Vilar 2002 and Gardner 1998 models, the flags come from deliberate abstractions: template synthesis, and degradation of one partner inside a complex.

The report is explicit that a flag proves a property of the network as written, not a modelling error.

Checked locally. GitHub Actions is not enabled on this fork, so there is no CI run. I ran:

  • pytest -q: 158 passed, no skips
  • node web/test_engines.mjs: passed
  • scripts/validate_sbml.py --survey: reproduces results.json

The environment was Python 3.13, NumPy 2.5.3, SciPy 1.18.1, libRoadRunner 2.11.0, OpenMM 8.6.1 and PySCF 2.15.0.

Limits

  • Units are not checked.
  • Kinetics run as Python closures, so they are neither JAX-differentiable nor fast.
  • Steady states are local to the starting guess.
  • 11 of the 60 surveyed models use constructs outside the subset.
  • Agreement with libRoadRunner validates the SBML semantics, not the biology.
  • Nothing here touches vh.bridge or patient data.

Relationship to other open PRs

https://claude.ai/code/session_01CoFhtFtE7wJgvHKAzNB3xD


Generated by Claude Code

Add a from-scratch reader for SBML Level 2/3 core (vh/graph/sbml.py):
compartments, species, global and local parameters, function
definitions, initial assignments, assignment rules and reactions with
MathML kinetic laws. Unsupported constructs (events, rate and algebraic
rules, delays, fast reactions, stoichiometryMath, conversion factors,
variable compartments, required packages) are refused by name.
Documents with a DOCTYPE are refused and MathML compiles to closures
over a fixed operator table. Models simulate, solve for steady states
on the conservation-reduced system and report reduced eigenvalues.

vh/graph/stoichiometry.py does exact rational algebra: canonical
conservation laws (RREF of the left nullspace) and positive-mass
stoichiometric consistency (Gevorgyan et al. 2008) with exactly checked
certificates. SBML graphs carry species, reaction, compartment and
moiety nodes; the schema recomputes N from edges and checks
stoichiometry, location, exact complete conservation and consistency.

Validation (scripts/validate_sbml.py, auto-research report): three CC0
BioModels vendored with SHA-256 provenance agree with libRoadRunner,
an independent SBML engine used only as a test referee, to <=6.9e-10 on
trajectories, <=1.7e-12 on steady states and <=1.3e-9 on reduced
eigenvalues; periods match to ~1e-11. Across BIOMD0000000001-060, 49
models are read and 11 refused by name; trajectories of all 49 agree
with the referee (median 1.7e-9). The consistency invariant flags 8
models with exact certificates, including a stoichiometry used as a
unit conversion in Chassagnole 2002, as its own notes describe.

libroadrunner joins the dev extras as a test-only referee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CoFhtFtE7wJgvHKAzNB3xD
The DOCTYPE/entity refusal scanned raw bytes for "<!doctype" and
"<!entity". In UTF-16 those markers are interleaved with NUL bytes, so
a UTF-16 document with an internal entity passed the scan and
ElementTree expanded it (reported in review on #4).

read_sbml now pre-parses with expat, which decodes the declared
encoding, and refuses on any DOCTYPE or entity declaration. Input expat
cannot decode (for example UTF-32), empty input and truncated XML raise
SBMLError instead of passing through. New tests cover UTF-16 with and
without a byte-order mark in both byte orders, a UTF-16 document
without a DOCTYPE that must still load, and unreadable input; six of
the seven fail on the previous code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CoFhtFtE7wJgvHKAzNB3xD

Copy link
Copy Markdown
Author

Thanks @orda-aska, the UTF-16 bypass was real, and I reproduced it in UTF-16, UTF-16-LE and UTF-16-BE. It's fixed in 05dd317.

Fix. read_sbml now pre-parses with expat, which decodes whatever encoding the document declares, and refuses as soon as it sees a DOCTYPE or entity declaration. Input expat can't decode (UTF-32, for example), empty input and truncated XML now raise SBMLError rather than passing the check unexamined.

New tests, six of which fail on the previous code:

  • your repro in all three UTF-16 variants
  • a UTF-16 document without a DOCTYPE, which must still load (passes before and after)
  • empty, truncated and UTF-32 input

Checks: 208 passed locally, and the JS engine checks pass.

Rebase: yes, it was intentional. After #2 and #3 merged I rebased my own branch onto 88bc75a, keeping both sets of rows in README.md and auto-research/README.md. The SBML code itself didn't change in the rebase.


Generated by Claude Code

@orda-aska
orda-aska merged commit 2f23ae5 into main Nov 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants