Repository navigation
Description
Summary
Make a measurement-model formula that references an SBML assignment-rule variable work instead of being rejected. #464 landed option 1 (the smaller, safer fix): an assignment-rule variable is excluded from the formula namespace and a reference to it fails fast at config build with a reconstruct-from-species message. This issue tracks option 2 (the user-friendly path the maintainer wants): observable: Epo_cells, formula: Epo_cells should just work, since SBML authors define exactly these convenience variables (the D2D Epo_cells / Epo_medium observables are assignment rules — Epo_cells := Epo_EpoRi + dEpoi), so reaching for them by name is the obvious first attempt.
Background (what #464 already did)
pybnf/petab/_sbml.py::parse_modelnow scans<listOfRules>and recordsSbmlEntities.assignment_rules={target -> frozenset(<ci> referent names)}(e.g.{'Epo_cells': {'Epo_EpoRi', 'dEpoi'}});namespace_symbolsexcludes those targets.pybnf/config.py::_model_expression_namespacereturns the assignment-rule targets asunresolved;_load_measurement_modelsraises a pointedPybnfError(Configuration._raise_assignment_rule_formula) when a formula references one.
So the parse-time groundwork (target + its referents) already exists. What's missing is resolving the value at materialize time.
The two resolution strategies (from #464's "Suggested fix" option 2)
- Evaluate the rule in the measurement layer (preferred — backend-agnostic). The observation layer (ADR-0036) is deliberately backend-agnostic; neither RoadRunner nor bngsim is asked to expose a computed observable today. Evaluating an assignment rule as a post-simulation transform over the trajectory keeps that property and reuses the existing PEtab-math machinery: inline the rule's RHS into the formula (or pre-materialize the rule target as a derived column before the user's formula is applied). This needs the full rule RHS expression, not just the
<ci>names — i.e. a MathML → PEtab-math (infix) translation of the<assignmentRule>body._sbml.parse_modelcurrently collects only the referent names; it would need to carry the serialized expression too. - Surface the rule target as a simulation-output column. RoadRunner already exposes assignment-rule variables; bngsim would need to emit them. This is backend-specific and asymmetric (works on one backend, not the other), so it's the weaker option for a backend-agnostic layer.
Wrinkles to handle
- Rule-dependency ordering: an assignment rule may reference another assignment-rule variable; in-layer evaluation must topologically resolve them (SBML allows forward references; evaluation order is by dependency, not document order).
- Rate rules: out of scope here (a
rateRuletarget is a genuine dynamical state); Measurement-formula namespace advertises SBML assignment-rule variables that aren't resolvable at materialize → fit fails mid-run #464 already leaves them alone. .antparity: must work identically on the Antimony form (now routed through the SBML namespace by Measurement-model formulas silently unavailable on Antimony (.ant) models — namespace built from parse_bngl #463) —becker_epor.ant'sEpo_cells := Epo_EpoRi + dEpoishould resolve the same as the.xmlform.- Gradients:
MeasurementModel.prediction_sensitivity([#385 layer H] Gradient through the SBML/Antimony measurement-model seam #455) differentiates the formula through each referenced column's sensitivity. If the rule is inlined to species, the chain rule flows through automatically; if surfaced as a backend column, its sensitivity must be available too.
Acceptance
observable: Epo_cells, formula: Epo_cellsruns a fit end to end on bothtests/sbml_files/becker_epor.xml(sbml_backend = bngsim) andbecker_epor.ant, matching the species-reconstruction path (formula: Epo_EpoRi + dEpoi) the becker smoke test (Gradient optimizers: finish #386 deliverable 6 — convergence-vs-baseline test + becker smoke test #462) already validates.- Relax/remove the Measurement-formula namespace advertises SBML assignment-rule variables that aren't resolvable at materialize → fit fails mid-run #464 fail-fast (
_raise_assignment_rule_formula) once references resolve; keep a clear error for the genuinely-unresolvable cases (e.g. a rate-rule target). - A test asserting an assignment-rule observable materializes to the same column as its hand-reconstructed species formula (the analytic oracle the existing layer tests use).
References
- Parent / option-1 fix: Measurement-formula namespace advertises SBML assignment-rule variables that aren't resolvable at materialize → fit fails mid-run #464 (
_sbml.pyassignment_rules,config.py_model_expression_namespace/_raise_assignment_rule_formula). Landed in commit e462ace. .antnamespace routing: Measurement-model formulas silently unavailable on Antimony (.ant) models — namespace built from parse_bngl #463 (commit 64d6e68).- Layer:
pybnf/measurement/base.py::MeasurementModel.materialize/prediction_sensitivity; ADR-0036. Context: 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.
Activity
feat(measurement): resolve SBML assignment-rule observables by inlini…
feat(measurement): resolve SBML assignment-rule observables by inlini…
fix(petab): inline SBML assignmentRule variables in an imported obser…