/// <summary> /// TOMS Algorithm 748 uses a mixture of cubic, quadratic and linear (secant) interpolation to locate the root of f(x) /// Note: TOMS748 requires the root to be bracketed, so this routine will attempt to bracket the root. /// </summary> /// <param name="f">Function to solve</param> /// <param name="guess">The initial guess for the root.</param> /// <param name="step">The starting step size</param> /// <param name="fType">Unknown, increasing, or decreasing</param> /// <param name="min">Optional. The minimum location for the root, or NaN</param> /// <param name="max">Optional. The maximum location for the root, or NaN</param> /// <param name="areNear">A function that returns true when the required tolerance is reached, or null to use the default tolerance</param> /// <param name="maxIterations">The maximum number of iterations to keep refining the root estimate</param> /// <returns> /// Null, if any of the parameters were incorrect. /// The RootResults which contains the root if one was found, or the final state before failing. /// </returns> public static RootResults Toms748Bracket(Func <double, double> f, double guess, double step, FunctionShape fType, double min, double max, ToleranceFunc areNear, int maxIterations) { const RootResults nullResult = null; if (f == null) { Policies.ReportDomainError("Function cannot be null"); return(nullResult); } // NaN is valid here for min or max, so only fail if both have a value and min > max if (min > max) { Policies.ReportDomainError("Requires min <= max : min = {0}; max = {1}", min, max); return(nullResult); } // use the default if max iterations is negative if (maxIterations < 3) { Policies.ReportDomainError("Requires maxIterations >= 3: maxIterations = {0}", maxIterations); return(nullResult); } // use the default if tolerance is null if (areNear == null) { areNear = GetToleranceFunc(); } int iterations; RootBracketResult b = FindBracket(f, guess, step, fType, min, max, maxIterations, out iterations); if (b == null) { Policies.ReportDomainError("Invalid parameter in bracket"); return(nullResult); } if (!b.IsValid) { RootResults result = new RootResults() { Bracket = b, Iterations = iterations }; return(result); } var r = Toms748Int(f, b.XMin, b.FxMin, b.XMax, b.FxMax, areNear, maxIterations - iterations); r.Iterations += iterations; return(r); }
/// <summary> /// Tries to locate a bracket given an initial guess and a step. /// Routine walks downhill/uphill taking steps of increasing size until the function changes sign. /// </summary> /// <param name="f">function to solve</param> /// <param name="guess">initial guess of x value of the root</param> /// <param name="step">the starting step size</param> /// <param name="fType">unknown, increasing, or decreasing</param> /// <param name="min">Optional. The minimum limit on x if it exists, otherwise NaN</param> /// <param name="max">Optional. The maximum limit on x if it exists, otherwise NaN</param> /// <param name="maxIterations">The maximum number of iterations to find the bracket</param> /// <param name="nIterations">Output. Return the number of iterations used</param> /// <returns> /// Null, if any of the parameters were incorrect. /// The bracket if one was found, otherwise the last attempt. /// </returns> public static RootBracketResult FindBracket(Func <double, double> f, double guess, double step, FunctionShape fType, double min, double max, int maxIterations, out int nIterations) { const RootBracketResult nullResult = null; nIterations = 0; if (f == null) { Policies.ReportDomainError("Function cannot be null"); return(nullResult); } if (min > max) { Policies.ReportDomainError("Requires min <= max : min = {0}; max = {1}", min, max); return(nullResult); } if (maxIterations < 3) { Policies.ReportDomainError("Iterations must be > 3: maxIterations = {0}", maxIterations); return(nullResult); } if (step == 0 || double.IsNaN(step)) { Policies.ReportDomainError("Requires |Step| > 0 : step = {0}", step); return(nullResult); } Interval bounds = new Interval(min, max); if (guess != bounds.Clip(guess)) { Policies.ReportDomainError("Guess must be between min and max: guess = {0}; min = {1}; max = {2}", guess, min, max); return(nullResult); } // choose a step multiplier > 1 so that we don't go back and forth to // the same locations const double stepMultiplier = 1.5; double x = guess; double fx = f(guess); int iterations = 1; if (fx == 0 || double.IsNaN(fx)) { nIterations = iterations; return(new RootBracketResult(x, fx, x, fx)); } // the bracket is contained within [a, b], where a <= b // fa = f(a), fb = f(b) double a, fa, b, fb; bool force = false; // set initial values at guess and guess+step // If we don't know what the function looks like // count on the user to tell us with the sign of step if (fType == FunctionShape.Increasing) { // if ( fx < 0 ) positive step, else negative step force = true; step = -Math.Sign(fx) * Math.Abs(step); } else if (fType == FunctionShape.Decreasing) { // if ( fx < 0 ) negative step, else positive step force = true; step = Math.Sign(fx) * Math.Abs(step); } a = x; fa = fx; b = x; fb = fx; iterations = 1; while (iterations < maxIterations) { Debug.Assert(step != 0, "FindBracket: Step == 0"); if (step > 0) { if (x == max) { // We've gone as far as we can in this direction // Maybe the root is in the other direction? if (!force) { step = -step; force = true; continue; } // we've hit both limits, // return the last (unsuccessful) bracket nIterations = iterations; return(new RootBracketResult(a, fa, b, fb)); } // is our step size big enough double nextx = bounds.Clip(x + step); if (nextx == x) { step = Math.Abs(x * DoubleLimits.MachineEpsilon); nextx = bounds.Clip(x + step); } x = nextx; fx = f(x); iterations++; a = b; fa = fb; b = x; fb = fx; } else { // step < 0 if (x == min) { // We've gone as far as we can in this direction // Maybe the root is in the other direction? if (!force) { step = -step; force = true; continue; } // We've hit both limits, // return the last (unsuccessful) bracket nIterations = iterations; return(new RootBracketResult(a, fa, b, fb)); } // is our step size big enough double nextx = bounds.Clip(x + step); if (nextx == x) { step = -Math.Abs(x * DoubleLimits.MachineEpsilon); nextx = bounds.Clip(x + step); } x = nextx; fx = f(x); iterations++; b = a; fb = fa; a = x; fa = fx; } // check to see if the root is bracketed if (IsBracket(fa, fb)) { nIterations = iterations; return(new RootBracketResult(a, fa, b, fb)); } // at this point a and b have the same sign // otherwise the root would have been bracketed // if it is a rising function (i.e. fa < fb ) // if both are in negative territory then root > b (move b right) // if both are in positive territory then root < a (move a left) // else // if both are in negative territory then root < a (move a left) // if both are in positive territory then root > b (move b right) // if fa == fb, continue with the same direction if (!force) { double astep = Math.Abs(step); if (fa < fb) { if (fa < 0) { step = +astep; } else { step = -astep; } } else if (fa > fb) { if (fa < 0) { step = -astep; } else { step = +astep; } } } // set our new step size step *= stepMultiplier; } // no bracket found -- return the (unsuccessful) bracket nIterations = iterations; return(new RootBracketResult(a, fa, b, fb)); }