コード例 #1
0
        public static (double Result, int Iterations) SolveA(double z, double p, double q, double aGuess, double step, double min, double max)
        {
            Debug.Assert(aGuess >= min && aGuess <= max, "Guess out of range");

            Func <double, double> f;

            if (p < q)
            {
                f = a => Math2.GammaP(a, z) - p;
            }
            else
            {
                f = a => Math2.GammaQ(a, z) - q;
            }

            //
            // Use our generic derivative-free root finding procedure.
            // We could use Newton steps here, taking the PDF of the
            // Poisson distribution as our derivative, but that's
            // even worse performance-wise than the generic method :-(
            //

            // dQ/da is increasing, dP/da is decreasing
            FunctionShape fShape = (p < q) ? FunctionShape.Decreasing : FunctionShape.Increasing;
            RootResults   rr     = RootFinder.Toms748Bracket(f, aGuess, step, fShape, min, max);

            if (rr == null)
            {
                Policies.ReportRootNotFoundError($"Invalid Parameter: PQInvA(z: {z}, p: {p}, q: {q}) [{min}, {max}] g: {aGuess}");
                return(double.NaN, 0);
            }

            if (!rr.Success)
            {
                Policies.ReportRootNotFoundError("Root not found after {0} iterations", rr.Iterations);
                return(double.NaN, rr.Iterations);
            }

            return(rr.SolutionX, rr.Iterations);
        }
コード例 #2
0
        /// <summary>
        /// Brent-Dekker method for finding roots. Robust and relatively quick.
        /// Uses a combination of inverse quadratic interpolation, secant interpolation, and bisection.
        /// <para>Note: Brent-Dekker requires that the root be bracketed. This routine will attempt to
        /// bracket the root before using Brent-Dekker.</para>
        /// </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="relTolerance">The maximum relative tolerance</param>
        /// <param name="absTolerance">The maximum absolute 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, double relTolerance, double absTolerance, int maxIterations)
        {
            const RootResults nullResult = null;

            if (!(relTolerance >= 0) || double.IsInfinity(relTolerance))
            {
                Policies.ReportDomainError("Requires finite relative tolerance >= 0: relTolerance = {0}", relTolerance);
                return(nullResult);
            }

            if (!(absTolerance >= 0) || double.IsInfinity(absTolerance))
            {
                Policies.ReportDomainError("Requires finite absolute tolerance >= 0: absTolerance = {0}", absTolerance);
                return(nullResult);
            }

            var areNear = GetToleranceFunc(relTolerance, absTolerance);

            return(BrentBracket(f, guess, step, fType, min, max, areNear, maxIterations));
        }
コード例 #3
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>
 /// <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)
 {
     return(BrentBracket(f, guess, step, fType, min, max, null, Policies.MaxRootIterations));
 }
コード例 #4
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));
        }
コード例 #5
0
        /// <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));
        }