// Brent's algorithm: use 3-point parabolic interpolation,
        // switching to golden section if interval does not shrink fast enough
        // see Richard Brent, "Algorithms for Minimization Without Derivatives"

        // The bracket is [a, b] and the three lowest points are (u,fu), (v,fv), (w, fw)
        // Note that a or b may be u, v, or w.

        private static Extremum FindMinimum(
            Functor f,
            double a, double b,
            double u, double fu, double v, double fv, double w, double fw,
            EvaluationSettings settings
            )
        {
            double tol = 0.0; double fpp = Double.NaN;

            while (f.EvaluationCount < settings.EvaluationBudget)
            {
                Debug.WriteLine(String.Format("n={0} tol={1}", f.EvaluationCount, tol));
                Debug.WriteLine(String.Format("[{0}  f({1})={2}  f({3})={4}  f({5})={6}  {7}]", a, u, fu, v, fv, w, fw, b));

                // While a < u < b is guaranteed, a < v, w < b is not guaranteed, since the bracket can sometimes be made tight enough to exclude v or w.
                // For example, if u < v < w, then we can set b = v, placing w outside the bracket.

                Debug.Assert(a < b);
                Debug.Assert((a <= u) && (u <= b));
                Debug.Assert((fu <= fv) && (fv <= fw));

                // Expected final situation is a<tol><tol>u<tol><tol>b, leaving no point left to evaluate that is not within tol of an existing point.

                if ((b - a) <= 4.0 * tol)
                {
                    return(new Extremum(u, fu, fpp, f.EvaluationCount, settings));
                }

                double x; ParabolicFit(u, fu, v, fv, w, fw, out x, out fpp);
                Debug.WriteLine(String.Format("parabolic x={0} f''={1}", x, fpp));

                if (Double.IsNaN(fpp) || (fpp <= 0.0) || (x < a) || (x > b))
                {
                    // the parabolic fit didn't work out, so do a golden section reduction instead

                    // to get the most reduction of the bracket, pick the larger of au and ub
                    // for self-similarity, pick a point inside it that divides it into two segments in the golden section ratio,
                    // i.e. 0.3820 = \frac{1}{\phi + 1} and 0.6180 = \frac{\phi}{\phi+1}
                    // put the smaller segment closer to u so that x is closer to u, the best minimum so far

                    double au = u - a;
                    double ub = b - u;

                    if (au > ub)
                    {
                        x = u - au / (AdvancedMath.GoldenRatio + 1.0);
                    }
                    else
                    {
                        x = u + ub / (AdvancedMath.GoldenRatio + 1.0);
                    }

                    Debug.WriteLine(String.Format("golden section x={0}", x));
                }

                // ensure we don't evaluate within tolerance of an existing point
                if (Math.Abs(x - u) < tol)
                {
                    Debug.WriteLine(String.Format("shift from u (x={0})", x)); x = (x > u) ? u + tol : u - tol;
                }
                if ((x - a) < tol)
                {
                    Debug.WriteLine(String.Format("shift from a (x={0})", x)); x = a + tol;
                }
                if ((b - x) < tol)
                {
                    Debug.WriteLine(String.Format("shift from b (x={0})", x)); x = b - tol;
                }

                // evaluate the function at the new point x
                double fx = f.Evaluate(x);
                Debug.WriteLine(String.Format("f({0}) = {1}", x, fx));
                Debug.WriteLine(String.Format("delta={0}", fu - fx));

                // update a, b and u, v, w based on new point x

                if (fx < fu)
                {
                    // the new point is lower than all the others; this is success

                    // u now becomes a bracket point
                    if (u < x)
                    {
                        a = u;
                    }
                    else
                    {
                        b = u;
                    }

                    // x -> u -> v -> w
                    w = v; fw = fv;
                    v = u; fv = fu;
                    u = x; fu = fx;
                }
                else
                {
                    // x now becomes a bracket point
                    if (x < u)
                    {
                        a = x;
                    }
                    else
                    {
                        b = x;
                    }

                    if (fx < fv)
                    {
                        // the new point is higher than u, but still lower than v and w
                        // this isn't what we expected, but we have lower points that before

                        // x -> v -> w
                        w = v; fw = fv;
                        v = x; fv = fx;
                    }
                    else if (fx < fw)
                    {
                        // x -> w
                        w = x; fw = fx;
                    }
                    else
                    {
                        // the new point is higher than all our other points; this is the worst case

                        // we might still want to replace w with x because
                        // (i) otherwise a parabolic fit will reproduce the same x and
                        // (ii) w is quite likely far outside the new bracket and not telling us much about the behavior near u
                        // w = x; fw = fx;
                        // but tests with Rosenbrock function indicate this increases evaluation count

                        Debug.WriteLine("bad point");
                    }
                }

                // if the user has specified a tollerance, use it
                if ((settings.RelativePrecision > 0.0 || settings.AbsolutePrecision > 0.0))
                {
                    tol = Math.Max(Math.Abs(u) * settings.RelativePrecision, settings.AbsolutePrecision);
                }
                else
                {
                    // otherwise, try to get the tollerance from the curvature
                    if (fpp > 0.0)
                    {
                        tol = Math.Sqrt(2.0 * Global.Accuracy * (Math.Abs(fu) + Global.Accuracy) / fpp);
                    }
                    else
                    {
                        // but if we don't have a useable curvature either, wing it
                        if (tol == 0.0)
                        {
                            tol = Math.Sqrt(Global.Accuracy);
                        }
                    }
                }
            }

            throw new NonconvergenceException();
        }
        // Brent's algorithm: use 3-point parabolic interpolation,
        // switching to golden section if interval does not shrink fast enough
        // see Richard Brent, "Algorithms for Minimization Without Derivatives"
        // The bracket is [a, b] and the three lowest points are (u,fu), (v,fv), (w, fw)
        // Note that a or b may be u, v, or w.
        private static Extremum FindMinimum(
            Functor f,
            double a, double b,
            double u, double fu, double v, double fv, double w, double fw,
            EvaluationSettings settings
        )
        {
            double tol = 0.0; double fpp = Double.NaN;

            while (f.EvaluationCount < settings.EvaluationBudget) {

                Debug.WriteLine(String.Format("n={0} tol={1}", f.EvaluationCount, tol));
                Debug.WriteLine(String.Format("[{0}  f({1})={2}  f({3})={4}  f({5})={6}  {7}]", a, u, fu, v, fv, w, fw, b));

                // While a < u < b is guaranteed, a < v, w < b is not guaranteed, since the bracket can sometimes be made tight enough to exclude v or w.
                // For example, if u < v < w, then we can set b = v, placing w outside the bracket.

                Debug.Assert(a < b);
                Debug.Assert((a <= u) && (u <= b));
                Debug.Assert((fu <= fv) && (fv <= fw));

                // Expected final situation is a<tol><tol>u<tol><tol>b, leaving no point left to evaluate that is not within tol of an existing point.

                if ((b - a) <= 4.0 * tol) return (new Extremum(u, fu, fpp, f.EvaluationCount, settings));

                double x; ParabolicFit(u, fu, v, fv, w, fw, out x, out fpp);
                Debug.WriteLine(String.Format("parabolic x={0} f''={1}", x, fpp));

                if (Double.IsNaN(fpp) || (fpp <= 0.0) || (x < a) || (x > b)) {

                    // the parabolic fit didn't work out, so do a golden section reduction instead

                    // to get the most reduction of the bracket, pick the larger of au and ub
                    // for self-similarity, pick a point inside it that divides it into two segments in the golden section ratio,
                    // i.e. 0.3820 = \frac{1}{\phi + 1} and 0.6180 = \frac{\phi}{\phi+1}
                    // put the smaller segment closer to u so that x is closer to u, the best minimum so far

                    double au = u - a;
                    double ub = b - u;

                    if (au > ub) {
                        x = u - au / (AdvancedMath.GoldenRatio + 1.0);
                    } else {
                        x = u + ub / (AdvancedMath.GoldenRatio + 1.0);
                    }

                    Debug.WriteLine(String.Format("golden section x={0}", x));

                }

                // ensure we don't evaluate within tolerance of an existing point
                if (Math.Abs(x - u) < tol) { Debug.WriteLine(String.Format("shift from u (x={0})", x)); x = (x > u) ? u + tol : u - tol; }
                if ((x - a) < tol) { Debug.WriteLine(String.Format("shift from a (x={0})", x)); x = a + tol; }
                if ((b - x) < tol) { Debug.WriteLine(String.Format("shift from b (x={0})", x)); x = b - tol; }

                // evaluate the function at the new point x
                double fx = f.Evaluate(x);
                Debug.WriteLine(String.Format("f({0}) = {1}", x, fx));
                Debug.WriteLine(String.Format("delta={0}", fu - fx));

                // update a, b and u, v, w based on new point x

                if (fx < fu) {

                    // the new point is lower than all the others; this is success

                    // u now becomes a bracket point
                    if (u < x) {
                        a = u;
                    } else {
                        b = u;
                    }

                    // x -> u -> v -> w
                    w = v; fw = fv;
                    v = u; fv = fu;
                    u = x; fu = fx;

                } else {

                    // x now becomes a bracket point
                    if (x < u) {
                        a = x;
                    } else {
                        b = x;
                    }

                    if (fx < fv) {

                        // the new point is higher than u, but still lower than v and w
                        // this isn't what we expected, but we have lower points that before

                        // x -> v -> w
                        w = v; fw = fv;
                        v = x; fv = fx;

                    } else if (fx < fw) {

                        // x -> w
                        w = x; fw = fx;

                    } else {

                        // the new point is higher than all our other points; this is the worst case

                        // we might still want to replace w with x because
                        // (i) otherwise a parabolic fit will reproduce the same x and
                        // (ii) w is quite likely far outside the new bracket and not telling us much about the behavior near u
                        // w = x; fw = fx;
                        // but tests with Rosenbrock function indicate this increases evaluation count

                        Debug.WriteLine("bad point");

                    }

                }

                // if the user has specified a tollerance, use it
                if ((settings.RelativePrecision > 0.0 || settings.AbsolutePrecision > 0.0)) {
                    tol = Math.Max(Math.Abs(u) * settings.RelativePrecision, settings.AbsolutePrecision);
                } else {
                    // otherwise, try to get the tollerance from the curvature
                    if (fpp > 0.0) {
                        tol = Math.Sqrt(2.0 * Global.Accuracy * (Math.Abs(fu) + Global.Accuracy) / fpp);
                    } else {
                        // but if we don't have a useable curvature either, wing it
                        if (tol == 0.0) tol = Math.Sqrt(Global.Accuracy);
                    }
                }

            }

            throw new NonconvergenceException();
        }
        private static Extremum FindMinimum(
            Functor f,
            double a, double b,
            EvaluationSettings settings
        )
        {
            // evaluate three points within the bracket
            double u = (3.0 * a + b) / 4.0;
            double v = (a + b) / 2.0;
            double w = (a + 3.0 * b) / 4.0;

            double fu = f.Evaluate(u); double fv = f.Evaluate(v); double fw = f.Evaluate(w);

            Debug.WriteLine(String.Format("f({0})={1}  f({2})={3}  f({4})={5}", u, fu, v, fv, w, fw));

            // move in the bracket boundaries, if possible
            if (fv < fu) { a = u; if (fw < fv) a = v; }
            if (fv < fw) { b = w; if (fu < fv) b = v; }

            Debug.WriteLine(String.Format("[{0} {1}]", a, b));

            // sort u, v, w by fu, fv, fw values
            // these three comparisons are the most efficient three-item sort
            if (fv < fu) { Global.Swap(ref v, ref u); Global.Swap(ref fv, ref fu); }
            if (fw < fu) { Global.Swap(ref w, ref u); Global.Swap(ref fw, ref fu); }
            if (fw < fv) { Global.Swap(ref w, ref v); Global.Swap(ref fw, ref fv); }

            // pass to Brent's algorithm to shrink bracket
            return (FindMinimum(f, a, b, u, fu, v, fv, w, fw, settings));
        }
        private static Extremum FindMinimum(Functor f, double x, double fx, double d, EvaluationSettings settings)
        {
            // This function brackets a minimum by starting from x and taking increasing steps downhill until it moves uphill again.

            // We write this function assuming f(x) has already been evaluated because when it is called
            // to do a line fit for Powell's multi-dimensional minimization routine, that is the case and
            // we don't want to do a superfluous evaluation.

            Debug.Assert(f != null); Debug.Assert(d > 0.0); Debug.Assert(settings != null);

            // evaluate at x + d
            double y = x + d;
            double fy = f.Evaluate(y);

            // if we stepped uphill, reverse direction of steps and exchange x & y
            if (fy > fx) {
                Global.Swap(ref x, ref y); Global.Swap(ref fx, ref fy);
                d = -d;
            }

            // we now know f(x) >= f(y) and we are stepping downhill
            // continue stepping until we step uphill
            double z, fz;
            while (true) {

                if (f.EvaluationCount >= settings.EvaluationBudget) throw new NonconvergenceException();

                z = y + d;
                fz = f.Evaluate(z);

                Debug.WriteLine(String.Format("f({0})={1} f({2})={3} f({4})={5} d={6}", x, fx, y, fy, z, fz, d));

                if (fz > fy) break;

                // increase the step size each time
                d = AdvancedMath.GoldenRatio * d;

                // x <- y <- z
                x = y; fx = fy; y = z; fy = fz;

            }

            // we x and z now bracket a local minimum, with y the lowest point evaluated so far
            double a = Math.Min(x, z); double b = Math.Max(x, z);
            if (fz < fx) { Global.Swap(ref x, ref z); Global.Swap(ref fx, ref fz);}

            return (FindMinimum(f, a, b, y, fy, x, fx, z, fz, settings));
        }
        private static Extremum FindMinimum(Functor f, double x, EvaluationSettings settings)
        {
            Debug.Assert(f != null); Debug.Assert(settings != null);

            // To call the bracketing function, we need an initial value and an initial step.
            double fx = f.Evaluate(x);
            // Pick a relatively small initial value to try to avoid running into any nearly singularities.
            double d = (Math.Abs(x) + 1.0 / 32.0) / 32.0;

            return(FindMinimum(f, x, fx, d, settings));
        }
Esempio n. 6
0
        private static Extremum FindMinimum(Functor f, double x, double fx, double d, ExtremumSettings settings)
        {
            // This function brackets a minimum by starting from x and taking increasing steps downhill until it moves uphill again.

            // We write this function assuming f(x) has already been evaluated because when it is called
            // to do a line fit for Powell's multi-dimensional minimization routine, that is the case and
            // we don't want to do a superfluous evaluation.

            Debug.Assert(f != null);
            Debug.Assert(d > 0.0);
            Debug.Assert(settings != null);

            // evaluate at x + d
            double y  = x + d;
            double fy = f.Evaluate(y);

            // if we stepped uphill, reverse direction of steps and exchange x & y
            if (fy > fx)
            {
                Global.Swap(ref x, ref y); Global.Swap(ref fx, ref fy);
                d = -d;
            }

            // we now know f(x) >= f(y) and we are stepping downhill
            // continue stepping until we step uphill
            double z, fz;

            while (true)
            {
                if (f.EvaluationCount >= settings.EvaluationBudget)
                {
                    throw new NonconvergenceException();
                }

                z  = y + d;
                fz = f.Evaluate(z);

                Debug.WriteLine($"f({x})={fx} f({y})={fy} f({z})={fz} d={d}");

                if (fz > fy)
                {
                    break;
                }

                // increase the step size each time
                d = AdvancedMath.GoldenRatio * d;

                // x <- y <- z
                x = y; fx = fy; y = z; fy = fz;
            }

            // we x and z now bracket a local minimum, with y the lowest point evaluated so far
            double a = Math.Min(x, z); double b = Math.Max(x, z);

            if (fz < fx)
            {
                Global.Swap(ref x, ref z); Global.Swap(ref fx, ref fz);
            }

            return(FindMinimum(f, a, b, y, fy, x, fx, z, fz, settings));
        }