/// <summary> /// Performs one iteration of the secant method on the <paramref name="bracket"/> interval. /// </summary> /// <param name="bracket">The bracket.</param> /// <param name="values">The values.</param> /// <returns>System.Double.</returns> private double Secant(Bracket bracket, ref FunctionValues values) { var a = bracket.Start; var b = bracket.End; var dφa = values.dφ(a); var dφb = values.dφ(b); var c = (a * dφb - b * dφa) / (dφb - dφa); return(c); }
/// <summary> /// Determines if the algorithm should terminate, given the currently selected step width <paramref name="α" /> /// and the shared <paramref name="values"/>. /// </summary> /// <param name="values">The values.</param> /// <param name="α">The selected step width.</param> /// <returns><see langword="true" /> if the line search should terminate, <see langword="false" /> otherwise.</returns> private bool ShouldTerminate(double α, ref FunctionValues values) { // calculate the function values at α var φα = values.φ(α); var dφα = values.dφ(α); // delegate return(ShouldTerminate(α, values.φ0, values.dφ0, φα, dφα)); }
/// <summary> /// Updates the <paramref name="current" /> bracketing interval around the midpoint <paramref name="c" />. /// </summary> /// <param name="current">The current bracketing interval.</param> /// <param name="c">The midpoint.</param> /// <param name="values">The values.</param> /// <returns>Bracket.</returns> /// <exception cref="System.NotImplementedException"></exception> private Bracket UpdateBracketing(Bracket current, double c, ref FunctionValues values) { // we do not check for infinity here, as this will be handled in U0 Debug.Assert(!Double.IsNaN(c), "!Double.IsNaN(c)"); // prefetch var θ = _θ; // theta var ε = _ε; // epsilon // helpers var a = current.Start; var b = current.End; // U0: if the midpoint is out of range of the current bracketing // interval, stay where we are. if (c < a || c > b) { return(current); } // U1 var dφc = values.dφ(c); if (dφc >= 0.0D) { return(new Bracket(start: a, end: c)); } // U2 var φc = values.φ(c); if (φc <= values.φ0 + ε) { return(new Bracket(start: c, end: b)); } // U3 return(UpdateBracketInRange(start: a, end: c, values: ref values)); }
/// <summary> /// Updates the bracketing in range <see cref="start" /> to <see cref="end" />. /// </summary> /// <param name="start">The starting point.</param> /// <param name="end">The end point.</param> /// <param name="values">The values.</param> /// <returns>Bracket.</returns> private Bracket UpdateBracketInRange(double start, double end, ref FunctionValues values) { Debug.Assert(start.IsFinite(), "start.IsFinite()"); Debug.Assert(end.IsFinite(), "end.IsFinite()"); // prefetch var θ = _θ; // theta var ɛ = _ε; // epsilon var φ0 = values.φ0; var currentStart = start; var currentEnd = end; var previousD = double.NaN; while (true) // TODO: bug in disguise? { // U3a var d = (1 - θ) * currentStart + θ * currentEnd; Debug.Assert(d != previousD, "d != previousD"); // NOTE that this case is not in the paper previousD = d; if (values.dφ(d) >= 0.0D) { return(new Bracket(start: currentStart, end: d)); } // U3b: close in from the left if (values.φ(d) <= φ0 + ɛ) { currentStart = d; continue; } // U3c: close in from the right currentEnd = d; } }
/// <summary> /// Brackets the starting point <paramref name="α"/>. /// </summary> /// <param name="α">The starting point.</param> /// <param name="values">The values.</param> /// <returns>Bracket.</returns> /// <exception cref="System.NotImplementedException"></exception> private Bracket BracketStartingPoint(double α, FunctionValues values) { // prefetch var ρ = _ρ; // rho var θ = _θ; // theta var ε = _ε; // epsilon // make sure the parameters are in range Debug.Assert(ρ > 1, "ρ > 1"); Debug.Assert(θ > 0 && θ < 1, "θ > 0 && θ < 1"); Debug.Assert(α >= 0, "α >= 0"); // B0: initialize the current step value cj and keep track of the values in a stack var c = α; var previousC = new Stack <double>(); var φ0 = values.φ0; // TODO: set a maximum iteration count var maxBracketingIterations = _maxBracketingIterations; for (var j = 0; j < maxBracketingIterations; ++j) { // register the current step value previousC.Push(c); // determine the gradient at the current step length var dφc = values.dφ(c); // B1: if we find our currently selected end value to be ascending, // then backtrack the starting value to the last descend. // Note that we may end up here in the first iteration also // if the start point generation yields a problematic result. if (dφc >= 0) { var end = c; // find the most recent step selection c smaller cj that resulted // in a function value less than the starting point. // Since we just added cj, we'll just throw that away. previousC.Pop(); while (previousC.Count > 0) { c = previousC.Pop(); if (values.φ(c) > φ0 + ε) { continue; } var start = c; return(new Bracket(start, end)); } // at this point there was no good starting point. // sadly, this point is not handled in the paper, so // we'll just throw out 0 as the starting point and hope for the best. return(new Bracket(0, end)); } // B2: If, for some reason, we are on a descending direction, yet the // current function value is larger than what we started with, then // we know a minimum must exist between the current point and the start. // By using the secant method, we zoom in the range until we find a valid // search region. if (values.φ(c) > φ0 + ε) { return(UpdateBracketInRange(start: 0.0D, end: c, values: ref values)); } // B3: At this point, we are still descending and we did not skip // any minima as far as we know, so we'll increase our step size. c = ρ * c; } // welp return(new Bracket(0, c)); }