Skip to content

Gradient fit aborts on incidental non-differentiable actions (unscored parameter_scan / non-ODE simulate) #475

Description

@wshlavacek

Summary

On a gradient-based fit (fit_type = trf / lbfgs), PyBNF enables a sensitivity request on the whole model, and any action that cannot carry forward sensitivities aborts the entire fit — even when that action's output is not a scored gradient target.

Triggers (both raised during model.execute())

  • Carried-state scan (simulate()parameter_scan() with no reset between them) → PybnfError at pybnf/bngsim_model/net_model.py:1178 (_scan_carried_state).
  • Non-ODE-method action (ssa / psa / nfsim) → PybnfError at pybnf/bngsim_model/net_model.py:309-316 (_sensitivity_request_kwargs).

Both guards fire whenever self._sensitivity_request is not None — i.e. on every gradient fit — regardless of whether the offending action's output is actually scored against data.

Why this is a bug

A model whose fit target is a differentiable ODE time course, but which also carries an incidental action (a stochastic diagnostic simulate, or a pre-equilibrated simulateparameter_scan block) that is never scored, cannot be gradient-fit at all. The unscored action needs no sensitivities, yet it aborts the run.

This also contradicts the module's own documented intent in pybnf/algorithms/optimizers/gradient_base.py:320-328:

a non-ODE simulation method (SSA / NFsim) is likewise non-differentiable, but the method is an action-level property (a model can mix actions) rather than a model-structure one, so it is not hoisted here; it keeps its existing clean per-evaluation refusal

The intent is that only sensitivity-bearing (scored) actions should require differentiability. The implementation over-reaches by forcing a sensitivity request onto every action in the model.

Proposed fix

Gate sensitivity-enabling per action on whether that action's output is a scored gradient target. The scoring↔action mapping is already available — gradient_base.py:447-452 intersects res.simdata with exp_data during gradient assembly. Run incidental / unscored non-differentiable actions on the ordinary path with sensitivities off, so they neither compute wasted sensitivity tensors nor abort the fit.

Reproduction sketch

Edition-2 gradient fit (fit_type = trf or lbfgs). Model has:

  • one scored ODE simulate action mapped to time-course data, plus
  • one unscored action that is either non-ODE (nfsim / ssa) or a carried-state simulateparameter_scan block.

The fit aborts at the first evaluation with the PybnfError from one of the guards above, even though the scored objective is fully differentiable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions