Summary
A model with discrete events cannot use the gradient path at all. bngsim's CVODES forward
sensitivity vectors are not reinitialised across a state-dependent jump, so they go silently stale at
and after an event fires; bngsim therefore refuses forward output sensitivities outright on such a
model rather than return wrong derivatives (bngsim GH #205), and PyBNF reads that as a pre-flight gate
(_require_differentiable_dynamics) and refuses gntr / trf / lbfgs up front.
#461 delivered that gate — the honest refusal — and is closed. Nothing currently tracks the
capability, which is why this exists. A refusal is the right behaviour in the absence of the
feature; it is not a substitute for it.
What it blocks
Smith_BMCSystBiol2013 (k=25, n=62) is the last remaining gradient refusal in the Grein et al.
2026 subset-I corpus. With #530/#531/#534 landed, every other slug in that collection either fits on
the gradient path or is a tuning question. Smith is on cmaes purely because the gradient is
unavailable, not because CMA-ES is the better method for it — and the Grein benchmark puts multi-start
gradient (MS+fides) on the podium, so a problem parked on a metaheuristic for a capability reason is
one where PyBNF is benchmarked with its weaker method for reasons unrelated to the optimizer. That is
the same argument #530 made for Bertozzi, and Bertozzi went from cmaes to solved on gntr at
OG = 5.4e-06.
The work
This is mostly upstream in bngsim, not in PyBNF: the sensitivity system has to reinitialise its
vectors at each event, applying the jump's Jacobian to the carried sensitivities
(S⁺ = ∂g/∂x · S⁻ for a state assignment x⁺ = g(x⁻), plus the time-derivative correction when the
trigger time itself depends on parameters). Once bngsim can supply them:
Note on scope
A parameter-dependent trigger time is the part to be careful about. A jump at a fixed time only
needs the jump Jacobian applied to the carried sensitivities. A trigger whose time depends on θ adds
a term through dt_event/dθ, and getting that wrong is exactly the silent-wrong-derivative failure the
current refusal exists to prevent. Worth splitting if the first half is cheap on its own.
Related: #461 (the gate this would lift), #385 (the gradient epic), #530/#534 (the condition-routing
and bind-by-id seeding work that cleared the other refusals), #535 (FD-verifying the gradient on real
corpus problems).
Summary
A model with discrete events cannot use the gradient path at all. bngsim's CVODES forward
sensitivity vectors are not reinitialised across a state-dependent jump, so they go silently stale at
and after an event fires; bngsim therefore refuses forward output sensitivities outright on such a
model rather than return wrong derivatives (bngsim GH #205), and PyBNF reads that as a pre-flight gate
(
_require_differentiable_dynamics) and refusesgntr/trf/lbfgsup front.#461 delivered that gate — the honest refusal — and is closed. Nothing currently tracks the
capability, which is why this exists. A refusal is the right behaviour in the absence of the
feature; it is not a substitute for it.
What it blocks
Smith_BMCSystBiol2013(k=25, n=62) is the last remaining gradient refusal in the Grein et al.2026 subset-I corpus. With #530/#531/#534 landed, every other slug in that collection either fits on
the gradient path or is a tuning question. Smith is on
cmaespurely because the gradient isunavailable, not because CMA-ES is the better method for it — and the Grein benchmark puts multi-start
gradient (
MS+fides) on the podium, so a problem parked on a metaheuristic for a capability reason isone where PyBNF is benchmarked with its weaker method for reasons unrelated to the optimizer. That is
the same argument #530 made for Bertozzi, and Bertozzi went from
cmaesto solved ongntratOG = 5.4e-06.The work
This is mostly upstream in bngsim, not in PyBNF: the sensitivity system has to reinitialise its
vectors at each event, applying the jump's Jacobian to the carried sensitivities
(
S⁺ = ∂g/∂x · S⁻for a state assignmentx⁺ = g(x⁻), plus the time-derivative correction when thetrigger time itself depends on parameters). Once bngsim can supply them:
where the current refusal lives)
_require_differentiable_dynamicsfrom a blanket refusal to a capability check, soa build that supports event-aware sensitivities is allowed through and an older one still
refuses cleanly
first, then a state-dependent trigger, which is the harder case because the trigger time moves
with the parameters
Smith_BMCSystBiol2013ongntrand record the result(Grein et al. 2026 subset-I: drive PyBNF optimizers to solved on the 19 unfitted problems wshlavacek/BNGL-Models#38)
Note on scope
A parameter-dependent trigger time is the part to be careful about. A jump at a fixed time only
needs the jump Jacobian applied to the carried sensitivities. A trigger whose time depends on θ adds
a term through
dt_event/dθ, and getting that wrong is exactly the silent-wrong-derivative failure thecurrent refusal exists to prevent. Worth splitting if the first half is cheap on its own.
Related: #461 (the gate this would lift), #385 (the gradient epic), #530/#534 (the condition-routing
and bind-by-id seeding work that cleared the other refusals), #535 (FD-verifying the gradient on real
corpus problems).