HypervolumeCalculatorMDHOY.h
Go to the documentation of this file.
1 /*!
2  *
3  *
4  * \brief Implementation of the exact hypervolume calculation in m dimensions.
5  *
6  * \author T.Voss, O.Krause, T. Glasmachers
7  * \date 2014-2016
8  *
9  *
10  * \par Copyright 1995-2017 Shark Development Team
11  *
12  * <BR><HR>
13  * This file is part of Shark.
14  * <http://shark-ml.org/>
15  *
16  * Shark is free software: you can redistribute it and/or modify
17  * it under the terms of the GNU Lesser General Public License as published
18  * by the Free Software Foundation, either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * Shark is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24  * GNU Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public License
27  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30 #ifndef SHARK_ALGORITHMS_DIRECTSEARCH_HYPERVOLUMECALCULATOR_MD_HOY_H
31 #define SHARK_ALGORITHMS_DIRECTSEARCH_HYPERVOLUMECALCULATOR_MD_HOY_H
32 
33 #include <shark/LinAlg/Base.h>
34 
35 #include <algorithm>
36 #include <vector>
37 #include <map>
38 
39 namespace shark {
40 /// \brief Implementation of the exact hypervolume calculation in m dimensions.
41 ///
42 /// The algorithm is described in
43 ///
44 /// Nicola Beume und Guenter Rudolph.
45 /// Faster S-Metric Calculation by Considering Dominated Hypervolume as Klee's Measure Problem.
46 /// In: B. Kovalerchuk (ed.): Proceedings of the Second IASTED Conference on Computational Intelligence (CI 2006),
47 /// pp. 231-236. ACTA Press: Anaheim, 2006.
49 
50  /// \brief Executes the algorithm.
51  /// \param [in] set The set \f$S\f$ of points for which the following assumption needs to hold: \f$\forall s \in S: \lnot \exists s' \in S: s' \preceq s \f$
52  /// \param [in] refPoint The reference point \f$\vec{r} \in \mathbb{R}^n\f$ for the hypervolume calculation, needs to fulfill: \f$ \forall s \in S: s \preceq \vec{r}\f$. .
53  template<typename Set, typename VectorType >
54  double operator()( Set const& points, VectorType const& refPoint){
55  if(points.empty())
56  return 0;
57  SIZE_CHECK( points.begin()->size() == refPoint.size() );
58 
59  std::vector<VectorType> set;
60  set.reserve(points.size());
61  for(std::size_t i = 0; i != points.size(); ++i){
62  set.push_back(points[i]);
63  }
64  std::sort( set.begin(), set.end(), [ ](VectorType const& x, VectorType const& y){return x.back() < y.back();});
65 
66  m_sqrtNoPoints = static_cast< std::size_t >( ::sqrt( static_cast<double>( points.size() ) ) );
67 
68  VectorType regLow( refPoint.size(), 1E15 );
69  for( std::size_t i = 0; i < set.size(); i++ ){
70  noalias(regLow) = min(regLow,set[i]);
71  }
72  return stream( regLow, refPoint, set, 0, refPoint.back() );
73  }
74 
75  template<typename VectorType>
76  int covers( VectorType const& cuboid, VectorType const& regionLow ) {
77  for( std::size_t i = 0; i < cuboid.size()-1; i++ ) {
78  if( cuboid[i] > regionLow[i] )
79  return 0;
80  }
81  return 1;
82  }
83 
84  template<typename VectorType>
85  int partCovers( VectorType const& cuboid, VectorType const& regionUp ) {
86  for( std::size_t i = 0; i < cuboid.size()-1; i++) {
87  if (cuboid[i] >= regionUp[i])
88  return 0;
89  }
90  return 1;
91  }
92 
93  template<typename VectorType>
94  int containsBoundary( VectorType const& cub, VectorType const& regLow, int split ) {
95  if( !( regLow[split] < cub[split] ) ) {
96  return -1;
97  } else {
98  for ( int j = 0; j < split; j++) {
99  if (regLow[j] < cub[j]) {
100  return 1;
101  }
102  }
103  }
104  return 0;
105  }
106 
107  template<typename VectorType>
108  double getMeasure( const VectorType & regionLow, const VectorType & regionUp ) {
109  double volume = 1.0;
110  for( std::size_t i = 0; i < regionLow.size()-1; i++) {
111  volume *= (regionUp[i] - regionLow[i]);
112  }
113  return volume;
114  }
115 
116  template<typename VectorType>
117  int isPile( const VectorType & cuboid, const VectorType & regionLow, const VectorType & regionUp ) {
118  std::size_t pile = cuboid.size();
119  for( std::size_t i = 0; i < cuboid.size()-1; i++ ) {
120  if( cuboid[i] > regionLow[i] ) {
121  if( pile != cuboid.size() ) {
122  return (-1);
123  }
124  pile = i;
125  }
126  }
127 
128  return (int)pile;
129  }
130 
131  template<typename VectorType>
132  unsigned int binaryToInt( const VectorType & v ) {
133  int result = 0;
134  unsigned i;
135  for (i = 0; i < v.size(); i++) {
136  result += v[i] ? ( 1 << i ) : 0;
137  }
138 
139  return result;
140  }
141 
142  template<typename VectorType>
143  void intToBinary(unsigned int i, VectorType & result) {
144  for (std::size_t j = 0; j < result.size(); j++)
145  result[j] = 0;
146 
147  unsigned int rest = i;
148  std::size_t idx = 0;
149 
150  while (rest != 0) {
151  result[idx] = (rest % 2);
152 
153  rest = rest / 2;
154  idx++;
155  }
156  }
157 
158  template<typename VectorType>
159  double computeTrellis( const VectorType & regLow, const VectorType & regUp, const VectorType & trellis ) {
160  std::vector<int> bs( regLow.size()-1, 1 );
161 
162  double result = 0;
163 
164  unsigned int noSummands = binaryToInt(bs);
165  int oneCounter; double summand;
166 
167  for(unsigned i = 1; i <= noSummands; i++ ) {
168  summand = 1;
169  intToBinary(i, bs);
170  oneCounter = 0;
171 
172  for(std::size_t j = 0; j < regLow.size()-1; j++ ) {
173  if (bs[j] == 1) {
174  summand *= regUp[j] - trellis[j];
175  oneCounter++;
176  } else
177  summand *= regUp[j] - regLow[j];
178  }
179 
180  if (oneCounter % 2 == 0)
181  result -= summand ;
182  else
183  result += summand;
184  }
185 
186  return result;
187  }
188 
189  template<typename VectorType>
190  double getMedian( const VectorType & bounds, int length) {
191  if( length == 1 ) {
192  return bounds[0];
193  } else if( length == 2 ) {
194  return bounds[1];
195  }
196 
197  VectorType v( length );
198  std::copy( bounds.begin(), bounds.begin() + length, v.begin() );
199  std::sort( v.begin(), v.end() );
200 
201  return (length % 2 == 1) ? v[length/2] : (v[length/2-1] + v[(length/2)]) / 2;
202  }
203 
204  template<typename Set, typename VectorType>
205  double stream( const VectorType & regionLow,
206  const VectorType & regionUp,
207  const Set & points,
208  int split,
209  double cover
210  ) {
211  std::size_t numObjectives = regionLow.size();
212  double coverOld;
213  coverOld = cover;
214  int coverIndex = 0;
215  int coverIndexOld = -1;
216  int c;
217 
218  double result = 0;
219 
220  double dMeasure = getMeasure(regionLow, regionUp);
221  while( cover == coverOld && coverIndex < static_cast<int>( points.size() ) ) {
222  if( coverIndexOld == coverIndex )
223  break;
224 
225  coverIndexOld = coverIndex;
226 
227  if( covers( points[coverIndex], regionLow) ) {
228  cover = points[coverIndex][numObjectives-1];
229  result += dMeasure * (coverOld - cover);
230  }
231  else
232  coverIndex++;
233  }
234 
235  for (c = coverIndex; c > 0; c--) {
236  if( points[c-1][numObjectives-1] == cover) {
237  coverIndex--;
238  }
239  }
240 
241  if (coverIndex == 0)
242  return (result);
243 
244  bool allPiles = true;
245 
246  std::vector<int> piles( coverIndex );
247 
248  for( int i = 0; i < coverIndex; i++ ) {
249  piles[i] = isPile( points[i], regionLow, regionUp );
250  if (piles[i] == -1) {
251  allPiles = false;
252  break;
253  }
254  }
255 
256  if( allPiles ) {
257  VectorType trellis( regionUp );
258 
259  double current = 0.0;
260  double next = 0.0;
261  int i = 0;
262  do {
263  current = points[i][numObjectives-1];
264  do {
265  if( points[i][piles[i]] < trellis[piles[i]] ) {
266  trellis[piles[i]] = points[i][piles[i]];
267  }
268  i++;
269  if (i < coverIndex) {
270  next = points[i][numObjectives-1];
271  }
272  else {
273  next = cover;
274  break;
275  }
276 
277  }
278  while (next == current);
279 
280  result += computeTrellis(regionLow, regionUp, trellis) * (next - current);
281  } while (next != cover);
282 
283  } else {
284  double bound = -1.0;
285  std::vector<double> boundaries( coverIndex );
286  std::vector<double> noBoundaries( coverIndex );
287  unsigned boundIdx = 0;
288  unsigned noBoundIdx = 0;
289 
290  do {
291  for( int i = 0; i < coverIndex; i++ ) {
292  int contained = containsBoundary(points[i], regionLow, split );
293  if (contained == 1) {
294  boundaries[boundIdx] = points[i][split];
295  boundIdx++;
296  } else if (contained == 0) {
297  noBoundaries[noBoundIdx] = points[i][split];
298  noBoundIdx++;
299  }
300  }
301 
302  if (boundIdx > 0) {
303  bound = getMedian( boundaries, boundIdx );
304  } else if( noBoundIdx > m_sqrtNoPoints ) {
305  bound = getMedian( noBoundaries, noBoundIdx );
306  } else {
307  split++;
308  }
309  } while (bound == -1.0);
310 
311  Set pointsChildLow, pointsChildUp;
312 
313  VectorType regionUpC( regionUp );
314  regionUpC[split] = bound;
315  VectorType regionLowC( regionLow );
316  regionLowC[split] = bound;
317 
318  for( int i = 0; i < coverIndex; i++) {
319  if( partCovers( points[i], regionUpC) ) {
320  pointsChildUp.push_back( points[i] );
321  }
322 
323  if( partCovers( points[i], regionUp ) ) {
324  pointsChildLow.push_back( points[i] );
325  }
326  }
327 
328 
329  if( pointsChildUp.size() > 0 ) {
330  result += stream(regionLow, regionUpC, pointsChildUp, split, cover);
331  }
332  if (pointsChildLow.size() > 0) {
333  result += stream(regionLowC, regionUp, pointsChildLow, split, cover);
334  }
335  }
336 
337  return result;
338  }
339 
340  std::size_t m_sqrtNoPoints;
341 };
342 
343 }
344 #endif