Repository navigation
Description
Summary
A measurement-model observable formula (observable: <id>, formula: <expr>, ADR-0036) fails on an Antimony (.ant) model even when the referenced species exist, because the formula's allowed-symbol namespace is built by parsing the .ant file as BNGL, not SBML. The species are therefore absent from the namespace and every formula symbol is rejected as "not a known model entity" — surprising, since the rest of the pipeline treats .ant and .xml identically (an .ant model is converted to SBML at load).
Where
Configuration._model_expression_namespace (pybnf/config.py:1858) dispatches on extension:
if mf.endswith('.xml'):
ent = parse_sbml(text) # species ∪ parameters ∪ compartments
...
else: # .bngl (the BNGL ParamList; .ant antimony carries no formula observables)
ent = parse_bngl(text) # <-- a .ant file lands here and parses as BNGL
...A .ant file matches neither .xml nor (meaningfully) the BNGL grammar, so parse_bngl returns an essentially empty namespace and the formula validation in _load_measurement_models raises.
Reproduction
With tests/sbml_files/becker_epor.ant (the Becker EpoR model) and a measurement formula over two of its species:
edition = 2
job_type = trf
model: becker_epor.ant
observable: obs_cells, formula: Epo_EpoRi + dEpoi
experiment: tc, data: tc.exp
uniform_var = kon 1e-5 1e-2
uniform_var = koff 1e-3 1e-1
raises:
PybnfError: The observableFormula references 'Epo_EpoRi', which is not a known model entity
(species / parameter / observable / function). ...
Measurement model 'obs_cells': allowed symbols are the model's species/parameters/observables/
functions, the fit's free parameters ['koff', 'kon'], and any row-varying placeholder.
The same model in SBML form (becker_epor.xml, sbml_backend = bngsim) with the identical formula works end to end (verified by tests/test_gradient_optimizer.py::test_trf_multistart_smoke_on_becker_epor_sbml_measurement_layer, #462). A .ant model works only when the data column matches a bare species (no formula), which is the dependency-free path the sibling smoke test takes.
Why it matters
.antand.xmlare otherwise interchangeable — an.antmodel is converted to SBML at load (bngsim_antimony_model.py,antimony.getSBMLString), so the species/parameter namespace a formula needs is fully available; it's just not surfaced to_model_expression_namespace.- The failure mode is misleading: the error says the species "is not a known model entity" when it demonstrably is one — a user can't tell it's an unsupported-path issue rather than a typo.
- It blocks the natural becker workflow on the canonical
.antfixture (the D2D observablesEpo_cells/Epo_mediumare exactly such formulas), forcing a detour through the SBML form.
Suggested fix
Route .ant through the SBML namespace path. Either:
- branch
mf.endswith('.ant')to convert Antimony → SBML text (reusing the sameantimony.getSBMLStringconversion the model loader already does) andparse_sbmlthe result; or - generalize the dispatch so anything that loads as SBML (
.xmland.ant) shares theparse_sbmlbranch.
Either way, drop the stale ".ant antimony carries no formula observables" comment and add a measurement-formula-on-.ant test (the SBML sibling already exists).
References
- Surfaced while finishing Gradient-based local optimizers (D2D-style: TRF/Levenberg-Marquardt + L-BFGS-B) in the async Algorithm loop #386 deliverable 6 / Gradient optimizers: finish #386 deliverable 6 — convergence-vs-baseline test + becker smoke test #462 (the becker SBML/Antimony gradient smoke tests). The SBML measurement-layer path works; this is the
.antgap that test had to route around. pybnf/config.py:1858(_model_expression_namespace),pybnf/config.py:1764(_load_measurement_models).
Activity
fix(measurement): route Antimony (.ant) models through the SBML formu…
wshlavacek commentedon Jun 30, 2026
Fixed in 64d6e68.
.antmodels now share the SBML namespace branch in_model_expression_namespace: the Antimony source is converted to SBML (the sameantimony.getSBMLStringthe model loader uses) andparse_sbml'd, so a measurement formula over a.antmodel's species validates exactly like the.xmlform (and inherits the #464 assignment-rule exclusion). The stale ".antcarries no formula observables" comment is dropped, andtest_petab_sbml_layer.TestAntimonyConfigWiringcovers the routing — including thebecker_epor.antreproduction from this issue (formula: Epo_EpoRi + dEpoi) building end to end.fix(measurement): route Antimony (.ant) models through the SBML formu…