Add adaptive Jacobian reuse to first-order solvers#1072
Draft
ChrisRackauckas-Claude wants to merge 5 commits into
Draft
Add adaptive Jacobian reuse to first-order solvers#1072ChrisRackauckas-Claude wants to merge 5 commits into
ChrisRackauckas-Claude wants to merge 5 commits into
Conversation
Contributor
Author
|
Implementation scratchpad for commit
Benchmarking showed wins for expensive dense/sparse factorizations but overhead and policy sensitivity on small/problem-library cases, so the solver default remains exact Newton. |
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
6dd2157 to
99348eb
Compare
Contributor
Author
|
Published final stacked head Safety and history checks:
Final local outputs include:
CI is now running on the combined stack, which includes both focused prerequisite fixes before the unchanged feature commit. |
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.
Prompt
Add OrdinaryDiffEq-style Jacobian reuse heuristics to NonlinearSolve's first-order methods, benchmark the standalone nonlinear-solver behavior before choosing a default, and keep OrdinaryDiffEq's separately owned Jacobian/iteration-matrix lifecycle unaffected.
Summary
JacobianReuse(max_age = 10, max_residual_ratio = 1)policyjacobian_reuse = trueor a configured policyreinit!; explicitstep!(...; recompute_jacobian = true/false)decisions take precedenceNewtonRaphson already constructs
GeneralizedFirstOrderAlgorithm, so a second solver alias/type is unnecessary: the existing solver becomes adaptive only when the policy is supplied.Design and prior art
This builds on the iterator and recomputation controls from #345 and the manual reuse request in #247. The closed WIP in #206 found that unconditional/threshold reuse was not consistently beneficial on small problems. The bounded age follows the established modified-Newton shape used by KINSOL; explicit lag configuration is also established in PETSc SNES.
The policy is intentionally immutable and stateless; per-solve residual/age state lives in the cache. This lets one configured policy be shared safely by polyalgorithm members.
Prerequisite stack and baseline findings
The published branch is intentionally stacked, in this order:
The old CI failures were reproduced on clean master and bisected separately before being included here. The apparent Brown bad-Broyden “fix” was disproved: identical Julia/package/source/Ubuntu-x86 graphs diverge between EPYC 7502 and EPYC 9354. The invalid expectation PR #1077 was reverted to a zero diff and closed; corrected evidence is recorded in #1056. The pre-existing global broken expectation remains unchanged.
Benchmark/default decision
BenchmarkTools medians, one BLAS thread, warmed solves:
For the sparse Brusselator, age 5 reduced Jacobian evaluations 4→3 and factorizations 3→2, while nonlinear steps increased 3→7. Policy sensitivity is real: the 23-problem age-5 run regressed to 3.36 ms, and the n=2 case showed pure overhead. Matrix-free runs had identical Jacobian/factorization work counts and inconsistent timing noise, so no matrix-free benefit is claimed.
Therefore this PR keeps exact Newton and the default polyalgorithm unchanged.
jacobian_reuse = trueselects a conservative age-10 bounded policy; users with expensive concrete Jacobians can tune age and residual ratio.OrdinaryDiffEq isolation
OrdinaryDiffEqNonlinearSolve owns distinct
JandWrefresh decisions, including mass-matrix/γdtchanges and convergence history across time steps. Its explicitrecompute_jacobianBoolean overrides this standalone policy, whilereinit!resets standalone reuse state.Local integration checks ran the existing Robertson FBDF/TRBDF2 Jacobian-reuse test (8/8) and repeated both methods with
NewtonRaphson(jacobian_reuse = true). Both remained accurate and successful; FBDF observed 10 Jacobian calls versus 115 W builds, and TRBDF2 observed 7 versus 129.Validation
Final stacked commit:
99348eb0d.--check --diff .across the repository: passedGROUP=Core Pkg.test()for NonlinearSolveFirstOrder: passedGROUP=QA Pkg.test()for NonlinearSolveFirstOrder: 18/18GROUP=PolyAlgorithms Pkg.test()for NonlinearSolve: passed, including 24/24 basic and 46/46 problem-library assertionsGROUP=QA Pkg.test()for NonlinearSolve: 17/17GROUP=Core Pkg.test()for NonlinearSolveBase: passedGROUP=QA Pkg.test()for NonlinearSolveBase: 18/18A local full root Core run cannot be a green signal on this EPYC 7502 host because the pre-existing Brown bad-Broyden
@test_brokenunexpectedly passes here; the identical graph fails with residual 5.5 on the EPYC 9354 CI host. That master failure was investigated under #1056, and no platform/version gate or weakened expectation is included.Documenter expanded the new
@docs JacobianReuseentry and completed document checks. The strict full docs build then stopped at pre-existing external link checks:numerical.recipestimed out and GitHub returned HTTP 429 for a pinned CMINPACK source link. Link checking and failure behavior were left unchanged.Process