Add Hager-Zhang and More-Thuente line searches - #87
Draft
ChrisRackauckas-Claude wants to merge 2 commits into
Draft
Add Hager-Zhang and More-Thuente line searches#87ChrisRackauckas-Claude wants to merge 2 commits into
ChrisRackauckas-Claude wants to merge 2 commits into
Conversation
Line search algorithms in this package were tied to root finding in two ways: every `init` dispatched on `AbstractNonlinearProblem`, and each one inlined the merit `ϕ(α) = ‖F(u + α du)‖²/2` into its own closures. An `OptimizationProblem` could not enter at all — it is not a subtype of `AbstractNonlinearProblem` — so an optimizer wanting Wolfe conditions had to reimplement the searches. The scalar function is the only merit-specific part of any of these algorithms. This factors it out: - `AbstractMerit` with `ResidualMerit` (‖F‖²/2, ϕ' via a JVP) and `ObjectiveMerit` (f itself, ϕ' = ⟨∇f, du⟩ against a gradient the caller usually already has, rather than a Hessian-vector product). - `MeritEvaluator`, built once by `init_merit` and reused, so a search allocates nothing in steady state. - `init(prob::OptimizationProblem, alg, u)` alongside the existing nonlinear entry point, for BackTracking, StrongWolfe and GoldenSection. Two related interface gaps are closed at the same time: - `LineSearchSolution` gains optional `ϕ` and `dϕ` fields, and the evaluator is left holding the accepted point. Callers previously had to re-evaluate the objective to recover its value and gradient at the accepted step — a full derivative pass per outer iteration. - `solve!` accepts `ϕ0`/`dϕ0` keywords, and `set_initial_step!` sets the first trial step. Every algorithm re-evaluated at α = 0 on entry even though the caller had just computed it; on a quasi-Newton method that roughly doubled the gradient evaluations per iteration. Derivative-free searches keep working without an AD backend: `init_merit` takes `need_deriv`, and GoldenSection passes `false` rather than being forced to construct a Jacobian operator it never uses. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Neither was available here. Both are written against `AbstractMerit`, so a single implementation serves `NonlinearProblem`s and `OptimizationProblem`s; a test asserts the two merits produce identical step sizes on a problem stated both ways. Hager-Zhang (SIAM J. Optim. 16(1), 2005) accepts a step satisfying either the original Wolfe conditions or the approximate Wolfe conditions. The latter stay testable once ϕ(α) - ϕ(0) has fallen to roundoff, which is where the original conditions become unsatisfiable — near a minimizer. The regression test pins this to a case where it is unambiguous: log(cosh(z)) underflows to exactly 0.0 for |z| <= 1e-8 while tanh(z) is still nonzero, so ϕ carries no detectable decrease and a strict strong-Wolfe search stalls above the gradient floor. More-Thuente follows MINPACK-2 dcsrch/dcstep, minimizing the auxiliary function ψ(α) = ϕ(α) - ϕ(0) - ftol α ϕ'(0) during its first stage. Both judge bracket collapse relative to the bracket's own magnitude rather than against an absolute eps(T) floor. With a large gradient the optimal step is legitimately far below machine epsilon — on the variably_dimensioned problem at n = 1000 it is ~1e-22 — and an absolute floor there ends the search before it starts. Verified against Optim.jl over 45 unconstrained problems (Moré-Garbow- Hillstrom plus scalable and applied ones) driving an external L-BFGS: matching or better solve counts, evaluation counts within a few percent, and zero allocations per solve. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
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.
What this adds
HagerZhangLineSearchandMoreThuenteLineSearch. Neither existed here; the only route to Hager–Zhang was theLineSearchesJLwrapper.Both are written against the
AbstractMeritabstraction from #86, so one implementation servesNonlinearProblems andOptimizationProblems. A test states a problem both ways and asserts the two merits produce identical step sizes.Why Hager–Zhang matters
It accepts a step satisfying either the original Wolfe conditions or the approximate Wolfe conditions
The approximate form stays testable once
ϕ(α) − ϕ(0)has fallen to roundoff — exactly where the original conditions become numerically unsatisfiable, near a minimizer.The regression test pins this to a case where it is unambiguous rather than statistical:
f(x) = Σ log(cosh(xᵢ − 1))underflows to exactly0.0for|xᵢ − 1| ≲ 1e-8, while∇f = tanh(xᵢ − 1)is still nonzero.ϕcarries no detectable decrease at all, so a strict strong-Wolfe search stalls above the gradient floor while Hager–Zhang still accepts.MoreThuenteLineSearchfollows MINPACK-2dcsrch/dcstep, minimizingψ(α) = ϕ(α) − ϕ(0) − ftol·α·ϕ'(0)during its first stage.One subtle detail worth review
Both judge bracket collapse relative to the bracket's own magnitude, not against an absolute
eps(T):With a large gradient the optimal step is legitimately far below machine epsilon. On the Moré–Garbow–Hillstrom
variably_dimensionedproblem at n = 1000 it is ~1e-22, and an absolute floor at 2.2e-16 ends the search before it begins — the solver fails on iteration 1 without moving. This also removes spurious line-search failures well away from that extreme.Verification
Driving an external L-BFGS over 45 unconstrained problems (Moré–Garbow–Hillstrom plus scalable and applied ones), against
Optim.jlwith the identical fused value+gradient closure, starting points, per-problem gradient tolerance and caps:Zero allocations per solve at every problem size, and 1.1×–2.4× faster wall clock with analytic gradients. A solve counts only if it reaches the tolerance and an objective as good as the best any solver found — a small gradient alone is not convergence, since
exp-based objectives can underflow to a flat plateau where∇fis identically zero far from any minimizer.Existing
CoreandLineSearchesJLgroups pass unchanged. Runic clean.🤖 Generated with Claude Code