PaGMO  1.1.5
hoy.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 "hoy.h"
27 #include "base.h"
28 #include <algorithm>
29 #include <bitset>
30 #include <cmath>
31 #include <limits>
32 #include <boost/numeric/conversion/cast.hpp>
33 
34 namespace pagmo { namespace util { namespace hv_algorithm {
35 
37 hoy::hoy() { }
38 
40 
46 double hoy::compute(std::vector<fitness_vector> &points, const fitness_vector &r_point) const
47 {
48  m_dimension = r_point.size();
49  m_total_size = points.size();
50  m_sqrt_size = sqrt(boost::numeric_cast<double>(m_total_size));
51  m_volume = 0.0;
52 
53  sort(points.begin(), points.end(), fitness_vector_cmp(m_dimension - 1, '<'));
54 
55  m_region_low = new double[m_dimension - 1];
56  m_region_up = new double[m_dimension - 1];
57  m_boundaries = new double[m_total_size];
58  m_no_boundaries = new double[m_total_size];
59  m_piles = new int[m_total_size];
60  m_trellis = new double[m_dimension - 1];
61 
62  // initialize the D-1 dimensional region vectors and D-dimensional reference point
63  for (int i = 0 ; i < m_dimension - 1 ; ++i) {
64  m_region_up[i] = r_point[i];
65  m_region_low[i] = std::numeric_limits<double>::max();
66  }
67 
68  double** initial_points = new double*[m_total_size];
69  for (int n = 0 ; n < m_total_size ; ++n) {
70  initial_points[n] = new double[m_dimension];
71 
72  initial_points[n][m_dimension - 1] = points[n][m_dimension - 1];
73  for (int i = 0 ; i < m_dimension - 1 ; ++i) {
74  initial_points[n][i] = points[n][i];
75  if(initial_points[n][i] < m_region_low[i]) {
76  m_region_low[i] = (double)initial_points[n][i];
77  }
78  }
79  }
80 
81  // call stream initially
82  stream(m_region_low, m_region_up, initial_points, m_total_size, 0, r_point[m_dimension - 1], 0);
83 
84  // free the memory for child node points
85  for (unsigned int n = 0; n < m_child_points.size() ; ++n) {
86  delete[] m_child_points[n];
87  }
88  m_child_points.clear();
89 
90  // free the memory of the initial points
91  for (int n = 0; n < m_total_size ; ++n) {
92  delete[] initial_points[n];
93  }
94  delete[] initial_points;
95 
96  // free the member variables
97  delete[] m_region_low;
98  delete[] m_region_up;
99  delete[] m_boundaries;
100  delete[] m_no_boundaries;
101  delete[] m_piles;
102  delete[] m_trellis;
103 
104  return m_volume;
105 }
106 
107 bool hoy::covers(const double cub[], const double reg_low[]) const
108 {
109  for (int i = 0; i < m_dimension - 1; ++i) {
110  if (cub[i] > reg_low[i]) {
111  return false;
112  }
113  }
114  return true;
115 }
116 
117 bool hoy::part_covers(const double cub[], const double reg_up[]) const
118 {
119  for (int i = 0; i < m_dimension - 1; ++i) {
120  if (cub[i] >= reg_up[i]) {
121  return false;
122  }
123  }
124  return true;
125 }
126 
127 int hoy::contains_boundary(const double cub[], const double reg_low[], const int split) const
128 {
129  // condition only checked for split > 0
130  if (reg_low[split] >= cub[split]){
131  // boundary in m_dimension split not contained in region, thus
132  // boundary is no candidate for the splitting line
133  return -1;
134  }
135  else {
136  for (int j = 0 ; j < split; ++j) { // check boundaries
137  if (reg_low[j] < cub[j]) {
138  // boundary contained in region
139  return 1;
140  }
141  }
142  }
143  // no boundary contained in region
144  return 0;
145 }
146 
147 double hoy::get_measure(const double reg_low[], const double reg_up[]) const
148 {
149  double vol = 1.0;
150  for (int i = 0 ; i < m_dimension - 1 ; ++i) {
151  vol *= (reg_up[i] - reg_low[i]);
152  }
153  return vol;
154 }
155 
156 int hoy::is_pile(const double cub[], const double reg_low[]) const
157 {
158 
159  int pile = m_dimension;
160  // check all dimensions of the node
161  for (int k = 0 ; k < m_dimension - 1 ; ++k) {
162  // k-boundary of the node's region contained in the cuboid?
163  if (cub[k] > reg_low[k]) {
164  if (pile != m_dimension) {
165  // second dimension occured that is not completely covered
166  // ==> cuboid is no pile
167  return -1;
168  }
169  pile = k;
170  }
171  }
172  // if pile == this.dimension then
173  // cuboid completely covers region
174  // case is not possible since covering cuboids have been removed before
175 
176  // region in only one dimenison not completly covered
177  // ==> cuboid is a pile
178  return pile;
179 }
180 
181 double hoy::compute_trellis(const double reg_low[], const double reg_up[], const double trellis[]) const
182 {
183 
184  double total = 1.0; // total area region
185  double empty = 1.0; // inner "empty" box for trellis
186  for (int i = 0 ; i < m_dimension - 1 ; ++i) {
187  // no abs value is required as reg_up[i] > reg_low[i] and trellis[i] >= reg_low[i]
188  total *= (reg_up[i] - reg_low[i]);
189  empty *= (trellis[i] - reg_low[i]);
190  }
191  return total - empty;
192 }
193 
194 double hoy::get_median(double* bounds, unsigned int n) const
195 {
196  if (n < 3) {
197  return bounds[n - 1];
198  }
199  unsigned int n2 = n/2;
200  std::partial_sort(bounds, bounds + n2, bounds + n);
201  return bounds[n2];
202 }
203 
205 void hoy::stream(double m_region_low[], double m_region_up[], double** points, const unsigned int n_points, int split, double cover, unsigned int rec_level) const
206 {
207  double cover_old = cover;
208  unsigned int cover_index = 0;
209 
210  // Identify first covering cuboid.
211  // Compute the D-1 dimensional sweeping hyperplane area
212  double plane_area = get_measure(m_region_low, m_region_up);
213  while (cover == cover_old && cover_index < n_points) {
214  if ( covers(points[cover_index], m_region_low) ) {
215  // new cover value
216  cover = points[cover_index][m_dimension - 1];
217  m_volume += plane_area * (cover_old - cover);
218  }
219  else ++cover_index;
220  }
221 
222  /* cover_index shall be the index of the first point in points which
223  * is ignored in the remaining process
224  *
225  * It may occur that that some points in front of cover_index have the same
226  * d-th coordinate as the point at cover_index. This points must be discarded
227  * and therefore the following for-loop checks for this points and reduces
228  * cover_index if necessary.
229  */
230  for (int c = cover_index ; c > 0; --c) {
231  if (points[c - 1][m_dimension - 1] == cover) {
232  --cover_index;
233  }
234  }
235 
236  // Abort if points is empty
237  if (cover_index == 0) {
238  return;
239  }
240  // Note: in the remainder points is only considered to index cover_index
241 
242  // Make sure we have a trellis (each box must be an i-pile).
243  bool all_piles = true;
244 
245  for (unsigned int i = 0; i < cover_index; ++i) {
246  m_piles[i] = is_pile(points[i], m_region_low);
247  if (m_piles[i] == -1) {
248  all_piles = false;
249  break;
250  }
251  }
252 
253  /*
254  * m_trellis[i] contains the values of the minimal i-coordinate of
255  * the i-piles.
256  * If there is no i-pile the default value is the upper bpund of the region.
257  * The 1-dimensional KMP of the i-piles is: reg[1][i] - trellis[i]
258  *
259  */
260  if (all_piles) { // Proceed with the sweeping.
261  // Initialize trellis with region's upper bound
262  for (int c = 0 ; c < m_dimension - 1; ++c) {
263  m_trellis[c] = m_region_up[c];
264  }
265 
266  double current = 0.0;
267  double next = 0.0;
268  unsigned int i = 0;
269  do { // while(next != cover)
270  current = points[i][m_dimension-1];
271  do { // while(next == current)
272  if (points[i][m_piles[i]] < m_trellis[m_piles[i]]) {
273  m_trellis[m_piles[i]] = points[i][m_piles[i]];
274  }
275  ++i; // index of next point
276  if (i < cover_index) {
277  next = points[i][m_dimension - 1];
278  }
279  else {
280  next = cover;
281  }
282  } while(next == current);
283  m_volume += compute_trellis(m_region_low, m_region_up, m_trellis) * (next - current);
284  } while(next != cover);
285  }
286  // If the trellis was NOT detected, partite the node.
287  else{
288  // Find the splitting boundary.
289  double bound = 0.0;
290  bool not_found = true;
291  unsigned int n_bounds = 0;
292  unsigned int n_no_bounds = 0;
293 
294  do {
295  for (unsigned int i = 0; i < cover_index; ++i) {
296  int contained = contains_boundary(points[i], m_region_low, split);
297  if (contained == 1) {
298  m_boundaries[n_bounds++] = points[i][split];
299  } else if (contained == 0) {
300  m_no_boundaries[n_no_bounds++] = points[i][split];
301  }
302  }
303 
304  //if (boundaries.size() > 0) {
305  if (n_bounds > 0) {
306  bound = get_median(m_boundaries, n_bounds);
307  not_found = false;
308  }
309  else if (n_no_bounds > m_sqrt_size) {
310  bound = get_median(m_no_boundaries, n_no_bounds);
311  not_found = false;
312  }
313  else {
314  ++split;
315  }
316  } while (not_found);
317 
318  // if new frame for child_points is required
319  if (rec_level >= m_child_points.size()) {
320  m_child_points.push_back(new double*[m_total_size]);
321  }
322 
323  unsigned int n_cp = 0;
324 
325  double d_last = m_region_up[split];
326  // Left child
327  m_region_up[split] = bound;
328  for (unsigned int i = 0; i < cover_index ; ++i) {
329  if (part_covers(points[i], m_region_up)) {
330  m_child_points[rec_level][n_cp++] = points[i];
331  }
332  }
333  if (n_cp > 0) {
334  stream(m_region_low, m_region_up, m_child_points[rec_level], n_cp, split, cover, rec_level + 1);
335  }
336 
337  // Right child
338  n_cp = 0;
339  m_region_up[split] = d_last;
340  d_last = m_region_low[split];
341  m_region_low[split] = bound;
342  for (unsigned int i = 0 ; i < cover_index ; ++i) {
343  if (part_covers(points[i], m_region_up)) {
344  m_child_points[rec_level][n_cp++] = points[i];
345  }
346  }
347  if (n_cp > 0) {
348  stream(m_region_low, m_region_up, m_child_points[rec_level], n_cp, split, cover, rec_level + 1);
349  }
350  m_region_low[split] = d_last;
351  }
352 }
353 
355 
363 void hoy::verify_before_compute(const std::vector<fitness_vector> &points, const fitness_vector &r_point) const
364 {
365  base::assert_minimisation(points, r_point);
366 }
367 
370 {
371  return base_ptr(new hoy(*this));
372 }
373 
375 std::string hoy::get_name() const
376 {
377  return "HOY algorithm";
378 }
379 
380 } } }
381 
382 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::util::hv_algorithm::hoy)
Root PaGMO namespace.
HOY algorithm.
Definition: hoy.h:57
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: hoy.cpp:369
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
double compute(std::vector< fitness_vector > &, const fitness_vector &) const
Compute hypervolume.
Definition: hoy.cpp:46
std::string get_name() const
Algorithm name.
Definition: hoy.cpp:375
boost::shared_ptr< base > base_ptr
Base hypervolume algorithm class.
hoy()
Constructor.
Definition: hoy.cpp:37
void verify_before_compute(const std::vector< fitness_vector > &, const fitness_vector &) const
Verify before compute method.
Definition: hoy.cpp:363