What
model.save_state() / load_state() restore fields but not the values of uw.expression parameters. TranscriptAdjoint replays a run by restoring each step's snapshot and re-solving, so any parameter that changed later in the run is replayed at its final value, not the value that step actually used.
The adjoint then assembles dR/du about a state the forward run never visited, and returns a gradient for a different problem. No error, no warning.
Demonstrated
st.solve() # eta = 1.0
snap = model.save_state()
eta.sym = sympy.Float(99.0) # a later-step parameter change
model.load_state(snap)
eta.sym # 99.0 <-- not 1.0
at save_state : eta = 1.00000000000000
after change : eta = 99.0000000000000
after load_state(snap) : eta = 99.0000000000000
Why this is severe
Any run that changes a parameter mid-run is affected, and those are exactly the runs people take adjoints of:
- a continuation or homotopy ladder (the parameter IS the ladder);
- a ramped boundary condition or body force;
- time-dependent material properties;
- an inversion that steps a control between iterations.
The failure is silent and plausible: the replayed solve converges, the transcript records it as normal (see #772 — the transcript never records parameter values either, so nothing in the record contradicts it), and the gradient is simply wrong. A Taylor test done at the END of the run would pass, because there the final value IS the current value; the error only appears for steps before the last change.
Relationship to #772
#772 is the reporting half: the transcript fingerprints the symbolic form and never the values, so an invisible parameter change leaves no trace. This is the correctness half: replay cannot restore what was never recorded. Recording the change (the preferred fix in #772) is also what makes replay correct, since the replay can then re-apply each step's parameter values before re-solving.
They should probably be fixed together: log expr.sym assignments as transcript events, and have load_state / the replay path re-apply the values in force at that step.
Acceptance
A test that: solves at p = a, snapshots, changes to p = b, replays the snapshot, and asserts the replayed solve used a — and a TranscriptAdjoint gradient over a run with a mid-run parameter change that passes a Taylor test at an EARLY step, not only the last one.
What
model.save_state()/load_state()restore fields but not the values ofuw.expressionparameters.TranscriptAdjointreplays a run by restoring each step's snapshot and re-solving, so any parameter that changed later in the run is replayed at its final value, not the value that step actually used.The adjoint then assembles
dR/duabout a state the forward run never visited, and returns a gradient for a different problem. No error, no warning.Demonstrated
Why this is severe
Any run that changes a parameter mid-run is affected, and those are exactly the runs people take adjoints of:
The failure is silent and plausible: the replayed solve converges, the transcript records it as normal (see #772 — the transcript never records parameter values either, so nothing in the record contradicts it), and the gradient is simply wrong. A Taylor test done at the END of the run would pass, because there the final value IS the current value; the error only appears for steps before the last change.
Relationship to #772
#772 is the reporting half: the transcript fingerprints the symbolic form and never the values, so an invisible parameter change leaves no trace. This is the correctness half: replay cannot restore what was never recorded. Recording the change (the preferred fix in #772) is also what makes replay correct, since the replay can then re-apply each step's parameter values before re-solving.
They should probably be fixed together: log
expr.symassignments as transcript events, and haveload_state/ the replay path re-apply the values in force at that step.Acceptance
A test that: solves at
p = a, snapshots, changes top = b, replays the snapshot, and asserts the replayed solve useda— and aTranscriptAdjointgradient over a run with a mid-run parameter change that passes a Taylor test at an EARLY step, not only the last one.