Skip to content

Measurement-model formulas silently unavailable on Antimony (.ant) models — namespace built from parse_bngl #463

Description

@wshlavacek

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

  • .ant and .xml are otherwise interchangeable — an .ant model 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 .ant fixture (the D2D observables Epo_cells / Epo_medium are exactly such formulas), forcing a detour through the SBML form.

Suggested fix

Route .ant through the SBML namespace path. Either:

  1. branch mf.endswith('.ant') to convert Antimony → SBML text (reusing the same antimony.getSBMLString conversion the model loader already does) and parse_sbml the result; or
  2. generalize the dispatch so anything that loads as SBML (.xml and .ant) shares the parse_sbml branch.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions