PaGMO  1.1.5
hv3d.cpp
1 /*****************************************************************************
2  * Copyright (C) 2004-2015 The PaGMO development team, *
3  * Advanced Concepts Team (ACT), European Space Agency (ESA) *
4  * *
5  * https://github.com/esa/pagmo *
6  * *
7  * act@esa.int *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the *
21  * Free Software Foundation, Inc., *
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
23  *****************************************************************************/
24 
25 
26 #include "hv3d.h"
27 #include "wfg.h"
28 
29 namespace pagmo { namespace util { namespace hv_algorithm {
30 
32 
39 hv3d::hv3d(bool initial_sorting) : m_initial_sorting(initial_sorting) { }
40 
42 
56 double hv3d::compute(std::vector<fitness_vector> &points, const fitness_vector &r_point) const
57 {
58  if (m_initial_sorting) {
59  sort(points.begin(), points.end(), fitness_vector_cmp(2,'<'));
60  }
61  double V = 0.0; // hypervolume
62  double A = 0.0; // area of the sweeping plane
63  std::multiset<fitness_vector, fitness_vector_cmp> T(fitness_vector_cmp(0, '>'));
64 
65  // sentinel points (r_point[0], -INF, r_point[2]) and (-INF, r_point[1], r_point[2])
66  const double INF = std::numeric_limits<double>::max();
67  fitness_vector sA(r_point.begin(), r_point.end()); sA[1] = -INF;
68  fitness_vector sB(r_point.begin(), r_point.end()); sB[0] = -INF;
69 
70  T.insert(sA);
71  T.insert(sB);
72  double z3 = points[0][2];
73  T.insert(points[0]);
74  A = fabs((points[0][0] - r_point[0]) * (points[0][1] - r_point[1]));
75 
76  std::multiset<fitness_vector>::iterator p;
77  std::multiset<fitness_vector>::iterator q;
78  for(std::vector<fitness_vector>::size_type idx = 1 ; idx < points.size() ; ++idx) {
79  p = T.insert(points[idx]);
80  q = (p);
81  ++q; //setup q to be a successor of p
82  if ( (*q)[1] <= (*p)[1] ) { // current point is dominated
83  T.erase(p); // disregard the point from further calculation
84  } else {
85  V += A * fabs(z3 - (*p)[2]);
86  z3 = (*p)[2];
87  std::multiset<fitness_vector>::reverse_iterator rev_it(q);
88  ++rev_it;
89 
90  std::multiset<fitness_vector>::reverse_iterator erase_begin (rev_it);
91  std::multiset<fitness_vector>::reverse_iterator rev_it_pred;
92  while((*rev_it)[1] >= (*p)[1] ) {
93  rev_it_pred = rev_it;
94  ++rev_it_pred;
95  A -= fabs(((*rev_it)[0] - (*rev_it_pred)[0])*((*rev_it)[1] - (*q)[1]));
96  ++rev_it;
97  }
98  A += fabs(((*p)[0] - (*(rev_it))[0])*((*p)[1] - (*q)[1]));
99  T.erase(rev_it.base(),erase_begin.base());
100  }
101  }
102  V += A * fabs(z3 - r_point[2]);
103 
104  return V;
105 }
106 
108 bool hv3d::hycon3d_tree_cmp::operator()(const std::pair<fitness_vector, int> &a, const std::pair<fitness_vector, int> &b)
109 {
110  return a.first[0] > b.first[0];
111 }
112 
114 
117 double hv3d::box_volume(const box3d &b)
118 {
119  return fabs((b.ux - b.lx) * (b.uy - b.ly) * (b.uz - b.lz));
120 }
121 
123 bool hv3d::hycon3d_sort_cmp(const std::pair<fitness_vector, unsigned int> &a, const std::pair<fitness_vector, unsigned int> &b)
124 {
125  return a.first[2] < b.first[2];
126 }
127 
129 /*
130  * This method is the implementation of the HyCon3D algorithm.
131  * This algorithm computes the exclusive contribution to the hypervolume by every point, using an efficient HyCon3D algorithm by Emmerich and Fonseca.
132  *
133  * @see "Computing hypervolume contribution in low dimensions: asymptotically optimal algorithm and complexity results", Michael T. M. Emmerich, Carlos M. Fonseca
134  *
135  * @param[in] points vector of points containing the 3-dimensional points for which we compute the hypervolume
136  * @param[in] r_point reference point for the points
137  * @return vector of exclusive contributions by every point
138  */
139 std::vector<double> hv3d::contributions(std::vector<fitness_vector> &points, const fitness_vector &r_point) const
140 {
141  // Make a copy of the original set of points
142  std::vector<fitness_vector> p(points.begin(), points.end());
143 
144  std::vector<std::pair<fitness_vector, unsigned int> > point_pairs;
145  point_pairs.reserve(p.size());
146  for(unsigned int i = 0 ; i < p.size() ; ++i) {
147  point_pairs.push_back(std::make_pair(p[i], i));
148  }
149  if (m_initial_sorting) {
150  sort(point_pairs.begin(), point_pairs.end(), hycon3d_sort_cmp);
151  }
152  for(unsigned int i = 0 ; i < p.size() ; ++i) {
153  p[i] = point_pairs[i].first;
154  }
155 
156 
157  typedef std::multiset<std::pair<fitness_vector, int>, hycon3d_tree_cmp > tree_t;
158 
159  unsigned int n = p.size();
160  const double INF = std::numeric_limits<double>::max();
161 
162  // Placeholder value for undefined lower z value.
163  const double NaN = INF;
164 
165  // Contributions
166  std::vector<double> c(n, 0.0);
167 
168  // Sentinel points
169  fitness_vector s_x(3, -INF); s_x[0] = r_point[0]; // (r,oo,oo)
170  fitness_vector s_y(3, -INF); s_y[1] = r_point[1]; // (oo,r,oo)
171  fitness_vector s_z(3, -INF); s_z[2] = r_point[2]; // (oo,oo,r)
172 
173  p.push_back(s_z); // p[n]
174  p.push_back(s_x); // p[n + 1]
175  p.push_back(s_y); // p[n + 2]
176 
177  tree_t T;
178  T.insert(std::make_pair(p[0], 0));
179  T.insert(std::make_pair(s_x, n + 1));
180  T.insert(std::make_pair(s_y, n + 2));
181 
182  // Boxes
183  std::vector<std::deque<box3d> > L(n + 3);
184 
185  box3d b(r_point[0], r_point[1], NaN, p[0][0], p[0][1], p[0][2]);
186  L[0].push_front(b);
187 
188  for (unsigned int i = 1 ; i < n + 1 ; ++i) {
189  std::pair<fitness_vector, int> pi(p[i], i);
190 
191  tree_t::iterator it = T.lower_bound(pi);
192 
193  // Point is dominated
194  if (p[i][1] >= (*it).first[1]) {
195  return wfg(2).contributions(points, r_point);
196  }
197 
198  tree_t::reverse_iterator r_it(it);
199 
200  std::vector<int> d;
201 
202  while((*r_it).first[1] > p[i][1]) {
203  d.push_back((*r_it).second);
204  ++r_it;
205  }
206 
207  int r = (*it).second;
208  int t = (*r_it).second;
209 
210  T.erase(r_it.base(), it);
211 
212  // Process right neighbor region, region R
213  while(!L[r].empty()) {
214  box3d& b = L[r].front();
215  if(b.ux >= p[i][0]) {
216  b.lz = p[i][2];
217  c[r] += box_volume(b);
218  L[r].pop_front();
219  } else if(b.lx > p[i][0]) {
220  b.lz = p[i][2];
221  c[r] += box_volume(b);
222  b.lx = p[i][0];
223  b.uz = p[i][2];
224  b.lz = NaN;
225  break;
226  } else {
227  break;
228  }
229  }
230 
231  // Process dominated points, region M
232  double xleft = p[t][0];
233  std::vector<int>::reverse_iterator r_it_idx = d.rbegin();
234  std::vector<int>::reverse_iterator r_it_idx_e = d.rend();
235  for(;r_it_idx != r_it_idx_e ; ++r_it_idx) {
236  int jdom = *r_it_idx;
237  while(!L[jdom].empty()) {
238  box3d& b = L[jdom].front();
239  b.lz = p[i][2];
240  c[jdom] += box_volume(b);
241  L[jdom].pop_front();
242  }
243  L[i].push_back(box3d(xleft, p[jdom][1], NaN, p[jdom][0], p[i][1], p[i][2]));
244  xleft = p[jdom][0];
245  }
246  L[i].push_back(box3d(xleft, p[r][1], NaN, p[i][0], p[i][1], p[i][2]));
247  xleft = p[t][0];
248 
249  // Process left neighbor region, region L
250  while(!L[t].empty()) {
251  box3d &b = L[t].back();
252  if(b.ly > p[i][1]) {
253  b.lz = p[i][2];
254  c[t] += box_volume(b);
255  xleft = b.lx;
256  L[t].pop_back();
257  } else {
258  break;
259  }
260  }
261  if (xleft > p[t][0]) {
262  L[t].push_back(box3d(xleft, p[i][1], NaN, p[t][0], p[t][1], p[i][2]));
263  }
264  T.insert(std::make_pair(p[i], i));
265  }
266 
267  // Fix the indices
268  std::vector<double> contribs(n, 0.0);
269  for(unsigned int i=0;i < c.size();++i) {
270  contribs[point_pairs[i].second] = c[i];
271  }
272  return contribs;
273 }
274 
276 
284 void hv3d::verify_before_compute(const std::vector<fitness_vector> &points, const fitness_vector &r_point) const
285 {
286  if (r_point.size() != 3) {
287  pagmo_throw(value_error, "Algorithm hv3d works only for 3-dimensional cases");
288  }
289 
290  base::assert_minimisation(points, r_point);
291 }
292 
295 {
296  return base_ptr(new hv3d(*this));
297 }
298 
300 std::string hv3d::get_name() const
301 {
302  return "hv3d algorithm";
303 }
304 
305 } } }
306 
307 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::util::hv_algorithm::hv3d)
Root PaGMO namespace.
hv3d(bool initial_sorting=true)
Constructor.
Definition: hv3d.cpp:39
double compute(std::vector< fitness_vector > &, const fitness_vector &) const
Compute hypervolume.
Definition: hv3d.cpp:56
std::string get_name() const
Algorithm name.
Definition: hv3d.cpp:300
void assert_minimisation(const std::vector< fitness_vector > &, const fitness_vector &) const
Assert that reference point dominates every other point from the set.
base_ptr clone() const
Clone method.
Definition: hv3d.cpp:294
void verify_before_compute(const std::vector< fitness_vector > &, const fitness_vector &) const
Verify before compute.
Definition: hv3d.cpp:284
WFG hypervolume algorithm.
Definition: wfg.h:48
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
boost::shared_ptr< base > base_ptr
Base hypervolume algorithm class.
hv3d hypervolume algorithm class
Definition: hv3d.h:52
std::vector< double > contributions(std::vector< fitness_vector > &, const fitness_vector &) const
Contributions method.
Definition: wfg.cpp:88
std::vector< double > contributions(std::vector< fitness_vector > &, const fitness_vector &) const
Contributions method.
Definition: hv3d.cpp:139