Skip to content

Add support for BNGL models#501

Open
wshlavacek wants to merge 7 commits into
PEtab-dev:mainfrom
wshlavacek:bngl_model_support
Open

Add support for BNGL models#501
wshlavacek wants to merge 7 commits into
PEtab-dev:mainfrom
wshlavacek:bngl_model_support

Conversation

@wshlavacek

Copy link
Copy Markdown
Contributor

Adds BNGL as a PEtab model language next to SBML and PySB, so a problem
declaring language: bngl loads and validates instead of stopping at
model-load in petablint.

BnglModel reads BNGL with a pure-Python parser that pulls the entities
validation cares about (parameters, species, observables, functions) straight
from the model text. So loading and validating a BNGL problem needs no external
tools and adds no runtime dependency. is_valid() will additionally run
BNG2.pl --check if that executable is on the PATH, for a stricter check, and
returns True if it isn't — so BNG2.pl is used when available but never
required. Both v1 and v2 are covered.

PyBNF has been dogfooding this reader for a while — it uses it to fit rule-based
models against PEtab problems, and its PEtab tutorials exercise the loader end
to end: https://github.com/lanl/PyBNF/tree/main/examples/tutorial

Addresses PEtab-dev/PEtab#436.

wshlavacek and others added 4 commits July 5, 2026 02:55
Add a BnglModel loader (a peer of PySBModel/SbmlModel) so that a
`language: bngl` PEtab problem loads and validates via petablint /
Problem.from_yaml at the model level. See PEtab-dev/PEtab#436.

- petab/v1/models/bngl_model.py: BnglModel backed by a small,
  dependency-free BNGL block reader (parse_bngl). Introspection only;
  is_valid shells out to `BNG2.pl --check` when a BNG backend is
  locatable and falls back to True otherwise (mirroring how the SBML
  loader always validates because libsbml is always present).
- Register `bngl` in known_model_types and add a branch to model_factory;
  v2 picks it up via the existing re-exports (+ a v2 shim module).
- tests/v1/test_model_bngl.py and a minimal BNGL fixture: ABC unit tests
  plus a full Problem.from_yaml validation oracle covering the model-cross
  checks.
Re-sync with PyBNF's sibling reader (pybnf/petab/_bngl.py, ADR-0026 / lanl/PyBNF#437):

- Block aliases: `begin molecules` / `begin species` / `begin rules` now open
  the same blocks as `molecule types` / `seed species` / `reaction rules`
  (_block_lines consults a per-canonical-name alias table), per the BNGL
  grammar reference (BioNetGen Perl2/; BNG_vscode_extension docs/bngl-grammar.md).
- Seed-species `$` clamp: `SeedSpeciesDefn = ["$"], Species, ...` -- the `$`
  fixed-concentration marker is stripped so `$counter() 10` enumerates the
  state variable `counter()`, keeping is_state_variable correct under the clamp.

Adds grammar-hardening tests (alias parsing, `$`-clamp stripping, no
cross-block shadowing, the is_state_variable seam) that double as the drift
anchor against PyBNF's reader. ruff check + format clean; 19 passed.

Refs: PEtab-dev/PEtab#436.
A trailing `\` (BNGL line continuation) splits one logical declaration across
physical lines. The block scanner processed physical lines, so a continued
parameter/function/observable was truncated at the `\` (e.g. `k = \` read as
the value `\`). Add _logical_lines() mirroring BNG2.pl's readFile
(Perl2/BNGModel.pm): strip the comment first, then while a line ends with `\`
drop it and concatenate the next comment-stripped physical line directly (no
space, so `1e\`+`3` -> `1e3`).

Surfaced by the bng_parity corpus (895 community BNGL models): 252 use line
continuation, incl. inside enumerated blocks (functions, observables,
parameters, seed species). Adds continuation + backslash-in-comment tests;
kept in sync with PyBNF's sibling reader (pybnf/petab/_bngl.py). ruff clean,
21 passed. Refs: PEtab-dev/PEtab#436.
BNGL declarations may carry a leading line label (LineLabel = {Digit}, WS |
Name, ":", [WS]): a legacy .net-style numeric index (`1 L0 1`) or a named label
(`CD14: CD14(...)`). The reader took the label as the entity -- the index as a
parameter name, the label as the seed species. Add _strip_line_label() and apply
it in the parameter and seed-species extractors (a valid BNGL identifier starts
with a letter, so a leading digit-run is unambiguously an index; a compartment
prefix carries `@`, so a bare `Name:` is unambiguously a label).

Surfaced by a writeModel-based differential over the bng_parity corpus (895
community models): 4 models disagreed with BNG2.pl's canonical parse (indexed
params/seed, labeled seed); after this fix, 0 -- parameters/observables/
functions/molecule-types/compartments all match BNG2.pl across the corpus.
Kept in sync with PyBNF's sibling reader. ruff clean, 24 passed.
Refs: PEtab-dev/PEtab#436.
@wshlavacek wshlavacek requested a review from a team as a code owner July 5, 2026 09:28
Asserts parse_bngl enumerates the same model entities BNG2.pl does, over 21
curated public community BNGL models (RuleHub, BNGL-Models) under
tests/v1/bngl_corpus/. BNG2.pl's answers are cached in golden.json -- the entity
name sets it emits from `writeModel` (its canonical parse, no network
generation) -- so the test needs NO BNG2.pl and runs anywhere; it compares the
reader against the frozen oracle. Seed species are compared by molecule
composition to absorb BNG2.pl's pattern canonicalization (t vs t(), component
reordering, @compartment prefix vs suffix).

The models exercise every hardened reader path: line continuations, indexed and
labeled declarations, block aliases, the $ clamp, compartmental BNGL, energy
patterns, states/bonds, component reordering, bare-molecule seed species. The
golden is regenerated deliberately (needs BNG2.pl) via
`python tests/v1/test_bngl_corpus.py` and reviewed as a diff.

Mirrors PyBNF's live-BNG2.pl gate (lanl/PyBNF); validated there over the full
895-model bng_parity corpus (894/894 BNG2.pl-accepted models agree). ruff clean;
21 passed without BNG2.pl. Refs: PEtab-dev/PEtab#436.
…ies`)

The grammar doc lists `molecules` (for `molecule types`) and `rules` (for
`reaction rules`) as block aliases, but BNG2.pl 2.9.3 -- the reference this
reader targets -- REJECTS both ("Could not process block type 'molecules' /
'rules'"). Honoring them let the reader enumerate entities from a block BNG2.pl
refuses, i.e. accept models the reference rejects. Restrict _BLOCK_ALIASES to
`species` (for `seed species`), which BNG2.pl accepts and in fact emits as its
own canonical seed-species spelling. Verified empirically against BNG2.pl 2.9.3.

The corpus gate is unchanged (no fixture uses the dropped aliases; golden
regenerates identically). ruff clean; 24 passed. Refs: PEtab-dev/PEtab#436.
@wshlavacek wshlavacek force-pushed the bngl_model_support branch from 353c172 to 6276d1d Compare July 5, 2026 09:40
@codecov-commenter

codecov-commenter commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.20710% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.40%. Comparing base (e557e44) to head (a62b8a4).

Files with missing lines Patch % Lines
petab/v1/models/bngl_model.py 85.80% 18 Missing and 5 partials ⚠️
petab/v1/models/model.py 75.00% 0 Missing and 1 partial ⚠️
petab/v2/models/bngl_model.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #501      +/-   ##
==========================================
+ Coverage   75.12%   75.40%   +0.28%     
==========================================
  Files          64       66       +2     
  Lines        7157     7324     +167     
  Branches     1265     1283      +18     
==========================================
+ Hits         5377     5523     +146     
- Misses       1290     1307      +17     
- Partials      490      494       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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