Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 17703 was 17692, checked in by dleko, 4 years ago

#2825 Bugfix: NSGA3 now works as intended.

File size: 12.5 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using HeuristicLab.Core;
5using HeuristicLab.Optimization;
6
7namespace HeuristicLab.Algorithms.NSGA3
8{
9    public static class NSGA3Selection
10    {
11        private const double EPSILON = 10e-6; // a tiny number that is greater than 0
12
13        /// <summary>
14        /// TODO: Description If the fitnesses of all solutions in <paramref name="rt" /> do not
15        /// have the same number of objectives, undefined behavior happens.
16        /// </summary>
17        /// <param name="rt">The previous generation and its mutated children.</param>
18        /// <param name="referencePoints"></param>
19        /// <param name="maximization"></param>
20        /// <param name="N">Number of solutions to select.</param>
21        /// <param name="random">The <see cref="IRandom" /> to use for randomness.</param>
22        /// <returns></returns>
23        public static List<Solution> SelectSolutionsForNextGeneration(List<Solution> rt, List<ReferencePoint> referencePoints, bool[] maximization, int N, IRandom random)
24        {
25            int numberOfObjectives = rt.First().Fitness.Length;
26            List<Solution> st = new List<Solution>();
27
28            // Do non-dominated sort
29            var qualities = Utility.ToFitnessMatrix(rt);
30            // compute the pareto fronts using the DominationCalculator and discard the qualities
31            // part in the inner tuples
32            var fronts = DominationCalculator<Solution>.CalculateAllParetoFronts(rt.ToArray(), qualities, maximization, out int[] rank, true)
33                .Select(list => new List<Solution>(list.Select(pair => pair.Item1))).ToList();
34
35            int lf = -1; // last front to be included
36            for (int i = 0; i < fronts.Count && st.Count < N; i++)
37            {
38                st = Utility.Concat(st, fronts[i]);
39                lf = i;
40            }
41            // remove useless fronts
42            fronts.RemoveRange(lf + 1, fronts.Count - lf - 1);
43
44            List<Solution> nextGeneration = new List<Solution>();
45            if (st.Count == N) // no special selection needs to be done
46                nextGeneration = st;
47            else
48            {
49                // Add every front to the next generation list except for the one that is added only partially
50                nextGeneration = new List<Solution>();
51                for (int f = 0; f < lf; f++)
52                    nextGeneration = Utility.Concat(nextGeneration, fronts[f]);
53                int k = N - nextGeneration.Count;
54                Normalize(st, numberOfObjectives);
55                Associate(fronts, referencePoints);
56                List<Solution> solutionsToAdd = Niching(k, referencePoints, random);
57                nextGeneration = Utility.Concat(nextGeneration, solutionsToAdd);
58            }
59
60            return nextGeneration;
61        }
62
63        private static void Normalize(List<Solution> st, int numberOfObjectives)
64        {
65            // Find the ideal point
66            double[] idealPoint = new double[numberOfObjectives];
67
68            foreach (var solution in st)
69                solution.ConvertedFitness = new double[numberOfObjectives];
70
71            for (int j = 0; j < numberOfObjectives; j++)
72            {
73                // Compute ideal point
74                idealPoint[j] = Utility.Min(s => s.Fitness[j], st);
75
76                // Translate objectives and save the modified fitness values in ConvertedFitness
77                foreach (var solution in st)
78                    solution.ConvertedFitness[j] = solution.Fitness[j] - idealPoint[j];
79            }
80
81            // Find the extreme points
82            Solution[] extremePoints = new Solution[numberOfObjectives];
83            for (int j = 0; j < numberOfObjectives; j++)
84            {
85                // Compute extreme points
86                double[] weights = new double[numberOfObjectives];
87                for (int i = 0; i < numberOfObjectives; i++) weights[i] = EPSILON;
88                weights[j] = 1;
89
90                extremePoints[j] = Utility.ArgMin(s => ASF(s.ConvertedFitness, weights), st);
91            }
92
93            // Compute intercepts
94            List<double> intercepts = GetIntercepts(extremePoints.ToList());
95
96            // Normalize objectives
97            NormalizeObjectives(st, intercepts, idealPoint);
98        }
99
100        private static void NormalizeObjectives(List<Solution> st, List<double> intercepts, double[] idealPoint)
101        {
102            int numberOfObjectives = st.First().Fitness.Length;
103
104            foreach (var solution in st)
105            {
106                for (int i = 0; i < numberOfObjectives; i++)
107                {
108                    if (Math.Abs(intercepts[i] - idealPoint[i]) > EPSILON)
109                    {
110                        solution.ConvertedFitness[i] = solution.ConvertedFitness[i] / (intercepts[i] - idealPoint[i]);
111                    }
112                    else
113                    {
114                        solution.ConvertedFitness[i] = solution.ConvertedFitness[i] / EPSILON;
115                    }
116                }
117            }
118        }
119
120        private static void Associate(List<List<Solution>> fronts, List<ReferencePoint> referencePoints)
121        {
122            for (int f = 0; f < fronts.Count; f++)
123            {
124                foreach (var solution in fronts[f])
125                {
126                    // find reference point for which the perpendicular distance to the current
127                    // solution is the lowest
128                    var rpAndDist = Utility.MinArgMin(rp => GetPerpendicularDistance(rp.Values, solution.ConvertedFitness), referencePoints);
129                    // associated reference point
130                    var arp = rpAndDist.Item1;
131                    // distance to that reference point
132                    var dist = rpAndDist.Item2;
133
134                    if (f + 1 != fronts.Count)
135                    {
136                        // Todo: Add member for reference point on index min_rp
137                        arp.NumberOfAssociatedSolutions++;
138                    }
139                    else
140                    {
141                        // Todo: Add potential member for reference point on index min_rp
142                        arp.AddPotentialAssociatedSolution(solution, dist);
143                    }
144                }
145            }
146        }
147
148        private static List<Solution> Niching(int k, List<ReferencePoint> referencePoints, IRandom random)
149        {
150            List<Solution> solutions = new List<Solution>();
151            while (solutions.Count != k)
152            {
153                ReferencePoint min_rp = FindNicheReferencePoint(referencePoints, random);
154
155                Solution chosen = SelectClusterMember(min_rp);
156                if (chosen == null)
157                {
158                    referencePoints.Remove(min_rp);
159                }
160                else
161                {
162                    min_rp.NumberOfAssociatedSolutions++;
163                    min_rp.RemovePotentialAssociatedSolution(chosen);
164                    solutions.Add(chosen);
165                }
166            }
167
168            return solutions;
169        }
170
171        private static ReferencePoint FindNicheReferencePoint(List<ReferencePoint> referencePoints, IRandom random)
172        {
173            // the minimum number of associated solutions for a reference point over all reference points
174            int minNumber = Utility.Min(rp => rp.NumberOfAssociatedSolutions, referencePoints);
175
176            // the reference points that share the number of associated solutions where that number
177            // is equal to minNumber
178            List<ReferencePoint> minAssociatedReferencePoints = new List<ReferencePoint>();
179            foreach (var referencePoint in referencePoints)
180                if (referencePoint.NumberOfAssociatedSolutions == minNumber)
181                    minAssociatedReferencePoints.Add(referencePoint);
182
183            if (minAssociatedReferencePoints.Count > 1)
184                return minAssociatedReferencePoints[random.Next(minAssociatedReferencePoints.Count)];
185            else
186                return minAssociatedReferencePoints.Single();
187        }
188
189        private static Solution SelectClusterMember(ReferencePoint referencePoint)
190        {
191            Solution chosen = null;
192            if (referencePoint.HasPotentialMember())
193            {
194                if (referencePoint.NumberOfAssociatedSolutions == 0)
195                    chosen = referencePoint.FindClosestMember();
196                else
197                    chosen = referencePoint.RandomMember();
198            }
199            return chosen;
200        }
201
202        private static double GetPerpendicularDistance(double[] values, double[] fitness)
203        {
204            double numerator = 0;
205            double denominator = 0;
206            for (int i = 0; i < values.Length; i++)
207            {
208                numerator += values[i] * fitness[i];
209                denominator += Math.Pow(values[i], 2);
210            }
211            double k = numerator / denominator;
212
213            double d = 0;
214            for (int i = 0; i < values.Length; i++)
215            {
216                d += Math.Pow(k * values[i] - fitness[i], 2);
217            }
218            return Math.Sqrt(d);
219        }
220
221        private static double ASF(double[] x, double[] weight)
222        {
223            int numberOfObjectives = x.Length;
224            List<int> dimensions = new List<int>();
225            for (int i = 0; i < numberOfObjectives; i++)
226                dimensions.Add(i);
227
228            return Utility.Max((dim) => x[dim] / weight[dim], dimensions);
229        }
230
231        private static List<double> GetIntercepts(List<Solution> extremePoints)
232        {
233            int numberOfObjectives = extremePoints.First().Fitness.Length;
234
235            // Check whether there are duplicate extreme points. This might happen but the original
236            // paper does not mention how to deal with it.
237            bool duplicate = false;
238            for (int i = 0; !duplicate && i < extremePoints.Count; i++)
239            {
240                for (int j = i + 1; !duplicate && j < extremePoints.Count; j++)
241                {
242                    duplicate = extremePoints[i] == extremePoints[j];
243                }
244            }
245
246            List<double> intercepts = new List<double>();
247
248            // todo: do handling of this case as in Tsung Che Chiang's implementation
249            if (duplicate)
250            { // cannot construct the unique hyperplane (this is a casual method to deal with the condition)
251                for (int f = 0; f < numberOfObjectives; f++)
252                {
253                    // extreme_points[f] stands for the individual with the largest value of
254                    // objective f
255                    intercepts.Add(extremePoints[f].ConvertedFitness[f]);
256                }
257            }
258            else
259            {
260                // Find the equation of the hyperplane
261                List<double> b = new List<double>(); //(pop[0].objs().size(), 1.0);
262                for (int i = 0; i < numberOfObjectives; i++)
263                {
264                    b.Add(1.0);
265                }
266
267                List<List<double>> a = extremePoints.Select(p => p.ConvertedFitness.ToList()).ToList();
268
269                List<double> x = GaussianElimination(a, b);
270
271                // Find intercepts
272                for (int f = 0; f < numberOfObjectives; f++)
273                {
274                    intercepts.Add(1.0 / x[f]);
275                }
276            }
277
278            return intercepts;
279        }
280
281        private static List<double> GaussianElimination(List<List<double>> a, List<double> b)
282        {
283            List<double> x = new List<double>();
284
285            int n = a.Count;
286            for (int i = 0; i < n; i++)
287                a[i].Add(b[i]);
288
289            for (int @base = 0; @base < n - 1; @base++)
290                for (int target = @base + 1; target < n; target++)
291                {
292                    double ratio = a[target][@base] / a[@base][@base];
293                    for (int term = 0; term < a[@base].Count; term++)
294                        a[target][term] = a[target][term] - a[@base][term] * ratio;
295                }
296
297            for (int i = 0; i < n; i++)
298                x.Add(0.0);
299
300            for (int i = n - 1; i >= 0; i--)
301            {
302                for (int known = i + 1; known < n; known++)
303                    a[i][n] = a[i][n] - a[i][known] * x[known];
304                x[i] = a[i][n] / a[i][i];
305            }
306
307            return x;
308        }
309    }
310}
Note: See TracBrowser for help on using the repository browser.