Beispiel #1
0
        /// <summary>
        /// Brent-Dekker method for finding roots. Robust and relatively quick.
        /// Uses a combination of inverse quadratic interpolation, secant interpolation, and bisection.
        /// Note: Brent-Dekker requires that the root be bracketed. This routine will attempt to
        /// bracket the root before using Brent-Dekker.
        /// </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">Optional. The maximum tolerance, 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 BrentBracket(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();
                result.Bracket    = b;
                result.Iterations = iterations;
                return(result);
            }

            return(BrentInt(f, b.XMin, b.FxMin, b.XMax, b.FxMax, areNear, maxIterations - iterations));
        }
Beispiel #2
0
        /// <summary>
        /// Brent-Dekker uses a combination of inverse quadratic interpolation, secant interpolation, and bisection to locate the root of f(x).
        /// Note: min and max must bracket the root.
        /// </summary>
        /// <param name="f">function to solve</param>
        /// <param name="min">root must lie within [min, max]</param>
        /// <param name="max">root must lie within [min, max]</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 Brent(Func <double, double> f, double min, double max, ToleranceFunc areNear, int maxIterations)
        {
            const RootResults nullResult = null;

            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);
            }

            // use the default if tolerance is null
            if (areNear == null)
            {
                areNear = GetToleranceFunc();
            }

            if (maxIterations < 3)
            {
                Policies.ReportDomainError("Requires maxIterations >= 3: maxIterations = {0}", maxIterations);
                return(nullResult);
            }

            double fmin = f(min);
            double fmax = f(max);

            if (!IsBracket(fmin, fmax))
            {
                RootResults results = new RootResults()
                {
                    Iterations = 2,
                    Bracket    = new RootBracketResult(min, fmin, max, fmax)
                };
                Policies.ReportDomainError("The root is not bracketed: f({0}) = {1}; f({2}) = {3}", min, fmin, max, fmax);
                return(results);
            }


            var r = BrentInt(f, min, fmin, max, fmax, areNear, maxIterations - 2);

            r.Iterations += 2;
            return(r);
        }
Beispiel #3
0
        // For more information, see: http://en.wikipedia.org/wiki/Brent's_method


        private static RootResults BrentInt(Func <double, double> f, double min, double fmin, double max, double fmax, ToleranceFunc areNear, int maxIterations)
        {
            // private routine so these conditions should already be true
            Debug.Assert(f != null, "Function cannot be null");
            Debug.Assert(min < max, "Parameter min < max");
            Debug.Assert(IsBracket(fmin, fmax), "Root is not bracketed");

            // code based on pseudocode in http://en.wikipedia.org/wiki/Brent's_method

            double xSolution  = double.NaN;
            int    iterations = 0;
            double x          = double.NaN;

            if (fmin == 0)
            {
                xSolution = min;
                goto exit;
            }
            if (fmax == 0)
            {
                xSolution = max;
                goto exit;
            }
            if (areNear(min, max))
            {
                xSolution = min + (max - min) / 2.0; // don't have an exact solution so take a midpoint
                goto exit;
            }

            // b is chosen to be the closer point to the root (i.e. Abs(fb) < Abs(fa) )
            // a is always of the opposite sign as b so that [a,b] or [b,a] contains the root
            // c = the previous value of b except initially when c = a
            // d = the previous value of c

            double a, b, c, fa, fb, fc, d = 0, df = 0;

            if (Math.Abs(fmin) < Math.Abs(fmax))
            {
                a = max; fa = fmax;
                b = min; fb = fmin;
            }
            else
            {
                b = max; fb = fmax;
                a = min; fa = fmin;
            }

            c = a; fc = fa; // first step will be a secant step


            bool usedBisection = true; // last step was bisection -- is d set

            while (iterations < maxIterations)
            {
                if (fa != fc && fb != fc)
                {
                    // use inverse quadratic interpolation step
                    // xn = b; xnm1=a; xnm2 = c;

                    x = (a * fb * fc) / ((fa - fb) * (fa - fc)) +
                        (b * fa * fc) / ((fb - fa) * (fb - fc)) +
                        (c * fa * fb) / ((fc - fa) * (fc - fb));
                }
                else     // fa != fb here because Brent requires fa,fb to have opposite signs
                // secant step
                // xn = b; xnm1=a;
                {
                    x = b - fb * (b - a) / (fb - fa);
                }

                if (((3 * a + b) / 4 <= x && x <= b) ||
                    (usedBisection && (Math.Abs(x - b) >= Math.Abs(b - c) / 2 || areNear(b, c))) ||      //Math.Abs(b-c) < tol.AbsTolerance
                    (!usedBisection && (Math.Abs(x - b) >= Math.Abs(c - d) / 2) || areNear(c, d)))       //Math.Abs(c-d) < tol.AbsTolerance
                // bisection step
                {
                    x             = (a + b) / 2;
                    usedBisection = true;
                }
                else
                {
                    usedBisection = false;
                }

                double fx = f(x);
                iterations++;
                if (fx == 0)
                {
                    xSolution = x;
                    break;
                }

                if (double.IsNaN(fx))
                {
                    Policies.ReportDomainError("Requires that function returns a value: f({0}) = {1}", x, fx);
                    break;
                }


                d = c; df = fc;
                c = b; fc = fb;

                if (IsBracket(fa, fx))
                {
                    b = x; fb = fx;
                }
                else
                {
                    a = x; fa = fx;
                }

                // a is always further away from the root
                if (Math.Abs(fa) < Math.Abs(fb))
                {
                    double t, ft;
                    t = b; ft = fb;
                    b = a; fb = fa;
                    a = t; fa = ft;
                }

                if (areNear(a, b))
                {
                    xSolution = x = a + 0.5 * (b - a); // don't have an exact solution, so return the midpoint
                    break;
                }
            }

            min = a; fmin = fa;
            max = b; fmax = fb;
exit:
            RootResults result = new RootResults();

            if (double.IsNaN(xSolution))
            {
                result.LastX = x;
            }
            else
            {
                result.SolutionX = xSolution;
            }

            result.Bracket    = new RootBracketResult(min, fmin, max, fmax);
            result.Iterations = iterations;
            return(result);
        }
Beispiel #4
0
        // See: http://en.wikipedia.org/wiki/Bisection_method

        private static RootResults BisectionInt(Func <double, double> f, double min, double fmin, double max, double fmax, ToleranceFunc areNear, int maxIterations)
        {
            // private routine so these conditions should already be true
            Debug.Assert(f != null);
            Debug.Assert(areNear != null);
            Debug.Assert(min < max);
            Debug.Assert(IsBracket(fmin, fmax));
            Debug.Assert(maxIterations > 0);

            // check the initial conditions

            double xSolution  = double.NaN;
            int    iterations = 0;
            double mid        = double.NaN;

            if (fmin == 0)
            {
                xSolution = min;
                goto exit;
            }
            if (fmax == 0)
            {
                xSolution = max;
                goto exit;
            }
            if (areNear(min, max))
            {
                xSolution = min + (max - min) / 2.0; // don't have an exact solution so take a midpoint
                goto exit;
            }

            while (iterations < maxIterations)
            {
                // evaluate the function at the midpoint
                mid = min + (max - min) / 2;
                double fmid = f(mid);
                iterations++;

                if (fmid == 0)
                {
                    xSolution = mid;
                    break;
                }



                if (IsBracket(fmin, fmid))
                {
                    max  = mid;
                    fmax = fmid;
                }
                else
                {
                    min  = mid;
                    fmin = fmid;
                }

                if (areNear(max, min))
                {
                    xSolution = min + (max - min) / 2.0; // don't have an exact solution so take a midpoint
                    break;
                }
            }

exit:
            RootResults result = new RootResults();

            if (double.IsNaN(xSolution))
            {
                result.LastX = mid;
            }
            else
            {
                result.SolutionX = xSolution;
            }

            result.Bracket    = new RootBracketResult(min, fmin, max, fmax);
            result.Iterations = iterations;
            return(result);
        }
Beispiel #5
0
        private static RootResults Toms748Int(Func <double, double> f, double min, double fmin, double max, double fmax, ToleranceFunc areNear, int max_iter)
        {
            // private routine so these conditions should already be true
            Debug.Assert(f != null, "Function cannot be null");
            Debug.Assert(min <= max, $"Requires min <= max, min: {min}, max: {max}");
            Debug.Assert(IsBracket(fmin, fmax), "Root is not bracketed");

            //
            // Main entry point and logic for Toms Algorithm 748
            // root finder.
            //

            double xSolution  = double.NaN;
            int    iterations = 0;


            double a  = min;
            double fa = fmin;

            double b  = max;
            double fb = fmax;

            if (fa == 0)
            {
                xSolution = a;
                goto exit;
            }
            if (fb == 0)
            {
                xSolution = b;
                goto exit;
            }
            if (areNear(a, b))
            {
                xSolution = a + (b - a) / 2.0; // don't have an exact solution so take a midpoint
                goto exit;
            }


            double d, fd; // d is the third best guess to the root
            double e, fe; // e is the fourth best guess to the root


            // initialize
            fe = e = d = fd = double.NaN;
            double c;

            int stepNumber = 0;

            while (iterations < max_iter)
            {
                // the root is in [a, b]; save it
                double a0 = a;
                double b0 = b;

                switch (stepNumber)
                {
                case 0:
                    // *Only* on the first iteration we take a secant step:
                    c = SecantStep(a, b, fa, fb);
                    break;

                case 1:
                    // *Only* on the second iteration, we take a quadratic step:
                    c = QuadraticStep(a, b, d, fa, fb, fd, 2);
                    break;

                case 2:
                case 3:
                    //
                    // Starting with the third step taken
                    // we can use either quadratic or cubic interpolation.
                    // Cubic interpolation requires that all four function values
                    // fa, fb, fd, and fe are distinct, should that not be the case
                    // then variable prof will get set to true, and we'll end up
                    // taking a quadratic step instead.
                    //
                    const double min_diff = DoubleLimits.MinNormalValue;
                    bool         prof     = (Math.Abs(fa - fb) < min_diff) ||
                                            (Math.Abs(fa - fd) < min_diff) ||
                                            (Math.Abs(fa - fe) < min_diff) ||
                                            (Math.Abs(fb - fd) < min_diff) ||
                                            (Math.Abs(fb - fe) < min_diff) ||
                                            (Math.Abs(fd - fe) < min_diff);

                    // the first time use 2 newton steps; the second time use three
                    int newtonSteps = (stepNumber == 2) ? 2 : 3;
                    if (prof)
                    {
                        c = QuadraticStep(a, b, d, fa, fb, fd, newtonSteps);
                    }
                    else
                    {
                        c = CubicStep(a, b, d, e, fa, fb, fd, fe);
                        if (!(c > a && c < b))
                        {
                            c = QuadraticStep(a, b, d, fa, fb, fd, newtonSteps);
                        }
                    }
                    break;

                case 4:
                    // Now we take a double-length secant step:
                    c = DoubleSecantStep(a, b, fa, fb);
                    break;

                case 5:
                    //
                    // And finally... check to see if an additional bisection step is
                    // to be taken, we do this if we're not converging fast enough:
                    //
                    const double mu = 0.5;
                    if ((b - a) < mu * (b0 - a0))
                    {
                        stepNumber = 2;
                        continue;
                    }
                    c = a + (b - a) / 2;
                    break;

                default:
                    Policies.ReportEvaluationError("Switch out of bounds");
                    return(null);
                }

                // save our next best bracket
                e  = d;
                fe = fd;


                // We require c in the interval [ a + |a|*minTol, b - |b|*minTol ]
                // If the interval is not valid: (b-a) < (|a|+|b|)*minTol, then take a midpoint

                const double minTol = DoubleLimits.MachineEpsilon * 10;

                double minA = (a < 0) ? a * (1 - minTol) : a * (1 + minTol);
                double minB = (b < 0) ? b * (1 + minTol) : b * (1 - minTol);
                if ((b - a) < minTol * (Math.Abs(a) + Math.Abs(b)))
                {
                    c = a + (b - a) / 2;
                }
                else if (c < minA)
                {
                    c = minA;
                }
                else if (c > minB)
                {
                    c = minB;
                }

                // OK, lets invoke f(c):
                double fc = f(c);
                ++iterations;

                // if we have a zero then we have an exact solution to the root:
                if (fc == 0)
                {
                    xSolution = c;
                    break;
                }

                if (double.IsNaN(fc))
                {
                    Policies.ReportDomainError("Requires a continuous function: f({0}) = {1}", c, fc);
                    break;
                }

                //
                // Non-zero fc, update the interval:
                //
                if (IsBracket(fa, fc))
                {
                    d = b; fd = fb;
                    b = c; fb = fc;
                }
                else
                {
                    d = a; d = fa;
                    a = c; fa = fc;
                }


                if (areNear(a, b))
                {
                    // we're close enough, but we don't have an exact solution
                    // so take a midpoint
                    xSolution = a + (b - a) / 2.0;
                    break;
                }

                // reset our step if necessary;
                if (++stepNumber > 5)
                {
                    stepNumber = 2;
                }
            }

exit:
            RootResults result = new RootResults();

            result.SolutionX  = xSolution;
            result.Bracket    = new RootBracketResult(a, fa, b, fb);
            result.Iterations = iterations;
            return(result);
        }
Beispiel #6
0
        /// <summary>
        /// Halley's method for finding roots.
        /// Note: min and max must bracket the root.
        /// </summary>
        /// <param name="f">function to solve along with first and second derivative at point x</param>
        /// <param name="guess">the initial guess for the root</param>
        /// <param name="min">root must lie within [min, max]</param>
        /// <param name="max">root must lie within [min, max]</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 Halley(Func <double, ValueTuple <double, double, double> > f, double guess, double min, double max, ToleranceFunc areNear, int maxIterations)
        {
            const RootResults nullResult = null;

            if (f == null)
            {
                Policies.ReportDomainError("Function cannot be null");
                return(nullResult);
            }

            // use the default if tolerance is null
            if (areNear == null)
            {
                areNear = GetToleranceFunc();
            }

            if (maxIterations < 3)
            {
                Policies.ReportDomainError("Requires maxIterations >= 3: maxIterations = {0}", maxIterations);
                return(nullResult);
            }

            if (!(min <= max))
            {
                Policies.ReportDomainError("Requires min <= max : min = {0}; max = {1}", min, max);
                return(nullResult);
            }

            if (!(guess >= min && guess <= max))
            {
                Policies.ReportDomainError("Requires a valid initial guess between min and max: guess = {0}; min = {1}; max = {2}", guess, min, max);
                return(nullResult);
            }


            double xSolution  = double.NaN;
            int    iterations = 0;



            double f0, f1, f2;

            f0 = double.NaN;

            double x = guess;

            double delta  = max - min;
            double delta1 = double.MaxValue;
            double delta2 = double.MaxValue;

            bool out_of_bounds_sentry = false;


            while (iterations < maxIterations)
            {
                double last_f0 = f0;
                delta2 = delta1;
                delta1 = delta;

                // get the value and first derivative
                iterations++;
                (f0, f1, f2) = f(x);

                if (f0 == 0)
                {
                    xSolution = x;
                    break;
                }

                if (double.IsNaN(f0))
                {
                    Policies.ReportDomainError("Requires that function returns a value: f({0}) = {1}", x, f0);
                    break;
                }


                if (f1 == 0)
                {
                    // Oops zero derivative!!!

                    // this is initially null
                    if (double.IsNaN(last_f0))
                    {
                        // this must be the first iteration, pretend that we had a
                        // previous one at either min or max:
                        x = (guess == min) ? max : min;

                        iterations++;
                        (f0, f1, f2) = f(x);

                        last_f0 = f0;

                        delta = x - guess;
                    }
                    if (IsBracket(last_f0, f0))
                    {
                        // we've crossed over so move in opposite direction to last step:
                        if (delta < 0)
                        {
                            delta = (x - min) / 2;
                        }
                        else
                        {
                            delta = (x - max) / 2;
                        }
                    }
                    else
                    {
                        // move in same direction as last step:
                        if (delta < 0)
                        {
                            delta = (x - max) / 2;
                        }
                        else
                        {
                            delta = (x - min) / 2;
                        }
                    }
                }
                else
                {
                    // Use Halley's method:
                    // Let f = f(x{n}); f' = f'(x{n}); f'' = f''(x{n});
                    //
                    // x{n+1} = x{n} - (2 * f * f')/(2 * f'^2 -  f * f'' )
                    // OR
                    // x{n+1} = x{n} - (2 * f)/(2 * f' -  f * f''/ f' )
                    // OR
                    // x{n+1} = x{n} - (f/f')/(1 - (1/2)*(f/f')(f''/f'))


                    if (f2 != 0 && !double.IsInfinity(f2))
                    {
                        double num   = 2 * f0;
                        double denom = 2 * f1 - f0 * (f2 / f1);

                        delta = num / denom;
                        if (double.IsInfinity(delta) || double.IsNaN(delta))
                        {
                            // possible overflow, use Newton step:
                            delta = f0 / f1;
                        }

                        if (delta * f1 / f0 < 0)
                        {
                            // probably cancellation error, try a Newton step instead:
                            delta = f0 / f1;
                        }
                    }
                    else
                    {
                        delta = f0 / f1;
                    }
                }

                double convergence = Math.Abs(delta / delta2);
                if ((convergence > 0.8) && (convergence < 2))
                {
                    // last two steps haven't converged, try bisection:
                    delta = (delta > 0) ? (x - min) / 2 : (x - max) / 2;
                    if (Math.Abs(delta) > x)
                    {
                        delta = Math.Sign(delta) * x; // protect against huge jumps!
                    }
                    // reset delta2 so that this branch will *not* be taken on the
                    // next iteration:
                    delta2 = delta * 3;
                }



                guess = x;
                x    -= delta;

                // check for out of bounds step:
                if (x < min)
                {
                    double diff = ((Math.Abs(min) < 1) && (Math.Abs(x) > 1) && (double.MaxValue / Math.Abs(x) < Math.Abs(min))) ? 1000.0 : x / min;
                    if (Math.Abs(diff) < 1)
                    {
                        diff = 1 / diff;
                    }
                    if (!out_of_bounds_sentry && (diff > 0) && (diff < 3))
                    {
                        // Only a small out of bounds step, lets assume that the result
                        // is probably approximately at min:
                        delta = 0.99 * (guess - min);
                        x     = guess - delta;
                        out_of_bounds_sentry = true; // only take this branch once!
                    }
                    else
                    {
                        delta = (guess - min) / 2;
                        x     = guess - delta;
                    }
                }
                else if (x > max)
                {
                    double diff = ((Math.Abs(max) < 1) && (Math.Abs(x) > 1) && (double.MaxValue / Math.Abs(x) < Math.Abs(max))) ? 1000.0 : x / max;
                    if (Math.Abs(diff) < 1)
                    {
                        diff = 1 / diff;
                    }
                    if (!out_of_bounds_sentry && (diff > 0) && (diff < 3))
                    {
                        // Only a small out of bounds step, lets assume that the result
                        // is probably approximately at min:
                        delta = 0.99 * (guess - max);
                        x     = guess - delta;
                        out_of_bounds_sentry = true; // only take this branch once!
                    }
                    else
                    {
                        delta = (guess - max) / 2;
                        x     = guess - delta;
                    }
                }
                // update brackets:
                if (delta > 0)
                {
                    max = guess;
                }
                else
                {
                    min = guess;
                }


                if (areNear(x, guess))
                {
                    xSolution = x;
                    break;
                }
            }

            RootResults result = new RootResults()
            {
                SolutionX = xSolution,
                // Note: this halley implementation does not keep track of fmin, fmax
                // for the brackets
                Bracket    = new RootBracketResult(min, double.NaN, max, double.NaN),
                Iterations = iterations
            };

            return(result);
        }