Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2825-NSGA3/HeuristicLab.Algorithms.NSGA3/3.3/ReferencePoint.cs @ 17693

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

#2825 Remove unnecessary parameter. Refactoring.

File size: 9.9 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using HeuristicLab.Common;
5using HeuristicLab.Core;
6
7namespace HeuristicLab.Algorithms.NSGA3
8{
9    public class ReferencePoint : IDeepCloneable
10    {
11        #region Properties
12
13        // The potentially associated solutions to this reference point and the distance to that solution
14        private readonly Dictionary<Solution, double> potentialAssociatedSolutions = new Dictionary<Solution, double>();
15
16        private readonly IRandom random;
17
18        public double[] Values { get; }
19        public int NumberOfDimensions => Values.Length;
20        public int NumberOfAssociatedSolutions { get; set; } = 0;
21
22        #endregion Properties
23
24        #region Constructors
25
26        public ReferencePoint(IRandom random, int numberOfDimensions)
27        {
28            this.random = random;
29            Values = new double[numberOfDimensions];
30        }
31
32        public ReferencePoint(ReferencePoint other)
33        {
34            random = other.random;
35            Values = new double[other.NumberOfDimensions];
36            other.Values.CopyTo(Values, 0);
37        }
38
39        public ReferencePoint(ReferencePoint other, Cloner cloner)
40        {
41            random = cloner.Clone(other.random);
42            Values = new double[other.NumberOfDimensions];
43            other.Values.CopyTo(Values, 0);
44        }
45
46        public IDeepCloneable Clone(Cloner cloner)
47        {
48            return new ReferencePoint(this, cloner);
49        }
50
51        public object Clone()
52        {
53            return new Cloner().Clone(this);
54        }
55
56        #endregion Constructors
57
58        #region Static methods
59
60        /// <summary>
61        /// Returns the number of reference points that would be created when using <see
62        /// cref="GenerateReferencePoints(IRandom, int)" />.
63        /// </summary>
64        /// <param name="nDim"></param>
65        /// <returns></returns>
66        public static int GetNumberOfGeneratedReferencePoints(int nDim)
67        {
68            int outerDiv;
69            int innerDiv;
70            switch (nDim)
71            {
72                case 3:
73                    outerDiv = 12;
74                    innerDiv = 0;
75                    break;
76
77                case 5:
78                    outerDiv = 6;
79                    innerDiv = 0;
80                    break;
81
82                case 8:
83                    outerDiv = 3;
84                    innerDiv = 2;
85                    break;
86
87                case 10:
88                    outerDiv = 3;
89                    innerDiv = 2;
90                    break;
91
92                case 15:
93                    outerDiv = 2;
94                    innerDiv = 1;
95                    break;
96
97                default:
98                    return -1;
99            }
100
101            return GetNumberOfGeneratedReferencePoints(nDim, outerDiv, innerDiv);
102        }
103
104        public static int GetNumberOfGeneratedReferencePoints(int nDim, int outerDiv, int innerDiv = 0)
105        {
106            int outerPoints = Utility.NCR(nDim + outerDiv - 1, outerDiv);
107            int innerPoints = innerDiv == 0 ? 0 : Utility.NCR(nDim + innerDiv - 1, innerDiv);
108            return outerPoints + innerPoints;
109        }
110
111        public static int GetPopulationSizeForReferencePoints(int referencePoints)
112        {
113            return (referencePoints + 3) / 4 * 4;
114        }
115
116        /// <summary>
117        /// Generate reference points that are evenly distributed over a hyperplane with dimensions
118        /// <paramref name="nDim" /> - 1 with the sum of the values in all dimensions being equal to
119        /// 1 for each reference point. <paramref name="nDim" /> may only be one of {3, 5, 8, 10, 15}
120        /// -&gt; The number of inner divisions and outer divisions are determined based on the
121        /// values provided in the NSGA-III paper, chapter V: Results. If different dimensions are
122        /// given, a NotSupportedException is thrown.
123        /// </summary>
124        /// <param name="random">
125        /// The <see cref="IRandom" /> to give as a parameter to the ReferencePoint constructors.
126        /// </param>
127        /// <param name="nDim">The dimensionality of the Reference Points</param>
128        /// <returns></returns>
129        public static List<ReferencePoint> GenerateReferencePoints(IRandom random, int nDim)
130        {
131            List<ReferencePoint> referencePoints;
132            switch (nDim)
133            {
134                case 3:
135                    referencePoints = GenerateReferencePoints(random, nDim, 12, 0);
136                    break;
137
138                case 5:
139                    referencePoints = GenerateReferencePoints(random, nDim, 6, 0);
140                    break;
141
142                case 8:
143                    referencePoints = GenerateReferencePoints(random, nDim, 3, 2);
144                    break;
145
146                case 10:
147                    referencePoints = GenerateReferencePoints(random, nDim, 3, 2);
148                    break;
149
150                case 15:
151                    referencePoints = GenerateReferencePoints(random, nDim, 2, 1);
152                    break;
153
154                default:
155                    throw new NotSupportedException("Only the reference points for 3, 5, 8, 10 or 15 dimensions can be created.");
156            }
157
158            return referencePoints;
159        }
160
161        /*
162         * This method is based on Tsung-Che Chiang's NSGA-III implementation (version 1.20)
163         * http://web.ntnu.edu.tw/~tcchiang/publications/nsga3cpp/nsga3cpp.htm
164         */
165
166        /// <summary>
167        /// Generate reference points that are evenly distributed over a hyperplane with dimensions
168        /// <paramref name="nDim" /> - 1 with the sum of the values in all dimensions being equal to
169        /// 1 for each reference point.
170        /// </summary>
171        /// <param name="nDim">The number of dimensions for the reference points.</param>
172        /// <param name="outerDiv">The number of divisions for the outer reference points</param>
173        /// <returns>The generated reference points (Check Fig. 4 in NSGA-III paper).</returns>
174        /// <param name="innerDiv">The number of divisions for the inner reference points</param>
175        /// <returns>
176        /// The generated reference points (Check Fig. 4 in NSGA-III paper). Set to 0, in order to
177        /// not have inner reference points
178        /// </returns>
179        public static List<ReferencePoint> GenerateReferencePoints(IRandom random, int nDim, int outerDiv, int innerDiv = 0)
180        {
181            if (nDim <= 0) throw new ArgumentException("nDim must be greater than 0");
182            if (outerDiv <= 1) throw new ArgumentException("outerDiv must be greater than 1");
183
184            List<ReferencePoint> referencePoints = new List<ReferencePoint>();
185            ReferencePoint refPoint = new ReferencePoint(random, nDim);
186            GenerateRecursive(referencePoints, refPoint, nDim, outerDiv, outerDiv, 0);
187
188            if (innerDiv > 0)
189            {
190                List<ReferencePoint> insideReferencePoints = new List<ReferencePoint>();
191                GenerateRecursive(insideReferencePoints, refPoint, nDim, innerDiv, innerDiv, 0);
192
193                double center = 1.0 / nDim;
194
195                for (int i = 0; i < insideReferencePoints.Count; i++)
196                {
197                    for (int j = 0; j < insideReferencePoints[i].Values.Length; j++)
198                    {
199                        insideReferencePoints[i].Values[j] = (center + insideReferencePoints[i].Values[j]) / 2;
200                    }
201                    referencePoints.Add(insideReferencePoints[i]);
202                }
203            }
204
205            return referencePoints;
206        }
207
208        /*
209         * This method is based on Tsung-Che Chiang's NSGA-III implementation (version 1.20)
210         * http://web.ntnu.edu.tw/~tcchiang/publications/nsga3cpp/nsga3cpp.htm
211         */
212
213        private static void GenerateRecursive(List<ReferencePoint> referencePoints, ReferencePoint refPoint, int numberOfObjectives, int left, int total, int element)
214        {
215            if (element == numberOfObjectives - 1)
216            {
217                refPoint.Values[element] = ((double)left) / total;
218                var newRefPoint = new ReferencePoint(refPoint);
219                referencePoints.Add(newRefPoint);
220            }
221            else
222            {
223                for (int i = 0; i <= left; i++)
224                {
225                    refPoint.Values[element] = (double)i / total;
226                    GenerateRecursive(referencePoints, refPoint, numberOfObjectives, left - i, total, element + 1);
227                }
228            }
229        }
230
231        #endregion Static methods
232
233        public void AddPotentialAssociatedSolution(Solution s, double distance)
234        {
235            if (potentialAssociatedSolutions.ContainsKey(s))
236                throw new InvalidOperationException("Given solution was already added as a potential solution");
237
238            potentialAssociatedSolutions.Add(s, distance);
239        }
240
241        public void RemovePotentialAssociatedSolution(Solution s)
242        {
243            potentialAssociatedSolutions.Remove(s);
244        }
245
246        public bool HasPotentialMember()
247        {
248            return potentialAssociatedSolutions.Any();
249        }
250
251        internal Solution FindClosestMember()
252        {
253            if (!potentialAssociatedSolutions.Any())
254                throw new InvalidOperationException("No potential members exist");
255
256            return Utility.ArgMin(pair => pair.Value, potentialAssociatedSolutions).Key;
257        }
258
259        internal Solution RandomMember()
260        {
261            if (!potentialAssociatedSolutions.Any())
262                throw new InvalidOperationException("No potential members exist");
263
264            return potentialAssociatedSolutions.Keys.ElementAt(random.Next(potentialAssociatedSolutions.Count));
265        }
266    }
267}
Note: See TracBrowser for help on using the repository browser.