Repository navigation
Fix uncompilable code for sigmas referencing assignment rules - #3388
Conversation
Noise formulas (sigma{y,z}) referencing expressions, e.g., targets of SBML
assignment rules, passed import, but resulted in uncompilable model code,
since the generated sigma{y,z} functions do not receive `w`. Also, the
derivatives of these expressions were missing from dsigmaydp.
Expressions in sigma{y,z} are now replaced by their definitions before
states are replaced by the respective observable. Sigmas that still depend
on quantities that are not available for their evaluation (states that
cannot be expressed in terms of the respective observable, conservation
law totals, events, splines) are rejected at import with an informative
error instead of failing during compilation.
Fixes #3384.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
4 open findings
Thewhileloop will not terminate ifwdefinitions contain a cycle (directly or indirectly),… · New The error message embeds a Pythonset({syms}), which can yield non-deterministic ordering for… · New This assertion depends on the exact string representation of aset({A}), which is brittle if… · New These tolerances can significantly slow down integration and make the test suite heavier,… · New
What changed in this PR
Fixes SBML-imported models producing uncompilable code when sigmay/sigmaz reference assignment-rule expressions (w) by expanding those expressions during equation computation and rejecting unsupported dependencies earlier.
Changes:
- Expand
w(assignment-rule/expression) symbols insidesigmay/sigmazand related observable/event-observable equations during_compute_equation. - Improve validation and error reporting for sigma dependencies that cannot be evaluated (states not eliminable to the respective observable, plus
tcl/h/spl). - Add a regression test covering sigma formulas depending on assignment rules (including parameter-only rules) and gradient checks.
| File | Description |
|---|---|
| python/sdist/amici/_symbolic/de_model.py | Substitutes w symbols in sigmas/observables and reports unavailable symbols with a clearer import-time error. |
| python/tests/test_sbml_import.py | Adds regression test for assignment-rule-dependent sigmas, validates rejection and correct sigma/gradient behavior. |
| changelog.d/+sigma-expressions.fix.md | Documents the fix and the new import-time rejection behavior for unsupported sigma dependencies. |
🧠 Review effort: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3388 +/- ##
==========================================
+ Coverage 80.06% 80.08% +0.02%
==========================================
Files 320 320
Lines 22680 22691 +11
Branches 1531 1531
==========================================
+ Hits 18158 18173 +15
+ Misses 4514 4510 -4
Partials 8 8
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
On CI, conservation laws for non-constant species are enabled (`GITHUB_ACTIONS`), so `A` is replaced by `tcl_A - B` and the rejected sigma depends on the conservation law total instead of `A`. The test now checks both cases explicitly, checks that the accepted sigmas are resolved with conservation laws, and compiles the model without them. Also, report unavailable symbols in sorted order, so the error message does not depend on set ordering. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sigmaz is evaluated without the event observables, so replacing states in event noise formulas by the event output generated uncompilable code. States are now only replaced in sigmay. State-dependent event noise formulas and event noise formulas that refer to the event observable itself are rejected at import (#3389, point 3). Adds `test_noise_formula_with_expression`, adapted from #3379, which checks sigmay, ssigmay and sigmaz for time-resolved and event noise formulas that refer to expressions (#3377), and `test_unsupported_sigmas_rejected` for the cases of #3389 (piecewise functions, splines, state- and event-observable-dependent event noise formulas). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Event assignments that reference expressions (`w`), e.g., targets of SBML assignment rules, resulted in uncompilable `deltax` code, as `deltax` is evaluated without access to `w`. Replace expressions by their definitions before substituting `x` by `x_old`, reusing the substitution for noise formulas from AMICI-dev#3388. This also fixes the JAX backend's `_delta_x`. Fixes AMICI-dev#3412. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Event assignments that reference expressions (`w`), e.g., targets of SBML assignment rules, resulted in uncompilable `deltax` code, as `deltax` is evaluated without access to `w`. Replace expressions by their definitions before substituting `x` by `x_old`, reusing the substitution for noise formulas from AMICI-dev#3388. This also fixes the JAX backend's `_delta_x`. Fixes AMICI-dev#3412. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dev#3416) Fixes AMICI-dev#3412. Event assignments that reference expressions (`w`), e.g., targets of SBML assignment rules (`y := 3 * x`, `E1: at time > 1: v = v + y`), produced uncompilable code (`deltax.cpp: 'y_' was not declared in this scope`), since `deltax` is evaluated without access to `w`. `Event.get_state_update` now takes a function that replaces expressions by their (recursively expanded) definitions, which is applied before substituting `x` by `x_old`. This reuses the substitution AMICI-dev#3388 added for noise formulas, moved to `DEModel._get_expression_substituter`. Since the fix is in `DEModel`, it also applies to the JAX backend's `_delta_x` for expressions that only depend on states, parameters and conservation law totals. JAX's `_delta_x` doesn't get `t` and the Heaviside variables, so event assignments that depend on `time` or `piecewise`, directly or via expressions, still fail there, as on `main` (AMICI-dev#3423). Event assignments using expressions that contain splines still don't compile (`deltax.cpp: 'spl_0_' was not declared in this scope`), as on `main`, since only `w` and `dwdx` get the spline values (AMICI-dev#3424). The new test `test_event_assignment_with_expressions` uses a chain of assignment rules depending on a state and a parameter. It checks the simulated values against the analytical solution, and forward and adjoint sensitivities against finite differences. On `main`, model compilation fails. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>


Fixes #3377.
Fixes #3384.
Fixes #3389.
Supersedes #3379.
Problem
Noise formulas (
sigmay/sigmaz) that reference expressions (w), e.g., targets of SBML assignment rules, passed import but produced uncompilable code:The generated
sigmay(t, p, k, y)/sigmaz(t, p, k)functions (and JAX_sigmay(y, p, np)) do not receivew.DEModelonly checked sigmas for states, so expressions went straight to code generation. As a consequence,dsigmaydpwas also wrong, since it lacked the parameter dependence of the expression. This also affected expressions that depend only on parameters (#3377, e.g.,sigma = swiths := 0.1 * p) and event noise formulas.Related cases that were accepted at import but did not compile (#3389): noise formulas with piecewise functions (no Heaviside argument
h), splines (no spline values), and state-dependent event noise formulas (states were replaced by the event outputz, butsigmazhas nozargument).Changes
In
DEModel._compute_equation, post-processing ofsigmay/sigmaz:sigmay, the observable equation that the existing state elimination solves for the state is expanded the same way.sigma = sd*readoutwithobs = readout,readout := Anow becomessd*obs; withobs = scale*readoutit becomessd*obs/scale.sigmazare no longer replaced by the event output, sincesigmazis evaluated without it (Noise formulas with piecewise functions, splines, or state-dependent event noise formulas generate non-compiling code #3389, point 3). State-dependent event noise formulas, and event noise formulas that refer to the event observable itself, are now rejected at import.h, Noise formulas with piecewise functions, splines, or state-dependent event noise formulas generate non-compiling code #3389 point 1), spline values (spl, Noise formulas with piecewise functions, splines, or state-dependent event noise formulas generate non-compiling code #3389 point 2), conservation law totals (tcl), and, forsigmaz, the event observables. These now fail at import with an informative message instead of failing in the compiler. All of these cases were compile errors before, so nothing that worked before is rejected now.The fix lives in
DEModel, so it covers the SBML and PySB importers and both the C++ and JAX backends.Notes for reviewers
__cause__of the genericSBMLExceptionraised insbml2amici; I left that wrapping unchanged.wsymbols that actually occur, until none remain, instead of usingsmart_subs_dictover all ofw. That avoids O(n_w) or O(n_w²) substitution work for large models.test_sigma_depending_on_assignment_rulesruns withrtol=atol=1e-12. The sigmas there are small and proportional to the observable, so at default tolerances the integration error (~4e-7 relative in the state) is amplified enough to make the finite-difference check miss by ~1e-4. At tight tolerances, FSA, ASA, and finite differences agree to ~1e-9.PEtab Testsuite (3.14)failed on earlier commits of this PR, as it did onmain, because libpetab-python#545 changed how numeric overrides are serialized (test_flatten_timepoint_specific_output_overrides). This was fixed onmainby Fix flattened observable IDs in PEtab v2 test #3394, which is now merged into this branch.Testing
New tests in
python/tests/test_sbml_import.py:test_sigma_depending_on_assignment_rules(Noise formula referencing an assignment-rule parameter generates uncompilable sigmay code #3384): simulatedsigmayvalues and FSA/ASA gradient checks for sigmas referencing state- and parameter-dependent assignment rules. It also checks that a sigma whose state cannot be expressed in terms of the observable is rejected, both without conservation laws and with them, where the conservation law total remains. Conservation laws for non-constant species are enabled explicitly, so the test does not depend onGITHUB_ACTIONS.test_noise_formula_with_expression(Generated code does not compile if a noise formula refers to an expression #3377, adapted from Fix noise formulas that refer to model expressions #3379):sigmay,ssigmay, andsigmazfor time-resolved and event noise formulas that refer to expressions, including a state-dependent one forsigmay.test_unsupported_sigmas_rejected(Noise formulas with piecewise functions, splines, or state-dependent event noise formulas generate non-compiling code #3389): piecewise functions, splines, and event noise formulas depending on states or on the event observable itself are rejected at import with a message naming the offending symbol.Locally, the sigma-related tests in
test_sbml_import.pypass withGITHUB_ACTIONS=true. The local AMICI build predates the latest C++ changes onmain, which do not affect these tests; the full suite runs on CI.🤖 Generated with Claude Code