Skip to content

Emit Python observables from the model's assignment rules - #35

Merged
jeliason merged 1 commit into
mainfrom
feature/emit-observables
Aug 3, 2026
Merged

Emit Python observables from the model's assignment rules#35
jeliason merged 1 commit into
mainfrom
feature/emit-observables

Conversation

@jeliason

@jeliason jeliason commented Aug 3, 2026

Copy link
Copy Markdown
Member

A consumer scoring the model against data has to recompute the derived quantities the data names: totals, lineages, volume fractions. Those are repeatedAssignment rules the C++ emitter already reads, so emitting Python from the same SBML keeps the two in step. Re-deriving them by hand is a second definition that drifts, and on the PDAC model a hand expansion of the aggregate module had already missed phi_collagen, which lives in a different module and is a nonlinear ratio.

  • resolve() closes a requested symbol set over the rules and returns the raw species it needs, which is the output contract for anything standing in for the simulator. Raises on unknown symbols and on cycles rather than dropping them.
  • emit() writes pure arithmetic, so the generated module needs no array library and runs on floats, numpy arrays or JAX tracers alike. Non-arithmetic operators are refused where the rule is first read, so the error names the rule instead of reporting std and max as missing symbols.
  • New emit-observables subcommand alongside generate and verify.

On PDAC: 35 symbols close to 30 states, 13 constants and 20 rules, and every aggregate identity reproduces exactly on random states.

A consumer scoring the model against data has to recompute the derived
quantities the data names: totals, lineages, volume fractions. Those are
repeatedAssignment rules the C++ emitter already reads, so emitting Python from
the same SBML keeps the two in step. Re-deriving them by hand is a second
definition that drifts, and on the PDAC model a hand expansion of the aggregate
module had already missed phi_collagen, which lives in a different module and is
a nonlinear ratio.

resolve() closes a requested symbol set over the rules and returns the raw
species it needs, which is the output contract for anything standing in for the
simulator. It raises on unknown symbols and on cycles rather than dropping them.

emit() writes pure arithmetic, so the module needs no array library and runs on
floats, numpy arrays or JAX tracers alike. Non-arithmetic operators are refused
where the rule is first read, so the error names the rule instead of reporting
'std' and 'max' as missing symbols.

On PDAC: 35 symbols close to 30 states, 13 constants and 20 rules, and every
aggregate identity reproduces exactly on random states.
@jeliason
jeliason merged commit b004f3b into main Aug 3, 2026
2 checks passed
@jeliason
jeliason deleted the feature/emit-observables branch August 3, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant