NonlinearSolveAlg: reuse stored J on dt-only W updates (do_newJW parity)#3820
Merged
ChrisRackauckas merged 2 commits intoJul 6, 2026
Merged
Conversation
The W-reuse path recomputed the Jacobian on every W update, including pure dt/gamma changes, so njacs tracked nw one-to-one (~10x the legacy count: on ROBER at 1e-8, NLNewton evaluates 9 Jacobians vs 94/95 for NSA). The legacy do_newJW policy separates the two decisions: a dt change only reassembles W = J - M/γdt from the stored J via jacobian2W! (O(nnz)); a fresh Jacobian is reserved for first use, always_new, or a diverged iteration. Apply the same split here: _update_nlsolvealg_W! takes a new_jac flag and skips the f.jac/jacobian! evaluation when only the γdt scaling changed. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
…e copies) Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Contributor
Author
|
CI triage: the failing lanes here are master-baseline, not from this change — StochasticDiffEq QA/downgrade + GPU lanes fail on all concurrent PRs including the 2-line #3816; the OrdinaryDiffEqNonlinearSolve Core lane failure is the pre-existing |
This was referenced Jul 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The W-reuse path recomputes the Jacobian on every W update, including pure dt/γ changes. The legacy
do_newJWpolicy separates the two decisions: a dt change only reassemblesW = J - M/γdtfrom the stored J viajacobian2W!(O(nnz)); a fresh Jacobian evaluation is reserved for first use,always_new, or a diverged iteration.Measured on the dense-AD 512-unknown brusselator at rtol 1e-8 (master):
FBDF+NLNewtonevaluates 1 Jacobian for the whole solve (nf = 2,764) whileFBDF+NSAevaluates 295 (nf = 152,393) — every dt-driven W refresh pays a full 512-dim AD Jacobian.Fix
_update_nlsolvealg_W!takes anew_jacflag;initialize!computesnew_jac = first_call || always_new || status === Divergenceseparately from the dt-change test, mirroringdo_newJW'sjbad/wbadsplit. dt-only updates reusecache.Jand only rerunjacobian2W!.The expected trade (same one NLNewton makes) is visible on cheap-J ROBER: user-jac calls drop ~100 → ~4, iteration counts rise to NLNewton's level (1433 vs NLNewton's 1320, from 911 with always-fresh J), wall time unchanged. On expensive-J problems it's decisive — dense brusselator NSA/NLNewton time ratios, master → with this series:
(together with #3819 and SciML/NonlinearSolve.jl#1003; the remaining TRBDF2/KenCarp4 gap is step-count inflation from dt-controller/η coupling, a separate issue.)
Tests
New
nsa_jacobian_reuse_tests.jlcounts calls to a user-suppliedjacon ROBER (thenjacsstat can't observe this — it's dominated by the inner analytic-jac closure copies, one per W update): assertsJAC_CALLS < stats.nw / 3. Verified the test fails without this change (JAC_CALLS ≈ nw, 2 failures) and passes with it. Solution accuracy asserted against a 1e-12 reference. Full-stack sparse brusselator + KLU reaches 1.00× of NLNewton.🤖 Generated with Claude Code