Ejemplo n.º 1
0
 public bool Agrees(Extremum extremum)
 {
     return (
         TestUtilities.IsNearlyEqual(extremum.Value, Value, 10.0 * TestUtilities.TargetPrecision) &&
         TestUtilities.IsNearlyEqual(extremum.Location, Location, 10.0 * Math.Sqrt(TestUtilities.TargetPrecision)) &&
         (Double.IsNaN(Curvature) || TestUtilities.IsNearlyEqual(extremum.Curvature, Curvature, 0.01))
     );
 }
        /// <summary>
        /// Minimizes a function on a multi-dimensional space in the vicinity of a given point, subject to the given settings.
        /// </summary>
        /// <param name="f">The function.</param>
        /// <param name="x">The starting point for the search.</param>
        /// <param name="settings">The evaluation settings.</param>
        /// <returns>The minimum.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="f"/>, <paramref name="x"/>, or <paramref name="settings"/> is null.</exception>
        /// <exception cref="NonconvergenceException">The minimum was not found to the required precision within the budgeted number of function evaluations.</exception>
        internal static SpaceExtremum FindMinimum(Func <double[], double> f, double[] x, EvaluationSettings settings)
        {
            if (f == null)
            {
                throw new ArgumentNullException("f");
            }
            if (x == null)
            {
                throw new ArgumentNullException("x");
            }
            if (settings == null)
            {
                throw new ArgumentNullException("settings");
            }

            int d = x.Length;

            // put the function into a Functor we will use for line searches
            LineSearchFunctor fo = new LineSearchFunctor(f);

            // keep track of the (conjugate) minimization directions
            double[][] Q = new double[d][];
            for (int i = 0; i < d; i++)
            {
                Q[i] = new double[d];
                for (int j = 0; j < d; j++)
                {
                    Q[i][j] = 0.0;
                }
                // pick a step size in each direction that represents a fraction of the input value
                Q[i][i] = (1.0 / 16.0) * (Math.Abs(x[i]) + (1.0 / 16.0));
            }
            // keep track of the curvature in these directions
            double[] r = new double[d];

            // keep track of the function value
            double y = f(x);

            // keep track of the total number of evaluations
            //int count = 0;

            bool skip = false;

            // interate until convergence
            while (fo.EvaluationCount < settings.EvaluationBudget)
            {
                // remember our starting position
                double[] x0 = new double[d];
                Array.Copy(x, x0, d);
                double y0 = y;

                // keep track of direction of largest decrease
                double max_dy = 0.0;
                int    max_i  = 0;

                // now minimize in each direction
                for (int i = 0; i < d; i++)
                {
                    // if we placed the net direction in the first slot last time,
                    // we are already at the minimum along that direction, so don't
                    // minimize along it
                    if (skip)
                    {
                        skip = false;
                        continue;
                    }
                    //if ((n > 0) && (i == 0) && (max_i == 0)) continue;

                    //Console.WriteLine("i = {0}", i);
                    //WriteVector(Q[i]);

                    // form a line function
                    //LineFunction f1 = new LineFunction(f, x, Q[i]);
                    fo.Origin    = x;
                    fo.Direction = Q[i];

                    // minimize it
                    Extremum m = FindMinimum(fo, 0.0, y, 1.0, new ExtremumSettings()
                    {
                        EvaluationBudget = settings.EvaluationBudget, AbsolutePrecision = 0.0, RelativePrecision = 0.0
                    });
                    //LineExtremum m = FindMinimum(new Func<double,double>(f1.Evaluate), 0.0, y, 1.0);

                    // add to the evaluation count
                    //count += f1.Count;

                    // update the current position
                    x = fo.ComputeLocation(m.Location);
                    //x = f1.Position(m.Location);
                    //WriteVector(x);
                    r[i] = m.Curvature;

                    // keep track of how much the function dropped, and
                    // if this is the direction of largest decrease
                    double dy = y - m.Value;
                    //Console.WriteLine("dy = {0}", dy);
                    if (dy > max_dy)
                    {
                        max_dy = dy;
                        max_i  = i;
                    }
                    y = m.Value;
                }

                //Console.WriteLine("max_i = {0}, max_dy = {1}", max_i, max_dy);
                //Console.WriteLine("y0 = {0}, y = {1}", y0, y);

                // figure out the net direction we have moved
                double[] dx = new double[d];
                for (int i = 0; i < d; i++)
                {
                    dx[i] = x[i] - x0[i];
                }
                //Console.WriteLine("Finish:");
                //WriteVector(x);
                //Console.WriteLine("Net direction:");
                //WriteVector(dx);

                // check termination criteria
                // we do this before minimizing in the net direction because if dx=0 it loops forever
                double Dy = Math.Abs(y0 - y);
                if ((Dy < settings.AbsolutePrecision) || (2.0 * Dy < (Math.Abs(y) + Math.Abs(y0)) * settings.RelativePrecision))
                {
                    SymmetricMatrix A = ComputeCurvature(f, x);
                    return(new SpaceExtremum(x, y, A));
                }

                // attempt a minimization in the net direction
                fo.Origin    = x;
                fo.Direction = dx;
                //LineFunction f2 = new LineFunction(f, x, dx);
                //LineExtremum mm = FindMinimum(new Func<double,double>(f2.Evaluate), 0.0, y, 1.0);
                Extremum mm = FindMinimum(fo, 0.0, y, 1.0, new ExtremumSettings()
                {
                    EvaluationBudget = settings.EvaluationBudget, RelativePrecision = 0.0, AbsolutePrecision = 0.0
                });
                //count += f2.Count;
                //x = f2.Position(mm.Location);
                x = fo.ComputeLocation(mm.Location);
                y = mm.Value;

                // rotate this direction into the direction set

                /*
                 * for (int i = 0; i < (d - 1); i++) {
                 *  Q[i] = Q[i + 1];
                 *  r[i] = r[i + 1];
                 * }
                 * Q[d - 1] = dx;
                 * r[d - 1] = mm.Curvature;
                 */
                // this is the basic Powell procedure, and it leads to linear dependence

                // replace the direction of largest decrease with the net direction
                Q[max_i] = dx;
                r[max_i] = mm.Curvature;
                if (max_i == 0)
                {
                    skip = true;
                }
                // this is powell's modification to avoid linear dependence

                // reset
            }

            throw new NonconvergenceException();
        }
Ejemplo n.º 3
0
        // 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,
            ExtremumSettings settings
            )
        {
            settings = SetExtremumDefaults(settings);

            double tol = 0.0;
            double fpp = Double.NaN;

            while (f.EvaluationCount < settings.EvaluationBudget)
            {
                Debug.WriteLine($"n={f.EvaluationCount} tol={tol}");
                Debug.WriteLine($"[{a}  f({u})={fu}  f({v})={fv}  f({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));

                if (settings.Listener != null)
                {
                    Extremum result = new Extremum(u, fu, fpp, a, b, f.EvaluationCount, settings);
                    settings.Listener(result);
                }

                // 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, a, b, f.EvaluationCount, settings));
                }

                ParabolicFit(u, fu, v, fv, w, fw, out double x, out fpp);
                Debug.WriteLine($"parabolic x={x} f''={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($"golden section x={x}");
                }

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

                // Evaluate the function at the new point x.
                double fx = f.Evaluate(x);
                Debug.WriteLine($"f({x}) = {fx}");
                Debug.WriteLine($"delta={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
                {
                    if (fx == fu)
                    {
                        Debug.WriteLine($"f({x}) = f({u}) = {fx}");
                    }

                    // 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
                        // But, tests with Rosenbrock function indicate this increases evaluation count, so hold off on this idea for now.
                        // w = x; fw = fx;

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

                if ((settings.RelativePrecision > 0.0 || settings.AbsolutePrecision > 0.0))
                {
                    // If the user has specified a tolerance, use it.
                    tol = Math.Max(Math.Abs(u) * settings.RelativePrecision, settings.AbsolutePrecision);
                }
                else
                {
                    // Otherwise, try to get the tolerance 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, just wing it.
                        if (tol == 0.0)
                        {
                            tol = Math.Sqrt(Global.Accuracy);
                        }
                    }
                }
            }

            throw new NonconvergenceException();
        }