Quasi-Newton Method (BFGS)
| This documentation was generated with the assistance of AI. Please report any inaccuracies. |
Quasi-Newton (variable metric) methods share the same goal as
conjugate gradient methods — accumulate information from
successive line minimizations so that, for a quadratic form, of them reach the exact minimum — but
store and update that information differently: as a full approximation to the inverse
Hessian, rather than an set of vectors. This package implements the Broyden-Fletcher-Goldfarb-
Shanno (BFGS) variant, described in Press et al., 2007, Section 10.9 (page
521), as QuasiNewtonMultiOptimizer in the com.irurueta.numerical.optimization package.
From Newton’s method to "quasi" Newton
Newton’s method for finding a zero of the gradient uses the local quadratic approximation \(f(\mathbf{x}) \approx f(\mathbf{x}_i) + (\mathbf{x}-\mathbf{x}_i)^\top\nabla f(\mathbf{x}_i) + \tfrac{1}{2}(\mathbf{x}-\mathbf{x}_i)^\top A (\mathbf{x}-\mathbf{x}_i)\) to jump directly to where the gradient should vanish:
The true Hessian is not known, so quasi-Newton methods instead build up a sequence of matrices converging toward — ideally within iterations, as for a true quadratic form. This turns out to be not just a practical necessity but often genuinely better than using the real Hessian: far from the minimum, need not be positive-definite, and taking the literal Newton step could move uphill. Starting as the identity matrix (positive-definite and symmetric) and updating it in a way that provably preserves both properties guarantees the method always has a downhill direction available, and recovers Newton’s quadratic convergence once close enough to the minimum for to have converged.
The BFGS update
Writing and , the update is built to satisfy the secant condition — requiring the new approximation to behave, along the step just taken, exactly like would. The DFP formula (Davidon-Fletcher-Powell, an earlier variant) is:
where is the outer product. BFGS — empirically the more robust of the two in practice, and the variant this package implements — adds one further rank-one correction term:
The update is skipped on any iteration where is not comfortably positive — guarding against roundoff pushing toward becoming singular or indefinite.
An approximate line search
Unlike Powell’s method and conjugate gradient, the quasi-Newton method does not need an accurate 1D line minimization along the Newton direction — only enough progress to guarantee the function actually decreases. This package’s internal line search takes the full Newton step whenever it can, and backtracks (via a quadratic/cubic fit to the step already tried) whenever that step overshoots, is rejected by an Armijo-style sufficient-decrease condition, or exceeds a maximum allowed step length.
Library implementation
QuasiNewtonMultiOptimizer extends MultiOptimizer directly (its line search is the internal backtracking
routine above, not the shared LineMultiOptimizer/DerivativeLineMultiOptimizer machinery used by
Powell’s Method and Conjugate Gradient Methods):
-
setGradientListener(…)— supplies ; as with Conjugate Gradient Methods, wrapping a numericalGradientEstimatorhere works just as well as an analytic gradient. -
setStartPoint(double[])/getStartPoint()— the initial point. -
getTolerance()/setTolerance(double)— the convergence criterion on the (scaled) gradient norm; default3e-8. -
getIterations()— the number of iterations used, afterminimize()completes.
Up to ITMAX = 200 iterations are attempted; convergence is additionally checked on the step size between
iterations (TOLX), independent of the gradient-norm test.
| Press et al., 2007 reports that quasi-Newton methods tend to outperform conjugate gradient whenever the storage for the inverse-Hessian approximation is affordable — and work well even when the gradient itself is only numerically estimated, which is why the authors describe it as an effective first choice whether or not computing an analytic gradient is practical. |
Example
final var listener = new MultiDimensionFunctionEvaluatorListener() {
@Override
public double evaluate(final double[] point) {
final var dx = point[0] - 1.0;
final var dy = point[1] + 2.0;
return dx * dx + dy * dy;
}
};
final var gradientListener = new GradientFunctionEvaluatorListener() {
@Override
public void evaluateGradient(final double[] params, final double[] result) {
result[0] = 2.0 * (params[0] - 1.0);
result[1] = 2.0 * (params[1] + 2.0);
}
};
final var optimizer = new QuasiNewtonMultiOptimizer(
listener, gradientListener, new double[]{0.0, 0.0}, 3e-8);
optimizer.minimize();
final var xmin = optimizer.getResult(); // approximately [1.0, -2.0]
final var fmin = optimizer.getEvaluationAtResult(); // approximately 0.0