Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Problems.MultiObjectiveTestFunctions/HeuristicLab.Problems.MultiObjectiveTestFunctions/3.3/Calculators/MultiDimensionalHypervolume.cs @ 14018

Last change on this file since 14018 was 14018, checked in by mkommend, 8 years ago

#1087: Rewrote and adapted the multi-objective calculators.

File size: 10.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using HeuristicLab.Common;
26
27namespace HeuristicLab.Problems.MultiObjectiveTestFunctions {
28
29  public class MultiDimensionalHypervolume {
30    /// <summary>
31    /// The Hyprevolume-metric is defined as the Hypervolume enclosed between a given reference point,
32    /// that is fixed for every evaluation function and the evaluated front.
33    ///
34    /// Example:
35    /// r is the reference Point at (1|1)  and every Point p is part of the evaluated front
36    /// The filled Area labled HV is the 2 diensional Hypervolume enclosed by this front.
37    ///
38    /// (0|1)                (1|1)
39    ///   +      +-------------r
40    ///   |      |###### HV ###|
41    ///   |      p------+######|
42    ///   |             p+#####|
43    ///   |              |#####|
44    ///   |              p-+###|
45    ///   |                p---+
46    ///   |                 
47    ///   +--------------------1
48    /// (0|0)                (1|0)
49    ///
50    ///  Please note that in this example both dimensions are minimized. The reference Point need to be dominated by EVERY point in the evaluated front
51    ///
52    /// This is an efficient calculation for a multidimensional Hypervolume as described in
53    /// "Faster S-Metric Calculation by Considering Dominated
54    /// Hypervolume as Klee’s Measure Problem" by
55    /// Nicola Beume and Günter Rudolph.
56    ///
57    ///
58    /// A reference Impelementation in C++ can be found at http://image.diku.dk/shark/doxygen_pages/html/_hypervolume_calculator_8h_source.html
59    /// </summary>
60    public static double Calculate(IEnumerable<double[]> points, IEnumerable<double> reference) {
61      double[] referencePoint = reference.ToArray<double>();
62      if (referencePoint == null || referencePoint.Length < 3) throw new ArgumentException("ReferencePoint unfit for complex Hypervolume calculation");
63      if (!IsDominated(referencePoint, points)) {
64        throw new ArgumentException("ReferencePoint unfit for complex Hypervolume calculation");
65      }
66      int objectives = referencePoint.Length;
67      List<double[]> lpoints = new List<double[]>();
68      foreach (double[] p in points) {
69        lpoints.Add(p);
70      }
71      lpoints.StableSort(new Utilities.DimensionComparer(objectives - 1, false));
72
73      double[] regLow = new double[objectives];
74      for (int i = 0; i < objectives; i++) {
75        regLow[i] = 1E15;
76      }
77      foreach (double[] p in lpoints) {
78        for (int i = 0; i < regLow.Length; i++) {
79          if (regLow[i] > p[i]) regLow[i] = p[i];
80        }
81      }
82
83      return Stream(regLow, referencePoint, lpoints, 0, referencePoint[objectives - 1], (int)Math.Sqrt(points.Count()), objectives);
84    }
85
86    private static bool IsDominated(double[] referencePoint, IEnumerable<double[]> points) {
87      foreach (double[] point in points) {
88        for (int i = 0; i < referencePoint.Length; i++) {
89          if (referencePoint[i] < point[i]) {
90            return false;
91          }
92        }
93      }
94      return true;
95    }
96
97    private static double Stream(double[] regionLow, double[] regionUp, List<double[]> points, int split, double cover, int sqrtNoPoints, int objectives) {
98      double coverOld = cover;
99      int coverIndex = 0;
100      int coverIndexOld = -1;
101      int c;
102      double result = 0;
103
104      double dMeasure = GetMeasure(regionLow, regionUp, objectives);
105      while (cover == coverOld && coverIndex < points.Count()) {
106        if (coverIndexOld == coverIndex) break;
107        coverIndexOld = coverIndex;
108        if (Covers(points[coverIndex], regionLow, objectives)) {
109          cover = points[coverIndex][objectives - 1];
110          result += dMeasure * (coverOld - cover);
111        } else coverIndex++;
112
113      }
114
115      for (c = coverIndex; c > 0; c--) if (points[c - 1][objectives - 1] == cover) coverIndex--;
116      if (coverIndex == 0) return result;
117
118      bool allPiles = true;
119      int[] piles = new int[coverIndex];
120      for (int i = 0; i < coverIndex; i++) {
121        piles[i] = IsPile(points[i], regionLow, regionUp, objectives);
122        if (piles[i] == -1) {
123          allPiles = false;
124          break;
125        }
126      }
127
128      if (allPiles) {
129        double[] trellis = new double[regionUp.Length];
130        for (int j = 0; j < trellis.Length; j++) trellis[j] = regionUp[j];
131        double current = 0;
132        double next = 0;
133        int i = 0;
134        do {
135          current = points[i][objectives - 1];
136          do {
137            if (points[i][piles[i]] < trellis[piles[i]]) trellis[piles[i]] = points[i][piles[i]];
138            i++;
139            if (i < coverIndex) next = points[i][objectives - 1];
140            else { next = cover; break; }
141          } while (next == current);
142          result += ComputeTrellis(regionLow, regionUp, trellis, objectives) * (next - current);
143        } while (next != cover);
144      } else {
145        double bound = -1;
146        double[] boundaries = new double[coverIndex];
147        double[] noBoundaries = new double[coverIndex];
148        int boundIdx = 0;
149        int noBoundIdx = 0;
150
151        do {
152          for (int i = 0; i < coverIndex; i++) {
153            int contained = ContainesBoundary(points[i], regionLow, split);
154            if (contained == 0) boundaries[boundIdx++] = points[i][split];
155            else if (contained == 1) noBoundaries[noBoundIdx++] = points[i][split];
156          }
157          if (boundIdx > 0) bound = GetMedian(boundaries, boundIdx);
158          else if (noBoundIdx > sqrtNoPoints) bound = GetMedian(noBoundaries, noBoundIdx);
159          else split++;
160        } while (bound == -1.0);
161
162        List<double[]> pointsChildLow, pointsChildUp;
163        pointsChildLow = new List<double[]>();
164        pointsChildUp = new List<double[]>();
165        double[] regionUpC = new double[regionUp.Length];
166        for (int j = 0; j < regionUpC.Length; j++) regionUpC[j] = regionUp[j];
167        double[] regionLowC = new double[regionLow.Length];
168        for (int j = 0; j < regionLowC.Length; j++) regionLowC[j] = regionLow[j];
169
170        for (int i = 0; i < coverIndex; i++) {
171          if (PartCovers(points[i], regionUpC, objectives)) pointsChildUp.Add(points[i]);
172          if (PartCovers(points[i], regionUp, objectives)) pointsChildLow.Add(points[i]);
173        }
174        //this could/should be done in Parallel
175
176        if (pointsChildUp.Count() > 0) result += Stream(regionLow, regionUpC, pointsChildUp, split, cover, sqrtNoPoints, objectives);
177        if (pointsChildLow.Count() > 0) result += Stream(regionLowC, regionUp, pointsChildLow, split, cover, sqrtNoPoints, objectives);
178      }
179      return result;
180    }
181
182    private static double GetMedian(double[] vector, int length) {
183      if (vector.Length != length) {
184        double[] vec = new double[length];
185        Array.Copy(vector, vec, length);
186        vector = vec;
187      }
188      return vector.Median();
189
190    }
191
192    private static double ComputeTrellis(double[] regionLow, double[] regionUp, double[] trellis, int objectives) {
193      bool[] bs = new bool[objectives - 1];
194      for (int i = 0; i < bs.Length; i++) bs[i] = true;
195
196      double result = 0;
197      uint noSummands = BinarayToInt(bs);
198      int oneCounter; double summand;
199      for (uint i = 1; i <= noSummands; i++) {
200        summand = 1;
201        IntToBinary(i, bs);
202        oneCounter = 0;
203        for (int j = 0; j < objectives - 1; j++) {
204          if (bs[j]) {
205            summand *= regionUp[j] - trellis[j];
206            oneCounter++;
207          } else {
208            summand *= regionUp[j] - regionLow[j];
209          }
210        }
211        if (oneCounter % 2 == 0) result -= summand;
212        else result += summand;
213
214      }
215      return result;
216    }
217
218    private static void IntToBinary(uint i, bool[] bs) {
219      for (int j = 0; j < bs.Length; j++) bs[j] = false;
220      uint rest = i;
221      int idx = 0;
222      while (rest != 0) {
223        bs[idx] = rest % 2 == 1;
224        rest = rest / 2;
225        idx++;
226      }
227
228    }
229
230    private static uint BinarayToInt(bool[] bs) {
231      uint result = 0;
232      for (int i = 0; i < bs.Length; i++) {
233        result += bs[i] ? ((uint)1 << i) : 0;
234      }
235      return result;
236    }
237
238    private static int IsPile(double[] cuboid, double[] regionLow, double[] regionUp, int objectives) {
239      int pile = cuboid.Length;
240      for (int i = 0; i < objectives - 1; i++) {
241        if (cuboid[i] > regionLow[i]) {
242          if (pile != objectives) return 1;
243          pile = i;
244        }
245      }
246      return pile;
247    }
248
249    private static double GetMeasure(double[] regionLow, double[] regionUp, int objectives) {
250      double volume = 1;
251      for (int i = 0; i < objectives - 1; i++) {
252        volume *= (regionUp[i] - regionLow[i]);
253      }
254      return volume;
255    }
256
257    private static int ContainesBoundary(double[] cub, double[] regionLow, int split) {
258      if (regionLow[split] >= cub[split]) return -1;
259      else {
260        for (int j = 0; j < split; j++) {
261          if (regionLow[j] < cub[j]) return 1;
262        }
263      }
264      return 0;
265    }
266
267    private static bool PartCovers(double[] v, double[] regionUp, int objectives) {
268      for (int i = 0; i < objectives - 1; i++) {
269        if (v[i] >= regionUp[i]) return false;
270      }
271      return true;
272    }
273
274    private static bool Covers(double[] v, double[] regionLow, int objectives) {
275      for (int i = 0; i < objectives - 1; i++) {
276        if (v[i] > regionLow[i]) return false;
277      }
278      return true;
279    }
280
281
282  }
283}
Note: See TracBrowser for help on using the repository browser.