HypervolumeCalculatorMDWFG.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 O.Krause
7  * \date 2017
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_WFG_H
31 #define SHARK_ALGORITHMS_DIRECTSEARCH_HYPERVOLUMECALCULATOR_MD_WFG_H
32 
33 #include <shark/LinAlg/Base.h>
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 /// L. While, L. Bradstreet and L. Barone, "A Fast Way of Calculating Exact Hypervolumes,"
45 /// in IEEE Transactions on Evolutionary Computation, vol. 16, no. 1, pp. 86-95, Feb. 2012.
46 ///
47 /// WFG is extremely fast in practice, while theoretically it has O(2^N) complexity where N
48 /// is the number of points.
49 ///
50 /// We do not implement slicing as the paper showed that it does have only small impact
51 /// while it increases the algorithm complexity dramatically.
53 
54  /// \brief Executes the algorithm.
55  /// \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$
56  /// \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$. .
57  template<typename Set, typename VectorType >
58  double operator()( Set const& points, VectorType const& refPoint)const{
59  if(points.empty())
60  return 0;
61  SIZE_CHECK( points.begin()->size() == refPoint.size() );
62 
63  std::vector<VectorType> set(points.begin(),points.end());
64  std::sort( set.begin(), set.end(), [ ](VectorType const& x, VectorType const& y){return x.front() > y.front();});
65  return wfg(set,refPoint);
66  }
67 
68 private:
69 
70  template<class Set, class VectorType>
71  double wfg(Set const& points, VectorType const& refPoint)const{
72  //first handle a few special cases as they are likely faster to compute than the recursion
73  std::size_t n = points.size();
74  if(n == 0){
75  return 0;
76  }
77  if(n == 1){
78  return boxVolume(points[0],refPoint);
79  }
80  if(n == 2){
81  double volume1 = boxVolume(points[0],refPoint);
82  double volume2 = boxVolume(points[1],refPoint);
83  double volume3 = boxVolume(max(points[0], points[1]),refPoint);
84  return volume1 + volume2 - volume3;
85  }
86  //by default we recurse and compute the sum of hypercontributions
87  //using Hyp(S) = sum_i HypCon{x_i|(x1,...,x_{i-1}}
88  //and S={x1...x_N}.
89  //We can next make use of the fact that HypCon can restrict
90  //the dominated set of S to the set dominated by x_i. This
91  //allows us to throw points away which do not affect the volume.
92  //This makes the algorithm fast as we can hope to throw away
93  //points quickly in early iterations.
94  double volume = 0;
95  for(std::size_t i = 0; i != points.size(); ++i){
96  auto const& point = points[i];
97  //compute restricted pointset wrt point
98  std::vector<RealVector> pointset( points.begin()+i+1, points.end() );
99  limitSet(pointset,point);
100 
101  double baseVol = boxVolume(point,refPoint);
102  volume += baseVol - wfg(pointset,refPoint);
103  }
104  return volume;
105  }
106 
107  /// \brief Restrict the points to the area covered by point and remove all points which are then dominated
108  template<class Pointset, class Point>
109  void limitSet(Pointset& pointset, Point const& point) const{
110  for(auto& p: pointset){
111  noalias(p) = max(p,point);
112  }
113  std::vector<std::size_t> ranks(pointset.size());
114  nonDominatedSort(pointset,ranks);
115  std::size_t end = pointset.size();
116  std::size_t pos = 0;
117  while(pos != end){
118  if(ranks[pos] == 1){
119  ++pos;
120  continue;
121  }
122  --end;
123  if(pos != end){
124  std::swap(pointset[pos],pointset[end]);
125  std::swap(ranks[pos],ranks[end]);
126  }
127  }
128  pointset.erase(pointset.begin() +end, pointset.end());
129  std::sort( pointset.begin(), pointset.end(), [ ](Point const& x, Point const& y){return x.front() > y.front();});
130 
131  }
132  template<class Point1, class Point2>
133  double boxVolume(Point1 const& p,Point2 const& ref) const {
134  double volume = 1;
135  for(std::size_t i = 0; i != ref.size(); ++i){
136  volume *= ref(i) - p(i);
137  }
138  return volume;
139  }
140 };
141 }
142 #endif