Skip to content

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

Closed

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

Activity

  1. wshlavacek commented on Jun 30, 2026

    @wshlavacek
    CollaboratorAuthor

    Fixed in 64d6e68. .ant models now share the SBML namespace branch in _model_expression_namespace: the Antimony source is converted to SBML (the same antimony.getSBMLString the model loader uses) and parse_sbml'd, so a measurement formula over a .ant model's species validates exactly like the .xml form (and inherits the #464 assignment-rule exclusion). The stale ".ant carries no formula observables" comment is dropped, and test_petab_sbml_layer.TestAntimonyConfigWiring covers the routing — including the becker_epor.ant reproduction from this issue (formula: Epo_EpoRi + dEpoi) building end to end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions