You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of #385 (cut 1, step 3 of A→B→C). Depends on #447 and #448. Closes the common case and unblocks #386.
What
Assemble, from #447's per-Result sensitivity tensor and #448's per-experiment routing, both forms of
the gradient for the default Gaussian, LINEAR scale, fixed-σ objective, summed across experiments:
scipy.least_squares minimizes ½‖ρ‖² with the same ρ/J, so the residual form and the scalar
form agree by construction — the optimizer walks the surface PyBNF reports. Assembly threads through
the existing per-point objective evaluation (LikelihoodObjective.eval_point, objective.py:723) so
the gradient is the exact derivative of the loss PyBNF reports — same σ-weighting, same column selection.
Native → sampling space (once)
Apply the θ↔u Jacobian exactly once (ADR-0029): priors/scale.py already exposes a JAX-differentiable inverse_jax per scale (scale.py:38/61/75/91), so ∂θ/∂u is one autodiff away — no hand-written
per-scale derivative, no double transform. ρ is scale-invariant; J → J·diag(∂θ/∂u).
Reaching the optimizer
The gradient must arrive through the async propose/score loop (Algorithm, algorithms/base.py:69; make_job/got_result), not a blocking call — consumed by #386, but the assembly API here must fit
that loop (return the residual/Jacobian alongside the scored result).
Deliverables
Objective (and Constraint stub) methods producing scalar ∂F/∂θand residual +
residual-Jacobian, summed across experiments, for Gaussian/fixed-σ/LINEAR.
Native→sampling transform applied once via priors/scale.py (ADR-0029).
FD test (acceptance gate): central differences of PyBNF's own loss(θ) vs assembled grad_sampling on a 2–3 parameter net model — wildtype + one conditioned experiment —
to a tight tolerance.
User-guide section: enabling sensitivities, the ~(1+P) cost factor, the capability gate.
Out of scope (deferred layers, each its own issue under #385)
Part of #385 (cut 1, step 3 of A→B→C). Depends on #447 and #448. Closes the common case and unblocks #386.
What
Assemble, from #447's per-Result sensitivity tensor and #448's per-experiment routing, both forms of
the gradient for the default Gaussian, LINEAR scale, fixed-σ objective, summed across experiments:
∂F/∂θ(for quasi-Newton / L-BFGS-B), andConvention pin
PyBNF Gaussian loss (
noise/gaussian.py:52-54):data_fit = r²/(2σ²),r = μ(pred) − additive_on.forward(obs); for default (LINEAR, MEDIAN) Gaussian,μ = predexactly(
gaussian.py:15-17, symmetric ⇒ smooth, no trajectory centering). Therefore:ρ_i = (pred_i − obs_i)/σ_i,loss = ½‖ρ‖²J_ij = (1/σ_i)·(factor_j)·∂pred_i/∂θ_j(native;factor_jfrom B — Gradient plumbing: route free params to sensitivity_params/sensitivity_ic (bind-by-id + condition chain-rule) #448)∂loss/∂θ = Jᵀρ(not2Jᵀρ)scipy.least_squaresminimizes½‖ρ‖²with the sameρ/J, so the residual form and the scalarform agree by construction — the optimizer walks the surface PyBNF reports. Assembly threads through
the existing per-point objective evaluation (
LikelihoodObjective.eval_point,objective.py:723) sothe gradient is the exact derivative of the loss PyBNF reports — same σ-weighting, same column selection.
Native → sampling space (once)
Apply the θ↔u Jacobian exactly once (ADR-0029):
priors/scale.pyalready exposes a JAX-differentiableinverse_jaxper scale (scale.py:38/61/75/91), so∂θ/∂uis one autodiff away — no hand-writtenper-scale derivative, no double transform.
ρis scale-invariant;J → J·diag(∂θ/∂u).Reaching the optimizer
The gradient must arrive through the async propose/score loop (
Algorithm,algorithms/base.py:69;make_job/got_result), not a blocking call — consumed by #386, but the assembly API here must fitthat loop (return the residual/Jacobian alongside the scored result).
Deliverables
Objective(andConstraintstub) methods producing scalar∂F/∂θand residual +residual-Jacobian, summed across experiments, for Gaussian/fixed-σ/LINEAR.
priors/scale.py(ADR-0029).loss(θ)vs assembledgrad_samplingon a 2–3 parameter net model — wildtype + one conditioned experiment —to a tight tolerance.
Out of scope (deferred layers, each its own issue under #385)
Estimated σ (layer D); LOG10/lognormal scale objective (layer E); normalization & cumulative→incident
(layer F); asymmetric families (layer G); SBML/Antimony (layer H); constraint/comparison gradients
(layer I); pre-equilibration/steady-state (layer J); the optimizer itself (#386).