| // Copyright ©2014 The Gonum Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package optimize |
| |
| const ( |
| defaultBacktrackingContraction = 0.5 |
| defaultBacktrackingDecrease = 1e-4 |
| minimumBacktrackingStepSize = 1e-20 |
| ) |
| |
| var _ Linesearcher = (*Backtracking)(nil) |
| |
| // Backtracking is a Linesearcher that uses backtracking to find a point that |
| // satisfies the Armijo condition with the given decrease factor. If the Armijo |
| // condition has not been met, the step size is decreased by ContractionFactor. |
| // |
| // The Armijo condition only requires the gradient at the beginning of each |
| // major iteration (not at successive step locations), and so Backtracking may |
| // be a good linesearch for functions with expensive gradients. Backtracking is |
| // not appropriate for optimizers that require the Wolfe conditions to be met, |
| // such as BFGS. |
| // |
| // Both DecreaseFactor and ContractionFactor must be between zero and one, and |
| // Backtracking will panic otherwise. If either DecreaseFactor or |
| // ContractionFactor are zero, it will be set to a reasonable default. |
| type Backtracking struct { |
| DecreaseFactor float64 // Constant factor in the sufficient decrease (Armijo) condition. |
| ContractionFactor float64 // Step size multiplier at each iteration (step *= ContractionFactor). |
| |
| stepSize float64 |
| initF float64 |
| initG float64 |
| |
| lastOp Operation |
| } |
| |
| func (b *Backtracking) Init(f, g float64, step float64) Operation { |
| if step <= 0 { |
| panic("backtracking: bad step size") |
| } |
| if g >= 0 { |
| panic("backtracking: initial derivative is non-negative") |
| } |
| |
| if b.ContractionFactor == 0 { |
| b.ContractionFactor = defaultBacktrackingContraction |
| } |
| if b.DecreaseFactor == 0 { |
| b.DecreaseFactor = defaultBacktrackingDecrease |
| } |
| if b.ContractionFactor <= 0 || b.ContractionFactor >= 1 { |
| panic("backtracking: ContractionFactor must be between 0 and 1") |
| } |
| if b.DecreaseFactor <= 0 || b.DecreaseFactor >= 1 { |
| panic("backtracking: DecreaseFactor must be between 0 and 1") |
| } |
| |
| b.stepSize = step |
| b.initF = f |
| b.initG = g |
| |
| b.lastOp = FuncEvaluation |
| return b.lastOp |
| } |
| |
| func (b *Backtracking) Iterate(f, _ float64) (Operation, float64, error) { |
| if b.lastOp != FuncEvaluation { |
| panic("backtracking: Init has not been called") |
| } |
| |
| if ArmijoConditionMet(f, b.initF, b.initG, b.stepSize, b.DecreaseFactor) { |
| b.lastOp = MajorIteration |
| return b.lastOp, b.stepSize, nil |
| } |
| b.stepSize *= b.ContractionFactor |
| if b.stepSize < minimumBacktrackingStepSize { |
| b.lastOp = NoOperation |
| return b.lastOp, b.stepSize, ErrLinesearcherFailure |
| } |
| b.lastOp = FuncEvaluation |
| return b.lastOp, b.stepSize, nil |
| } |