Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2825 Bugfix NSGA3 can now be cloned.

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