Read SBML initial assignments instead of the placeholder attribute they supersede - #801
Merged
Merged
Conversation
…ey supersede SBML lets a model set an entity's starting value in two places. A parameter carries a value attribute, a compartment a size, and a species an initial amount or concentration, and a listOfInitialAssignments entry may supersede any of them. When it does, the attribute is a placeholder the model never starts from. The import-time scanner read only the attributes, so it reported numbers the model does not start from, and said nothing. Two consumers used them. The measurement layer binds them as constants, so an observable or noise formula naming such a parameter was scored with the placeholder. The exporter turns them into absolute numbers for a relative condition, so conditions.tsv got a wrong value. A model that writes no attribute at all, which is what antimony emits for a derived parameter, failed differently and not much better: the symbol stayed in the namespace with no value behind it, so a formula naming it died at materialize time saying it was neither a simulation output nor a model parameter, which is not true. An entity an initial assignment derives now has no value the model file settles, so the scanner reports none. An assignment that is arithmetic over numbers alone is the value and replaces the attribute. An assignment computed from other entities drops the entity from the value map and the formula namespace and records its right hand side, and the measurement layer inlines that right hand side, which is what it already does for an assignment rule. So an observable over Bertozzi's beta_N, which the model computes as (R0_ * gamma_) / N_ behind a value of 0.0, is scored with the value the model starts from, and it tracks the fit when R0_ is estimated. Inlining is refused when the assignment reads something that moves during the simulation, because the substituted expression is read at a measurement time while the assignment fixed a value at the start. A species is deliberately left alone beyond losing a stale initial: an initial assignment pins the start only, so the species is still a dynamical state and still an output column, and excluding it would break the Boehm fixture, whose observables name two species set this way. Two adjacent defects are fixed in the same pass because they are the same sentence. An assignment rule target that carries a leftover value attribute no longer reports that number either. A prediction noise formula now inlines the same map a measurement formula does, so the two layers no longer disagree about which symbols a model offers. The refusal in mutation_target_value no longer blames BNGL alone. It names the parameter, gives both languages' version of the cause, and says what to write instead. That message was already reachable for an SBML model before this change. Measured on 25 models from the public PEtab benchmark collection, 16 use initial assignments, 31 of them on a parameter or compartment, and 28 of those were reporting a stale value. No model committed to this repository has one on a parameter, so nothing here changes. ADR-0147 records the decision.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #795.
SBML lets a model give an entity its starting value in two places: an attribute on
the entity (
valueon a parameter,sizeon a compartment,initialAmountorinitialConcentrationon a species) and alistOfInitialAssignmentsentry thatsupersedes it. When both are present the attribute is a placeholder the model never
starts from.
PyBNF's import-time SBML scanner read only the attributes, so it reported numbers the
model does not start from and said nothing about it. Two places used those numbers.
The measurement layer binds them as constants, so an observable or noise formula
naming such a parameter was scored with the placeholder. The exporter converts them
into absolute numbers for a relative condition, so a wrong number was written into
conditions.tsv. Where the model writes no attribute at all, which is what antimony
emits for a derived parameter, the failure was different and not much better: the
symbol stayed in the namespace with no value behind it, so a formula naming it died
during materialize saying it was neither a simulation output nor a model parameter,
which is not true.
What changes
An entity whose initial value an assignment derives has no value the model file
settles, so the scanner reports none.
An assignment that is arithmetic over numbers alone is the value, and it replaces the
attribute. The test for that is a small evaluator that walks the MathML and refuses an
identifier node, so "evaluates to a number" and "reads no other entity" are one test
rather than two. This is the same rule petab applies to an SBML initial assignment and
the rule we argued for upstream in PEtab-dev/libpetab-python#517 for BioNetGen.
An assignment computed from other entities drops the entity from the value map and
from the formula namespace, and records its right hand side. A formula naming the
entity is then rewritten to the entities it is computed from, through the same inliner
that already handles an assignment rule. So an observable over Bertozzi's
beta_N,which the model computes as
(R0_ * gamma_) / N_behind a placeholder value of 0.0, isscored with the value the model starts from, and it tracks the fit when
R0_isestimated rather than sitting at a number the file implied.
Inlining is refused when the assignment reads something that moves during the
simulation, because the substituted expression is read at a measurement time while the
assignment fixed a value at the start. The refusal names the entity that moves.
A species is deliberately treated differently. An initial assignment pins the start
only, so a species is still a dynamical state and still a simulation-output column. It
keeps its place in the namespace and is never inlined, and only loses a stale declared
initial. Treating it like an assignment-rule target would have broken the Boehm fixture
on the first run, since its observables name two species set this way.
Two adjacent defects are fixed here as well, because they are the same sentence. An
assignment rule target that carries a leftover value attribute no longer reports that
number either. A prediction noise formula now inlines the same map a measurement
formula does, so the two layers no longer disagree about which symbols a model offers.
The refusal in mutation_target_value no longer blames BNGL alone: it names the
parameter, gives both languages' version of the cause, and says what to write instead.
That message was already reachable for an SBML model before this change.
How much this reaches
Nothing committed to this repository is affected. Of 20 SBML files here, three have an
initial assignment and all three are on a species, and the field that held their stale
values had no reader. Outside it the pattern is common: of 25 models in the public
PEtab benchmark collection, 16 use initial assignments, 31 of those are on a parameter
or a compartment, and 28 of those 31 were getting a stale value out of the scanner. All
31 are computed from other entities rather than from numbers, which is why the fix is
built around inlining rather than around evaluating.
Verification
The full default test tier passes with BNG2.pl available: 5475 passed, 26 skipped. The
failure set on the petab modules is identical to the pre-change baseline. Lint is clean
under the pinned ruff. The new tests cover the scanner (value superseded, value
dropped, expression recorded, namespace membership, the antimony shape, compartments,
chains, block order, each kind of moving entity refused, untranslatable math, the
species asymmetry including a Boehm regression, and the numeric evaluator), the inliner
(Bertozzi and Laske shapes, chains, each error message), the measurement layer (the
inlined column is the value the model starts from, and it moves with a fitted
dependency), and the exporter refusal.
ADR-0147 records the decision and why the two rejected alternatives, refusing with a
better message or excluding without inlining, would turn 28 measured silent wrong
numbers into 28 refusals where a correct answer was available.