Ejemplo n.º 1
0
        private static MultiExtremum FindGlobalExtremum(Func <IReadOnlyList <double>, double> function, IReadOnlyList <Interval> volume, MultiExtremumSettings settings, bool negate)
        {
            if (function == null)
            {
                throw new ArgumentNullException(nameof(function));
            }
            if (volume == null)
            {
                throw new ArgumentNullException(nameof(volume));
            }
            MultiFunctor f = new MultiFunctor(function, negate);
            DifferentialEvolutionSettings deSettings = GetDefaultSettings(settings, volume.Count);
            MultiExtremum extremum = FindGlobalExtremum(f, volume, deSettings);

            return(extremum);
        }
        // This method is due to Powell (http://en.wikipedia.org/wiki/Michael_J._D._Powell), but it is not what
        // is usually called Powell's Method (http://en.wikipedia.org/wiki/Powell%27s_method); Powell
        // developed that method in the 1960s, it was included in Numerical Recipes and is very popular.
        // This is a model trust algorithm developed by Powell in the 2000s. It typically uses many
        // fewer function evaluations, but does more intensive calculations between each evaluation.

        // This is basically the UOBYQA variant of Powell's new methods. It maintains a quadratic model
        // that interpolates between (d + 1) (d + 2) / 2 points. The model is trusted
        // within a given radius. At each step, it moves to the minimum of the model (or the boundary of
        // the trust region in that direction) and evaluates the function. The new value is incorporated
        // into the model and the trust region expanded or contracted depending on how accurate its
        // prediction of the function value was.

        // Papers on these methods are collected at http://mat.uc.pt/~zhang/software.html#powell_software.
        // The UOBYQA paper is here: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.28.1756.
        // The NEWUOA paper is here: http://www.damtp.cam.ac.uk/user/na/NA_papers/NA2004_08.pdf.
        // The CONDOR system (http://www.applied-mathematics.net/optimization/CONDORdownload.html) is based on these same ideas.
        // The thesis of CONDOR's author (http://www.applied-mathematics.net/mythesis/index.html) was also helpful.

        // It should be very easy to extend this method to constrained optimization, either by incorporating the bounds into
        // the step limits or by mapping hyper-space into a hyper-cube.

        private static MultiExtremum FindMinimum_ModelTrust(MultiFunctor f, IReadOnlyList <double> x, double s, MultiExtremumSettings settings)
        {
            // Construct an initial model.
            QuadraticInterpolationModel model = QuadraticInterpolationModel.Construct(f, x, s);
            double trustRadius = s;

            while (f.EvaluationCount < settings.EvaluationBudget)
            {
                // Find the minimum point of the model within the trust radius
                double[] z             = model.FindMinimum(trustRadius);
                double   expectedValue = model.Evaluate(z);

                double deltaExpected = model.MinimumValue - expectedValue;

                // Evaluate the function at the suggested minimum
                double[] point = model.ConvertPoint(z);
                double   value = f.Evaluate(point);

                double delta = model.MinimumValue - value;
                double tol   = settings.ComputePrecision(Math.Min(model.MinimumValue, value));
                // Note value can be way off, so use better of old best and new value to compute tol.
                // When we didn't do this before, we got value = infinity, so tol = infinity, and thus terminated!

                if (delta > 0.0 && settings.Listener != null)
                {
                    MultiExtremum report = new MultiExtremum(f.EvaluationCount, settings, point, value, Math.Max(Math.Abs(delta), 0.75 * tol), model.GetHessian());
                    settings.Listener(report);
                }

                // To terminate, we demand: a reduction, that the reduction be small, that the reduction be in line with
                // its expected value, that we have not run up against the trust boundary, and that the gradient is small.
                // I had wanted to demand delta > 0, but we run into some cases where delta keeps being very slightly
                // negative, typically orders of magnitude less than tol, causing the trust radius to shrink in an
                // endless cycle that causes our approximation to ultimately go sour, even though terminating on the original
                // very slightly negative delta would have produced an accurate estimate. So we tolerate this case for now.
                if ((delta <= tol) && (-0.25 * tol <= delta))
                {
                    // We demand that the model be decent, i.e. that the expected delta was within tol of the measured delta.
                    if (Math.Abs(delta - deltaExpected) <= tol)
                    {
                        // We demand that the step not just be small because it ran up against the trust radius.
                        // If it ran up against the trust radius, there is probably more to be hand by continuing.
                        double zm = Blas1.dNrm2(z, 0, 1, z.Length);
                        if (zm < trustRadius)
                        {
                            // Finally, we demand that the gradient be small. You might think this was obvious since
                            // z was small, but if the Hessian is not positive definite
                            // the interplay of the Hessian and the gradient can produce a small z even if the model looks nothing like a quadratic minimum.
                            double gm = Blas1.dNrm2(model.GetGradient(), 0, 1, z.Length);
                            if (gm * zm <= tol)
                            {
                                if (f.IsNegated)
                                {
                                    value = -value;
                                }
                                return(new MultiExtremum(f.EvaluationCount, settings, point, value, Math.Max(Math.Abs(delta), 0.75 * tol), model.GetHessian()));
                            }
                        }
                    }
                }

                // There are now three decisions to be made:
                //   1. How to change the trust radius
                //   2. Whether to accept the new point
                //   3. Which existing point to replace

                // If the actual change was very far from the expected change, reduce the trust radius.
                // If the expected change did a good job of predicting the actual change, increase the trust radius.
                if ((delta < 0.25 * deltaExpected) /*|| (8.0 * deltaExpected < delta)*/)
                {
                    trustRadius = 0.5 * trustRadius;
                }
                else if ((0.75 * deltaExpected <= delta) /*&& (delta <= 2.0 * deltaExpected)*/)
                {
                    trustRadius = 2.0 * trustRadius;
                }
                // It appears that the limits on delta being too large don't help, and even hurt if made too stringent.

                // Replace an old point with the new point.
                int iMax = 0; double fMax = model.values[0];
                int iBad = 0; double fBad = model.ComputeBadness(0, z, point, value);
                for (int i = 1; i < model.values.Length; i++)
                {
                    if (model.values[i] > fMax)
                    {
                        iMax = i; fMax = model.values[i];
                    }
                    double bad = model.ComputeBadness(i, z, point, value);
                    if (bad > fBad)
                    {
                        iBad = i; fBad = bad;
                    }
                }
                // Use the new point as long as it is better than our worst existing point.
                if (value < fMax)
                {
                    Debug.Assert(!Double.IsPositiveInfinity(value) && !Double.IsNaN(value));
                    model.ReplacePoint(iBad, point, z, value);
                }
                // There is some question about how best to choose which point to replace.
                // The largest value? The furthest away? The one closest to new min?
            }

            throw new NonconvergenceException();
        }
Ejemplo n.º 3
0
        // Differential evolution is a global optimization algorithm over continuous inputs that is adapted from genetic algorithms for finite inputs.
        // The idea is to maintain a population of input vectors ("agents") and to vary that population over cycles ("generations") according to rules that incorporate
        // random mutation but on average tend to bring them closer to optima ("fitter").

        private static MultiExtremum FindGlobalExtremum(MultiFunctor f, IReadOnlyList <Interval> volume, DifferentialEvolutionSettings settings)
        {
            int d = volume.Count;

            // Choose a number of agents that increases with dimension and required precision.
            int m = settings.Population;

            Debug.WriteLine("d={0} m={1}", d, m);

            Random rng = new Random(3);

            // Start with random points in the allowed region.
            double[][] points = new double[m][];
            double[]   values = new double[m];
            for (int i = 0; i < m; i++)
            {
                points[i] = new double[d];
                for (int j = 0; j < d; j++)
                {
                    points[i][j] = volume[j].LeftEndpoint + rng.NextDouble() * volume[j].Width;
                }
                values[i] = f.Evaluate(points[i]);
            }


            while (f.EvaluationCount < settings.EvaluationBudget)
            {
                double[][] newPoints = new double[m][];
                double[]   newValues = new double[m];

                for (int i = 0; i < m; i++)
                {
                    // Mutation
                    // construct donor vector
                    int a = i; while (a == i)
                    {
                        a = rng.Next(m);
                    }
                    int b = i; while ((b == i) || (b == a))
                    {
                        b = rng.Next(m);
                    }
                    int c = i; while ((c == i) || (c == b) || (c == a))
                    {
                        c = rng.Next(m);
                    }
                    double[] donor = new double[d];
                    for (int j = 0; j < d; j++)
                    {
                        donor[j] = points[a][j] + mutationFactor * (points[b][j] - points[c][j]);
                        if (donor[j] < volume[j].LeftEndpoint)
                        {
                            donor[j] = volume[j].LeftEndpoint;
                        }
                        if (donor[j] > volume[j].RightEndpoint)
                        {
                            donor[j] = volume[j].RightEndpoint;
                        }
                    }

                    // Recombination
                    double[] trial = new double[d];
                    int      k     = rng.Next(d);
                    for (int j = 0; j < d; j++)
                    {
                        if ((j == k) || (rng.NextDouble() < settings.CrossoverProbability))
                        {
                            trial[j] = donor[j];
                        }
                        else
                        {
                            trial[j] = points[i][j];
                        }
                    }

                    // Selection
                    double value = f.Evaluate(trial);
                    if (value <= values[i])
                    {
                        newPoints[i] = trial;
                        newValues[i] = value;
                    }
                    else
                    {
                        newPoints[i] = points[i];
                        newValues[i] = values[i];
                    }
                }

                points = newPoints;
                values = newValues;

                // Check termination criteria
                int    minIndex = -1;
                double minValue = Double.MaxValue;
                double maxValue = Double.MinValue;
                for (int i = 0; i < m; i++)
                {
                    if (values[i] < minValue)
                    {
                        minValue = values[i];
                        minIndex = i;
                    }
                    if (values[i] > maxValue)
                    {
                        maxValue = values[i];
                    }
                }
                double range = maxValue - minValue;
                double tol   = settings.ComputePrecision(minValue);
                if (range <= tol)
                {
                    MultiExtremum result = new MultiExtremum(f.EvaluationCount, settings, points[minIndex], f.IsNegated ? -values[minIndex] : values[minIndex], Math.Max(range, 0.75 * tol), null);
                    return(result);
                }
                else if (settings.Listener != null)
                {
                    MultiExtremum report = new MultiExtremum(f.EvaluationCount, settings, points[minIndex], f.IsNegated ? -values[minIndex] : values[minIndex], Math.Max(range, 0.75 * tol), null);
                    settings.Listener(report);
                }
            }

            throw new NonconvergenceException();
        }
        // Differential evoluation is a global optimization algorithm over continuous inputs that is adapted from genetic algorithms for finite inputs.
        // The idea is maintain a population of input vectors ("agents") and to vary that population over cycles ("generations") according to rules that incorporate
        // random mutation but on average tend to bring them closer to optima ("fitter").
        private static MultiExtremum FindGlobalExtremum(MultiFunctor f, IList<Interval> volume, DifferentialEvolutionSettings settings)
        {
            int d = volume.Count;

            // Choose a number of agents that increases with dimension and required precision.
            int m = settings.Population;
            Debug.WriteLine("d={0} m={1}", d, m);

            Random rng = new Random(3);
            //Random rng = new Random(1001110000);

            // Start with random points in the allowed region.
            double[][] points = new double[m][];
            double[] values = new double[m];
            for (int i = 0; i < m; i++) {
                points[i] = new double[d];
                for (int j = 0; j < d; j++) {
                    points[i][j] = volume[j].LeftEndpoint + rng.NextDouble() * volume[j].Width;
                }
                values[i] = f.Evaluate(points[i]);
            }

            while (f.EvaluationCount < settings.EvaluationBudget) {

                //double mutationFactor = 0.5 + 0.5 * rng.NextDouble();

                double[][] newPoints = new double[m][];
                double[] newValues = new double[m];

                for (int i = 0; i < m; i++) {

                    // Mutation
                    // construct donor vector
                    int a = i; while (a == i) a = rng.Next(m);
                    int b = i; while ((b == i) || (b == a)) b = rng.Next(m);
                    int c = i; while ((c == i) || (c == b) || (c == a)) c = rng.Next(m);
                    double[] donor = new double[d];
                    for (int j = 0; j < d; j++) {
                        donor[j] = points[a][j] + mutationFactor * (points[b][j] - points[c][j]);
                        if (donor[j] < volume[j].LeftEndpoint) donor[j] = volume[j].LeftEndpoint;
                        if (donor[j] > volume[j].RightEndpoint) donor[j] = volume[j].RightEndpoint;
                    }

                    // Recombination
                    double[] trial = new double[d];
                    int k = rng.Next(d);
                    for (int j = 0; j < d; j++) {
                        if ((j == k) || (rng.NextDouble() < settings.CrossoverProbability)) {
                            trial[j] = donor[j];
                        } else {
                            trial[j] = points[i][j];
                        }
                    }

                    // Selection
                    double value = f.Evaluate(trial);
                    if (value <= values[i]) {
                        newPoints[i] = trial;
                        newValues[i] = value;
                    } else {
                        newPoints[i] = points[i];
                        newValues[i] = values[i];
                    }

                }

                points = newPoints;
                values = newValues;

                // Check termination criteria
                int minIndex = -1;
                double minValue = Double.MaxValue;
                double maxValue = Double.MinValue;
                for (int i = 0; i < m; i++) {
                    if (values[i] < minValue) {
                        minValue = values[i];
                        minIndex = i;
                    }
                    if (values[i] > maxValue) maxValue = values[i];
                }
                double range = maxValue - minValue;
                double tol = settings.ComputePrecision(minValue);
                if (range <= tol) {
                    MultiExtremum result = new MultiExtremum(f.EvaluationCount, settings, points[minIndex], f.IsNegated ? -values[minIndex] : values[minIndex], Math.Max(range, 0.75 * tol), null);
                    return (result);
                }

                //settings.OnUpdate(new MultiExtremum(points[minIndex], values[minIndex], null, f.EvaluationCount));

            }

            throw new NonconvergenceException();
        }