Skip to content

Default Newton-Krylov Auto warm starts to guarded Hegedüs - #4040

Merged
ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:agent/fix-4034-warmstart
Aug 9, 2026
Merged

ChrisRackauckas merged 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:agent/fix-4034-warmstart

Conversation

@ChrisRackauckas-Claude

@ChrisRackauckas-Claude ChrisRackauckas-Claude commented Jul 27, 2026 •

Copy link
Copy Markdown
Member

This PR should be ignored until reviewed by @ChrisRackauckas.

Fixes #4034. Scope changed after SciML/LinearSolve.jl#1123 merged — please re-read.

The bug

#3991 resolved WarmStart.Auto to WarmStart.Hegedus on the Newton path. The
guess there is the previous Newton increment, which converges to zero by
construction — so once Newton converges the guess is pure round-off, while the
Hegedüs scalar ξ = ⟨Au,b⟩/⟨Au,Au⟩ is ill-conditioned exactly where it is
applied (ξ measured over 3.9e-32 … 5.1e+31; |cos∠(Au,b)| down to 2e-16).
GMRES then returns the rescaled noise verbatim at iteration 0 on ~37% of solves,
and it feeds the SDIRK error estimate and step-size control.

Result: a 1-ulp input change moved the Hairer4 solution by 3.7e-03.

What changed since the first revision

The first revision gated the default behind isdefined(LinearSolve, :HEGEDUS_MIN_RESIDUAL_REDUCTION). SciML/LinearSolve.jl#1123 has since merged and
released in 5.8.0, but named the constant _HEGEDUS_MAX_RESIDUAL_RATIO —
internal, no public declaration. That gate would therefore have been false
forever, silently disabling the warm start for good.

Rather than probe someone else's internal, this now states the requirement where
it belongs — a [compat] floor:

LinearSolve = "5.8"

default_krylov_warm_start resolves Auto → Hegedus unconditionally again; the
resolver, not a runtime check, guarantees the guard is present.

Verification (LinearSolve 5.8.0, the released guard)

1-ulp perturbation of the #4034 DAE, exercising the Auto default this PR
resolves — no explicit warm_start:

max solution change
Auto (this branch) 1.33e-15
unguarded Hegedüs (master) 3.73e-03
cold start 6.64e-08

Better than a cold start, because the guard rejects precisely the guesses that
were injecting noise.

Performance

The guarded default is also the fastest of the three. 1D Brusselator,
nsolve identical within each pair:

cold unguarded guarded
KenCarp4, N=400 202,145 175,852 158,259 (−22%)
TRBDF2, N=800 273,379 396,253 288,297 (+5%)

At identical accuracy — KenCarp4 final-state error against a 1e-13 reference is
3.5894789585e-7 cold vs 3.5894791672e-7 guarded, so the iteration savings are
real work saved, not a weaker stopping test.

TRBDF2's +5% is within noise of a cold start rather than a win; worth knowing
which workloads Hegedüs actually helps, but that is a tuning question now, not a
correctness one.

Also tried and rejected

Warm-starting only the first Newton iteration of each step (cold-starting the
rest) — the obvious fix if the problem is only converged increments. It reduced
the amplification just 3.73e-03 → 1.69e-03, still ~25,000× worse than cold,
because the first solve of each step inherits the previous step's converged
increment. Not carried.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TPHRh64BLfoXSzQ3AxKNwC

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Member Author

Full local test results (all run after the change, on Julia 1.12.6):

suite result
lib/OrdinaryDiffEqDifferentiation Pkg.test() exit 0 — incl. Krylov warm_start default 7/7
lib/OrdinaryDiffEqSDIRK Pkg.test() exit 0 — Tableau 57/57, Stage predictors 115/115, Convergence 102 pass / 2 pre-existing broken, DAE 8/8
lib/OrdinaryDiffEqBDF Pkg.test() exit 0
lib/OrdinaryDiffEqNonlinearSolve Pkg.test() exit 0
umbrella GROUP=Regression_I exit 0 — Dense 4104/4104, Special Interp 30/30, Inplace 2/2, Adaptive 46/46, Hard DAE 36/36, Newton-Krylov Round-off 6/6
LinearSolve GROUP=Core (against the patched LinearSolve) exit 0 — Krylov warm start 40/40

The new regression test was additionally run standalone under both configurations it has to
survive — released LinearSolve 5.2.0 (cold start) and the locally patched LinearSolve
(guarded Hegedüs) — 3/3 for each of Hairer4 and Hairer42 in both.

🤖 Generated with Claude Code

@ChrisRackauckas-Claude ChrisRackauckas-Claude changed the title Require a guarded Hegedüs warm start before defaulting to it (fixes #4034) Keep Newton-Krylov Auto warm starts cold Jul 29, 2026

Copy link
Copy Markdown
Member Author

The branch has been rewritten on current clean master (938dae56bf) after the full comparison. The earlier self-rearming/guard-detection design is superseded.

Final decision:

The reason is performance, not only robustness: on the matrix-free n=800 Brusselator, guarded Hegedüs still regressed versus cold by +12.7% nf / +4.4% time (KenCarp4), +30.0% / +39.3% (TRBDF2), and +7.5% / +11.3% (FBDF). It remains useful on some correlated systems, but is not a sound automatic default.

Exact final local verification on Julia 1.12.6:

group / check result
umbrella GROUP=Regression_I passed; new one-ulp test 6/6
OrdinaryDiffEqNonlinearSolve_Core passed; Homotopy 25/25, Homotopy init/default 16/16
OrdinaryDiffEqNonlinearSolve_QA passed; JET + Aqua
OrdinaryDiffEqDifferentiation_Core passed
repository-wide Runic 1.7 --check passed
companion LinearSolve Core + QA passed, including Allocation QA 48/48 and SupernodalLU Allocation QA 8/8

The clean-master standalone regression was also run and failed at Hairer4 drift 0.003734637491723536, while the final cold-Auto branch passed the <1e-5 regression. An exact explicit guarded-Hegedüs rerun against #1123 produced Hairer4 drift 1.33e-15 (nf=617), Hairer42 1.33e-15 (nf=625), and Cash4 2.77e-9 (nf=740).

Copy link
Copy Markdown
Member Author

CI note: the immediate documentation, downstream, and ImplicitDiscreteSolve downgrade failures are the clean-master NonlinearSolveBase compatibility conflict fixed independently in #4051. The failing resolver requires NonlinearSolveBase >=2.40 from ImplicitDiscreteSolve while OrdinaryDiffEqNonlinearSolve currently caps the locally available range at 2.38. These setup failures occur before this PR's tests run and are unrelated to the warm-start diff; #4040 remains intentionally unstacked on the small prerequisite.

SciML#3991 resolved `WarmStart.Auto` to `WarmStart.Hegedus` on the Newton path. The
guess there is the previous Newton increment, and those converge to zero, so
once Newton has converged the guess is pure round-off while the Hegedüs scalar
`ξ = ⟨Au,b⟩/⟨Au,Au⟩` is ill-conditioned exactly where it is applied. A 1-ulp
input change moved the Hairer4 solution by 3.7e-03 (SciML#4034).

SciML/LinearSolve.jl#1123, released in 5.8.0, fixes that at the source: a
Hegedüs guess is discarded unless it reduces the residual by at least half. Pin
that with a `[compat]` floor rather than feature-detecting it, so the guarantee
is expressed in the resolver instead of in a runtime probe of a LinearSolve
internal.

Verified against LinearSolve 5.8.0, 1-ulp perturbation of the SciML#4034 DAE:

    Hairer4, Auto (this branch)   1.33e-15
    Hairer4, unguarded Hegedus    3.73e-03
    Hairer4, cold start           6.64e-08

The guarded default is also the fastest of the three on the workloads measured:
1D Brusselator KenCarp4 (N=400) nf 158_259 guarded vs 175_852 unguarded vs
202_145 cold, at identical accuracy against a 1e-13 reference.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas-Claude ChrisRackauckas-Claude changed the title Keep Newton-Krylov Auto warm starts cold Default Newton-Krylov Auto warm starts to guarded Hegedüs Aug 9, 2026
@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review August 9, 2026 14:09
@ChrisRackauckas
ChrisRackauckas merged commit 1c85484 into SciML:master Aug 9, 2026
48 of 183 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hegedüs warm-start default (#3991) amplifies round-off ~1e13x on the Newton-Krylov path (GPU DAE test failure)

2 participants