Skip to content

A parameter fixed by an SBML initialAssignment goes stale on the fast path, so a fitted dependency simulates the wrong model #531

Description

@wshlavacek

Summary

On the sbml_backend = bngsim fast path (the cached-clone + set_param route added in #415), a
parameter whose value is fixed by an initialAssignment is never recomputed. bngsim evaluates
the assignment once when the engine template is loaded, and set_param does not propagate to it, so
the derived parameter keeps its load-time value for the whole fit even when the parameters it is
derived from are fitted or set by a condition:.

_compute_initial_dependency_names only ever seeded its dependency set from initialAssignments on
species; an assignment on a parameter was collected into expr_refs but never marked as needing
a recompute.

Impact

The simulated model silently disagrees with what the same job produces through the reload path (and
with what any other PEtab tool produces). It is a scalar-path bug: it corrupts the objective for
every optimizer, not just the gradient ones.

Bertozzi_PNAS2020 (Benchmark-Models-PEtab, in the Grein et al. 2026 subset) is the worked example:

<initialAssignment symbol="beta_N">   <!-- beta_N = R0_ * gamma_ / N_ -->

beta_N is the only place R0_ reaches the dynamics, and the conditions set
R0_ = R0_CA / R0_ = R0_NY and N_ = 39560000. Observed before the fix:

base I_   : [5.00e+02 1.46e+07 5.35e+06 1.97e+06]
R0_ 3 -> 3.3    max trajectory delta = 0        # R0_ is completely inert
engine beta_N (fast path)  = 0.01               # the load-time value (R0_=0.1, gamma_=0.1, N_=1)
engine beta_N (reload)     = 8.34e-09           # the correct R0_*gamma_/N_

Two of the eight free parameters (R0_CA, R0_NY) therefore had an identically flat objective, and
the reported nominal NLL was 1.79e11 against a reference J* = 158.86. With the fix the fast path
is byte-identical to the reload path and the trajectory tracks the data.

Fix

Treat a parameter with an initialAssignment as the derived constant SBML says it is: seed the
initial-dependency set from parameter assignments as well as species ones, recompute both in
_recompute_initial_assignments (via libSBML's expandInitialAssignments, as the species initials
already are), and apply the recomputed parameter values to the engine clone alongside the
recomputed species initials. A parameter an assignmentRule or rateRule governs is excluded --
bngsim recomputes those dynamically, so an initial override would fight the rule.

Scope

bngsim SBML/Antimony backend only. The reload path (_needs_structural_reload) was already correct
and is the oracle the regression test compares against.

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