Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2825-NSGA3/HeuristicLab.Algorithms.NSGA3/3.3/NSGA3Selection.cs @ 17958

Last change on this file since 17958 was 17727, checked in by dleko, 4 years ago

#2825 Refactoring.

File size: 14.6 KB
RevLine 
[17665]1using System;
2using System.Collections.Generic;
3using System.Linq;
[17724]4using HEAL.Attic;
5using HeuristicLab.Common;
[17665]6using HeuristicLab.Core;
7using HeuristicLab.Optimization;
8
9namespace HeuristicLab.Algorithms.NSGA3
10{
[17724]11    [StorableType("53158EA5-4D1C-42DB-90F5-EC555EC0A450")]
12    public class NSGA3Selection : IDeepCloneable
[17665]13    {
14        private const double EPSILON = 10e-6; // a tiny number that is greater than 0
15
[17724]16        [Storable]
17        private int numberOfObjectives;
18
19        [Storable]
20        private bool[] maximization;
21
22        [Storable]
23        private int populationSize;
24
25        [Storable]
26        private IRandom random;
27
28        [Storable]
29        private bool dominateOnEqualQualities;
30
31        [StorableConstructor]
32        public NSGA3Selection(StorableConstructorFlag _) { }
33
34        public NSGA3Selection(int numberOfObjectives, bool[] maximization, int populationSize, IRandom random, bool dominateOnEqualQualities)
35        {
36            if (maximization == null) throw new ArgumentNullException(nameof(maximization));
37            if (random == null) throw new ArgumentNullException(nameof(random));
38            if (numberOfObjectives != maximization.Length) throw new ArgumentException($"{nameof(numberOfObjectives)} and the length of {nameof(maximization)} do not match.");
39            if (populationSize < 1) throw new ArgumentOutOfRangeException($"{nameof(populationSize)} has to be >= 1.");
40
41            this.numberOfObjectives = numberOfObjectives;
42            this.maximization = maximization;
43            this.populationSize = populationSize;
44            this.random = random;
45            this.dominateOnEqualQualities = dominateOnEqualQualities;
46        }
47
48        public NSGA3Selection(NSGA3Selection other, Cloner cloner)
49        {
50            numberOfObjectives = other.numberOfObjectives;
51            maximization = new bool[other.maximization.Length];
52            Array.Copy(other.maximization, maximization, maximization.Length);
53            populationSize = other.populationSize;
54            random = cloner.Clone(other.random);
55            dominateOnEqualQualities = other.dominateOnEqualQualities;
56        }
57
58        public IDeepCloneable Clone(Cloner cloner)
59        {
60            return new NSGA3Selection(this, cloner);
61        }
62
63        public object Clone()
64        {
65            return new Cloner().Clone(this);
66        }
67
[17665]68        /// <summary>
[17727]69        /// Select the solutions for the next generation from <see cref="rt" /> according to the
70        /// NSGA-III algorithm.
[17665]71        /// </summary>
[17727]72        /// <param name="rt">The current generation and its mutated children.</param>
[17665]73        /// <param name="referencePoints"></param>
74        /// <returns></returns>
[17724]75        public List<Solution> SelectSolutionsForNextGeneration(List<Solution> rt, List<ReferencePoint> referencePoints)
[17665]76        {
77            List<Solution> st = new List<Solution>();
78
79            // Do non-dominated sort
80            var qualities = Utility.ToFitnessMatrix(rt);
[17720]81
82            // compute the pareto fronts using the DominationCalculator
[17724]83            List<List<Solution>> fronts = DominationCalculator<Solution>.CalculateAllParetoFronts(rt.ToArray(), qualities, maximization, out int[] rank, dominateOnEqualQualities)
[17665]84                .Select(list => new List<Solution>(list.Select(pair => pair.Item1))).ToList();
85
[17727]86            int last = 0; // last front to be included
87            while (st.Count < populationSize)
[17665]88            {
[17727]89                st = Utility.Concat(st, fronts[last++]);
[17665]90            }
[17727]91
[17668]92            // remove useless fronts
[17727]93            fronts.RemoveRange(last, fronts.Count - last);
[17665]94
95            List<Solution> nextGeneration = new List<Solution>();
[17727]96            if (st.Count == populationSize)
97            { // no special selection needs to be done
[17665]98                nextGeneration = st;
[17727]99            }
[17665]100            else
101            {
102                // Add every front to the next generation list except for the one that is added only partially
103                nextGeneration = new List<Solution>();
[17727]104                for (int f = 0; f < last - 1; f++)
[17665]105                    nextGeneration = Utility.Concat(nextGeneration, fronts[f]);
[17727]106
[17724]107                int k = populationSize - nextGeneration.Count;
108                Normalize(rt, fronts);
[17665]109                Associate(fronts, referencePoints);
110                List<Solution> solutionsToAdd = Niching(k, referencePoints, random);
111                nextGeneration = Utility.Concat(nextGeneration, solutionsToAdd);
112            }
113
114            return nextGeneration;
115        }
116
[17724]117        private void Normalize(List<Solution> solutions, List<List<Solution>> fronts)
[17665]118        {
119            // Find the ideal point
[17727]120            double[] idealPoint = TranslateObjectives(solutions, fronts[0]);
[17692]121
[17665]122            // Find the extreme points
[17727]123            List<Solution> extremePoints = GetExtremePoints(fronts[0]);
[17665]124
125            // Compute intercepts
[17727]126            double[] intercepts = GetIntercepts(extremePoints, fronts[0]);
[17665]127
128            // Normalize objectives
[17724]129            NormalizeObjectives(solutions, intercepts, idealPoint);
[17665]130        }
131
[17727]132        private double[] TranslateObjectives(List<Solution> solutions, List<Solution> firstFront)
[17665]133        {
[17727]134            double[] idealPoint = new double[numberOfObjectives];
[17724]135            foreach (var solution in solutions)
[17665]136            {
[17727]137                if (solution.NormalizedObjectives == null) solution.NormalizedObjectives = new double[numberOfObjectives];
[17665]138            }
139
[17727]140            for (int j = 0; j < numberOfObjectives; j++)
[17665]141            {
[17727]142                // Compute ideal point
143                idealPoint[j] = Utility.Min(s => s.Objectives[j], firstFront);
[17665]144
[17727]145                // Translate objectives and save the modified fitness values in NormalizedObjectives
146                foreach (var solution in solutions)
147                    solution.NormalizedObjectives[j] = solution.Objectives[j] - idealPoint[j];
[17665]148            }
149
[17727]150            return idealPoint;
[17665]151        }
152
[17727]153        private List<Solution> GetExtremePoints(List<Solution> firstFront)
[17665]154        {
[17727]155            List<Solution> extremePoints = new List<Solution>(numberOfObjectives);
156            for (int j = 0; j < numberOfObjectives; j++)
[17665]157            {
[17727]158                // Compute extreme points
159                double[] weights = new double[numberOfObjectives];
160                for (int i = 0; i < numberOfObjectives; i++) weights[i] = EPSILON;
161                weights[j] = 1;
[17665]162
[17727]163                var extremePoint = Utility.ArgMin(s => ASF(s.NormalizedObjectives, weights), firstFront);
164                extremePoints.Add(extremePoint);
[17665]165            }
166
[17727]167            return extremePoints;
[17665]168        }
169
[17727]170        private double ASF(double[] convertedFitness, double[] weight)
[17665]171        {
[17727]172            var dims = Enumerable.Range(0, numberOfObjectives);
[17665]173
[17727]174            return Utility.Max((dim) => convertedFitness[dim] / weight[dim], dims);
[17665]175        }
176
[17724]177        private double[] GetIntercepts(List<Solution> extremePoints, List<Solution> solutions)
[17665]178        {
179            // Check whether there are duplicate extreme points. This might happen but the original
180            // paper does not mention how to deal with it.
181            bool duplicate = false;
182            for (int i = 0; !duplicate && i < extremePoints.Count; i++)
183            {
184                for (int j = i + 1; !duplicate && j < extremePoints.Count; j++)
185                {
[17692]186                    duplicate = extremePoints[i] == extremePoints[j];
[17665]187                }
188            }
189
[17724]190            double[] intercepts = new double[numberOfObjectives];
[17665]191
[17724]192            bool negative_intercept = false;
193            if (!duplicate)
[17665]194            {
195                // Find the equation of the hyperplane
[17727]196                List<double> b = new List<double>(Enumerable.Repeat(1.0, numberOfObjectives));
[17665]197
[17724]198                List<List<double>> a = new List<List<double>>(extremePoints.Count);
[17692]199
[17724]200                for (int i = 0; i < extremePoints.Count; i++)
201                {
[17727]202                    List<double> convertedFitness = new List<double>(extremePoints[i].NormalizedObjectives);
[17724]203                    a.Add(convertedFitness);
204                }
[17665]205                List<double> x = GaussianElimination(a, b);
206
207                // Find intercepts
[17724]208                for (int i = 0; i < intercepts.Length; i++)
[17665]209                {
[17724]210                    intercepts[i] = 1.0 / x[i];
211
212                    if (x[i] < 0)
213                    {
214                        negative_intercept = true;
215                        break;
216                    }
[17665]217                }
218            }
219
[17724]220            // follow the method in Yuan et al. (GECCO 2015)
221            if (duplicate || negative_intercept)
222            {
223                double[] maxObjectives = FindMaxObjectives(solutions);
224                for (int i = 0; i < intercepts.Length; i++)
225                    intercepts[i] = maxObjectives[i];
226            }
227
[17665]228            return intercepts;
229        }
230
[17724]231        private List<double> GaussianElimination(List<List<double>> a, List<double> b)
232        {
[17665]233            List<double> x = new List<double>();
234
235            int n = a.Count;
236            for (int i = 0; i < n; i++)
237                a[i].Add(b[i]);
238
239            for (int @base = 0; @base < n - 1; @base++)
240                for (int target = @base + 1; target < n; target++)
241                {
242                    double ratio = a[target][@base] / a[@base][@base];
243                    for (int term = 0; term < a[@base].Count; term++)
244                        a[target][term] = a[target][term] - a[@base][term] * ratio;
245                }
246
247            for (int i = 0; i < n; i++)
248                x.Add(0.0);
249
250            for (int i = n - 1; i >= 0; i--)
251            {
252                for (int known = i + 1; known < n; known++)
253                    a[i][n] = a[i][n] - a[i][known] * x[known];
254                x[i] = a[i][n] / a[i][i];
255            }
256
257            return x;
258        }
[17727]259
260        private double[] FindMaxObjectives(List<Solution> solutions)
261        {
262            double[] maxPoint = new double[numberOfObjectives];
263            for (int i = 0; i < maxPoint.Length; i++) maxPoint[i] = double.MinValue;
264
265            for (int f = 0; f < numberOfObjectives; f++)
266            {
267                maxPoint[f] = Utility.Max(s => s.Objectives[f], solutions);
268            }
269
270            return maxPoint;
271        }
272
273        private void NormalizeObjectives(List<Solution> solutions, double[] intercepts, double[] idealPoint)
274        {
275            foreach (var solution in solutions)
276            {
277                for (int i = 0; i < numberOfObjectives; i++)
278                {
279                    if (Math.Abs(intercepts[i] - idealPoint[i]) > 10e-10)
280                    {
281                        solution.NormalizedObjectives[i] = solution.NormalizedObjectives[i] / (intercepts[i] - idealPoint[i]);
282                    }
283                    else
284                    {
285                        solution.NormalizedObjectives[i] = solution.NormalizedObjectives[i] / 10e-10;
286                    }
287                }
288            }
289        }
290
291        private void Associate(List<List<Solution>> fronts, List<ReferencePoint> referencePoints)
292        {
293            for (int f = 0; f < fronts.Count; f++)
294            {
295                foreach (var solution in fronts[f])
296                {
297                    // find reference point for which the perpendicular distance to the current
298                    // solution is the lowest
299                    var rpAndDist = Utility.MinArgMin(rp => GetPerpendicularDistance(rp.Values, solution.NormalizedObjectives), referencePoints);
300                    // associated reference point
301                    var arp = rpAndDist.Item1;
302                    // distance to that reference point
303                    var dist = rpAndDist.Item2;
304
305                    if (f + 1 != fronts.Count)
306                    {
307                        arp.NumberOfAssociatedSolutions++;
308                    }
309                    else
310                    {
311                        arp.AddPotentialAssociatedSolution(solution, dist);
312                    }
313                }
314            }
315        }
316
317        private double GetPerpendicularDistance(double[] direction, double[] point)
318        {
319            double numerator = 0;
320            double denominator = 0;
321            for (int i = 0; i < direction.Length; i++)
322            {
323                numerator += direction[i] * point[i];
324                denominator += Math.Pow(direction[i], 2);
325            }
326            double k = numerator / denominator;
327
328            double d = 0;
329            for (int i = 0; i < direction.Length; i++)
330            {
331                d += Math.Pow(k * direction[i] - point[i], 2);
332            }
333            return Math.Sqrt(d);
334        }
335
336        private List<Solution> Niching(int k, List<ReferencePoint> referencePoints, IRandom random)
337        {
338            List<Solution> solutions = new List<Solution>();
339            while (solutions.Count != k)
340            {
341                ReferencePoint min_rp = FindNicheReferencePoint(referencePoints, random);
342
343                Solution chosen = SelectClusterMember(min_rp);
344                if (chosen == null)
345                {
346                    referencePoints.Remove(min_rp);
347                }
348                else
349                {
350                    min_rp.NumberOfAssociatedSolutions++;
351                    min_rp.RemovePotentialAssociatedSolution(chosen);
352                    solutions.Add(chosen);
353                }
354            }
355
356            return solutions;
357        }
358
359        private ReferencePoint FindNicheReferencePoint(List<ReferencePoint> referencePoints, IRandom random)
360        {
361            // the minimum number of associated solutions for a reference point over all reference points
362            int minNumber = Utility.Min(rp => rp.NumberOfAssociatedSolutions, referencePoints);
363
364            var minReferencePoints = new List<ReferencePoint>(referencePoints.Where(r => r.NumberOfAssociatedSolutions == minNumber));
365            if (minReferencePoints.Count == 1) return minReferencePoints.Single();
366            else return minReferencePoints[random.Next(minReferencePoints.Count)];
367        }
368
369        private Solution SelectClusterMember(ReferencePoint referencePoint)
370        {
371            Solution chosen = null;
372            if (referencePoint.HasPotentialMember())
373            {
374                if (referencePoint.NumberOfAssociatedSolutions == 0)
375                    chosen = referencePoint.FindClosestMember();
376                else
377                    chosen = referencePoint.RandomMember();
378            }
379            return chosen;
380        }
[17665]381    }
382}
Note: See TracBrowser for help on using the repository browser.