Skip to content

Add support for BNGL models (cont.) - #508

Draft
dweindl wants to merge 10 commits into
PEtab-dev:mainfrom
dweindl:bngl-corpus-fetch-on-demand
Draft

Add support for BNGL models (cont.)#508
dweindl wants to merge 10 commits into
PEtab-dev:mainfrom
dweindl:bngl-corpus-fetch-on-demand

Conversation

@dweindl

@dweindl dweindl commented Aug 12, 2026

Copy link
Copy Markdown
Member

Builds on #501 (BNGL model support, by @wshlavacek - full credit for the reader implementation and model corpus selection, all of which this branch carries forward unchanged).

  • Removes most of the vendored .bngl files and downloads them on demand

wshlavacek and others added 10 commits August 11, 2026 23:26
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.
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.
The 21 third-party .bngl fixtures under tests/v1/bngl_corpus/ are
unmodified (or, for Barua_2009, one-line-patched) copies of files already
published in RuleWorld/RuleHub and wshlavacek/BNGL-Models. Rather than
vendoring ~2600 lines of someone else's model text, fetch the same 21
files on demand from their pinned upstream commits, sha256-verified
against the exact bytes reviewed here.

golden.json and README.md stay committed -- they're this repo's own
oracle/test code, not sourced from anywhere upstream.

See scripts/fetch_bngl_corpus_demo.py for the fetcher.
Add tests/v1/fetch_bngl_corpus.py: fetches the 21 .bngl fixtures backing
tests/v1/test_bngl_corpus.py from their pinned upstream commits (RuleHub,
BNGL-Models) via jsdelivr's GitHub CDN, sha256-verified against the exact
bytes reviewed. No git/subprocess use, no execution of fetched content.

Wire it into CI as a step before the unit tests run, gitignore the fetched
.bngl files (golden.json/README.md stay tracked), and give
test_reader_matches_bng2_golden a real skip reason pointing at the fetch
script when the corpus hasn't been materialized yet.
@codecov-commenter

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.80%. Comparing base (73b680c) to head (d1124cf).

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     #508      +/-   ##
==========================================
+ Coverage   75.54%   75.80%   +0.26%     
==========================================
  Files          65       67       +2     
  Lines        7359     7526     +167     
  Branches     1325     1343      +18     
==========================================
+ Hits         5559     5705     +146     
- Misses       1296     1313      +17     
- Partials      504      508       +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.

3 participants