func (ls *LinesearchMethod) initNextLinesearch(loc *Location, xNext []float64) (EvaluationType, IterationType, error) { copy(ls.x, loc.X) var stepSize float64 if ls.first { stepSize = ls.NextDirectioner.InitDirection(loc, ls.dir) ls.first = false } else { stepSize = ls.NextDirectioner.NextDirection(loc, ls.dir) } projGrad := floats.Dot(loc.Gradient, ls.dir) if projGrad >= 0 { ls.evalType = NoEvaluation ls.iterType = NoIteration return ls.evalType, ls.iterType, ErrNonNegativeStepDirection } ls.evalType = ls.Linesearcher.Init(loc.F, projGrad, stepSize) floats.AddScaledTo(xNext, ls.x, stepSize, ls.dir) // Compare the starting point for the current iteration with the next // evaluation point to make sure that rounding errors do not prevent progress. if floats.Equal(ls.x, xNext) { ls.evalType = NoEvaluation ls.iterType = NoIteration return ls.evalType, ls.iterType, ErrNoProgress } ls.iterType = MinorIteration return ls.evalType, ls.iterType, nil }
// startingStepSize implements the algorithm for estimating the starting step // size as described in: // - Hairer, E., Wanner, G., Nørsett, S.: Solving Ordinary Differential // Equations I: Nonstiff Problems. Springer Berlin Heidelberg (1993) func startingStepSize(rhs Function, init, tmp *State, weight Weighting, w []float64, order float64, s *Settings) float64 { // Store 1 / (rtol * |Y_i| + atol) into w. weight(init.Y, w) d0 := s.Norm(init.Y, w) d1 := s.Norm(init.YDot, w) var h0 float64 if math.Min(d0, d1) < 1e-5 { h0 = 1e-6 } else { // Make the increment of an explicit Euler step small compared to the // size of the initial value. h0 = 0.01 * d0 / d1 } // Perform one explicit Euler step. floats.AddScaledTo(tmp.Y, init.Y, h0, init.YDot) // Evaluate the right-hand side f(init.Time+h, tmp.Y). rhs(tmp.YDot, init.Time+h0, tmp.Y) // Estimate the second derivative of the solution. floats.Sub(tmp.YDot, init.YDot) d2 := s.Norm(tmp.YDot, w) / h0 var h1 float64 if math.Max(d1, d2) < 1e-15 { h1 = math.Max(1e-6, 1e-3*h0) } else { h1 = math.Pow(0.01/math.Max(d1, d2), 1/(order+1)) } return math.Min(100*h0, h1) }
// initNextLinesearch initializes the next linesearch using the previous // complete location stored in loc. It fills loc.X and returns an evaluation // to be performed at loc.X. func (ls *LinesearchMethod) initNextLinesearch(loc *Location) (Operation, error) { copy(ls.x, loc.X) var step float64 if ls.first { ls.first = false step = ls.NextDirectioner.InitDirection(loc, ls.dir) } else { step = ls.NextDirectioner.NextDirection(loc, ls.dir) } projGrad := floats.Dot(loc.Gradient, ls.dir) if projGrad >= 0 { return ls.error(ErrNonNegativeStepDirection) } op := ls.Linesearcher.Init(loc.F, projGrad, step) if !op.isEvaluation() { panic("linesearch: Linesearcher returned invalid operation") } floats.AddScaledTo(loc.X, ls.x, step, ls.dir) if floats.Equal(ls.x, loc.X) { // Step size is so small that the next evaluation point is // indistinguishable from the starting point for the current iteration // due to rounding errors. return ls.error(ErrNoProgress) } ls.lastStep = step ls.eval = NoOperation // Invalidate all fields of loc. ls.lastOp = op return ls.lastOp, nil }
func (ls *LinesearchMethod) Iterate(loc *Location, xNext []float64) (EvaluationType, IterationType, error) { if ls.iterType == SubIteration { // We needed to evaluate invalid fields of Location. Now we have them // and can announce MajorIteration. copy(xNext, loc.X) ls.evalType = NoEvaluation ls.iterType = MajorIteration return ls.evalType, ls.iterType, nil } if ls.iterType == MajorIteration { // The linesearch previously signaled MajorIteration. Since we're here, // it means that the previous location is not good enough to converge, // so start the next linesearch. return ls.initNextLinesearch(loc, xNext) } projGrad := floats.Dot(loc.Gradient, ls.dir) if ls.Linesearcher.Finished(loc.F, projGrad) { copy(xNext, loc.X) // Check if the last evaluation evaluated all fields of Location. ls.evalType = complementEval(loc, ls.evalType) if ls.evalType == NoEvaluation { // Location is complete and MajorIteration can be announced directly. ls.iterType = MajorIteration } else { // Location is not complete, evaluate its invalid fields in SubIteration. ls.iterType = SubIteration } return ls.evalType, ls.iterType, nil } // Line search not done, just iterate. stepSize, evalType, err := ls.Linesearcher.Iterate(loc.F, projGrad) if err != nil { ls.evalType = NoEvaluation ls.iterType = NoIteration return ls.evalType, ls.iterType, err } floats.AddScaledTo(xNext, ls.x, stepSize, ls.dir) // Compare the starting point for the current iteration with the next // evaluation point to make sure that rounding errors do not prevent progress. if floats.Equal(ls.x, xNext) { ls.evalType = NoEvaluation ls.iterType = NoIteration return ls.evalType, ls.iterType, ErrNoProgress } ls.evalType = evalType ls.iterType = MinorIteration return ls.evalType, ls.iterType, nil }
func (ls *LinesearchMethod) Iterate(loc *Location) (Operation, error) { switch ls.lastOp { case NoOperation: // TODO(vladimir-ch): Either Init has not been called, or the caller is // trying to resume the optimization run after Iterate previously // returned with an error. Decide what is the proper thing to do. See also #125. case MajorIteration: // The previous updated location did not converge the full // optimization. Initialize a new Linesearch. return ls.initNextLinesearch(loc) default: // Update the indicator of valid fields of loc. ls.eval |= ls.lastOp if ls.nextMajor { ls.nextMajor = false // Linesearcher previously finished, and the invalid fields of loc // have now been validated. Announce MajorIteration. ls.lastOp = MajorIteration return ls.lastOp, nil } } // Continue the linesearch. f := math.NaN() if ls.eval&FuncEvaluation != 0 { f = loc.F } projGrad := math.NaN() if ls.eval&GradEvaluation != 0 { projGrad = floats.Dot(loc.Gradient, ls.dir) } op, step, err := ls.Linesearcher.Iterate(f, projGrad) if err != nil { return ls.error(err) } switch op { case MajorIteration: // Linesearch has been finished. ls.lastOp = complementEval(loc, ls.eval) if ls.lastOp == NoOperation { // loc is complete, MajorIteration can be declared directly. ls.lastOp = MajorIteration } else { // Declare MajorIteration on the next call to Iterate. ls.nextMajor = true } case FuncEvaluation, GradEvaluation, FuncEvaluation | GradEvaluation: if step != ls.lastStep { // We are moving to a new location, and not, say, evaluating extra // information at the current location. // Compute the next evaluation point and store it in loc.X. floats.AddScaledTo(loc.X, ls.x, step, ls.dir) if floats.Equal(ls.x, loc.X) { // Step size has become so small that the next evaluation point is // indistinguishable from the starting point for the current // iteration due to rounding errors. return ls.error(ErrNoProgress) } ls.lastStep = step ls.eval = NoOperation // Indicate all invalid fields of loc. } ls.lastOp = op default: panic("linesearch: Linesearcher returned invalid operation") } return ls.lastOp, nil }
func plusScaled(a []float64, k float64, b []float64) []float64 { dst := make([]float64, len(a)) floats.AddScaledTo(dst, a, k, b) return dst }