OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
hypervolume.cpp
Go to the documentation of this file.
1 /*
2 
3  This program is free software (software libre); you can redistribute
4  it and/or modify it under the terms of the GNU General Public License
5  as published by the Free Software Foundation; either version 2 of the
6  License, or (at your option) any later version.
7 
8  This program is distributed in the hope that it will be useful, but
9  WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, you can obtain a copy of the GNU
15  General Public License at:
16  http://www.gnu.org/copyleft/gpl.html
17  or by writing to:
18  Free Software Foundation, Inc., 59 Temple Place,
19  Suite 330, Boston, MA 02111-1307 USA
20 
21  ----------------------------------------------------------------------
22 
23 */
24 
25 // To do:
26 // - can we sort less often or reduce/optimise dominance checks?
27 // - should we use FPL's data structure?
28 // - two changes in read.c
29 // - heuristics
30 
31 // hyper_opt: 0 = basic, 1 = sorting, 2 = slicing to 2D, 3 = slicing to 3D
32 #include <stdio.h>
33 #include <stdbool.h>
34 #include <math.h>
35 #include <sys/time.h>
36 #include <sys/resource.h>
37 
38 #include "wfg.h"
39 #include "avl.h"
40 
41 #include <string>
42 
43 #include "hypervolume.h"
44 
45 #define MAXIMISING true
46 
47 #if MAXIMISING
48 #define BEATS(x,y) (x > y)
49 #else
50 #define BEATS(x,y) (x < y)
51 #endif
52 
53 #define WORSE(x,y) (BEATS(y,x) ? (x) : (y))
54 
55 namespace Hypervolume {
56  int n; // the number of objectives
57  POINT ref; // the reference point
58 
59  FRONT *fs; // memory management stuff
60  int fr = 0; // current depth
61  int frmax = -1; // max depth malloced so far (for hyper_opt = 0)
62  int maxm = 0; // identify the biggest fronts in the file
63  int maxn = 0;
64 
65  static avl_tree_t *tree;
66  double hv(FRONT);
67 
68  static int compare_tree_asc( const void *p1, const void *p2)
69  {
70  const double x1= *((const double *)p1+1);
71  const double x2= *((const double *)p2+1);
72 
73  if (x1 != x2) return (x1 > x2) ? -1 : 1;
74  else return 0;
75  }
76 
77 
78  int greater(const void *v1, const void *v2)
79  // this sorts points improving in the last objective
80  {
81  POINT p = *(POINT*)v1;
82  POINT q = *(POINT*)v2;
83 #if hyper_opt == 1
84  for (int i = n - fr - 1; i >= 0; i--) {
85 #else
86  for (int i = n - 1; i >= 0; i--) {
87 #endif
88  if BEATS(p.objectives[i],q.objectives[i]) return 1;
89  else if BEATS(q.objectives[i],p.objectives[i]) return -1;
90  }
91  return 0;
92  }
93 
94 
96  // returns -1 if p dominates q, 1 if q dominates p, 2 if p == q, 0 otherwise
97  {
98  // domination could be checked in either order
99 #if hyper_opt == 1
100  for (int i = n - fr - 1; i >= 0; i--)
101 #else
102  for (int i = n - 1; i >= 0; i--)
103 #endif
104  if BEATS(p.objectives[i],q.objectives[i])
105  {for (int j = i - 1; j >= 0; j--)
106  if BEATS(q.objectives[j],p.objectives[j]) return 0;
107  return -1;}
108  else
109  if BEATS(q.objectives[i],p.objectives[i])
110  {for (int j = i - 1; j >= 0; j--)
111  if BEATS(p.objectives[j],q.objectives[j]) return 0;
112  return 1;}
113  return 2;
114  }
115 
116 
117  void makeDominatedBit(FRONT ps, int p)
118  // creates the front ps[p+1 ..] in fs[fr], with each point bounded by ps[p] and dominated points removed
119  {
120  // when hyper_opt = 0 each new frame is allocated as needed, because the worst-case needs #frames = #points
121 #if hyper_opt == 0
122  if (fr > frmax)
123  {frmax = fr;
124  fs[fr].points = (POINT*) malloc( maxm * sizeof(POINT));
125  for (int j = 0; j < maxm; j++)
126  {
127  fs[fr].points[j].objectives = (OBJECTIVE*) malloc(maxn * sizeof(OBJECTIVE));
128  }
129  }
130 #endif
131 
132  int z = ps.nPoints - 1 - p;
133  for (int i = 0; i < z; i++)
134  for (int j = 0; j < n; j++)
135  fs[fr].points[i].objectives[j] = WORSE(ps.points[p].objectives[j],ps.points[p + 1 + i].objectives[j]);
136  POINT t;
137  fs[fr].nPoints = 1;
138  for (int i = 1; i < z; i++)
139  {int j = 0;
140  bool keep = true;
141  while (j < fs[fr].nPoints && keep)
142  switch (dominates2way(fs[fr].points[i], fs[fr].points[j]))
143  {case -1: t = fs[fr].points[j];
144  fs[fr].nPoints--;
145  fs[fr].points[j] = fs[fr].points[fs[fr].nPoints];
146  fs[fr].points[fs[fr].nPoints] = t;
147  break;
148  case 0: j++; break;
149  // case 2: printf("Identical points!\n");
150  default: keep = false;
151  }
152  if (keep) {t = fs[fr].points[fs[fr].nPoints];
153  fs[fr].points[fs[fr].nPoints] = fs[fr].points[i];
154  fs[fr].points[i] = t;
155  fs[fr].nPoints++;}
156  }
157  fr++;
158  }
159 
160 
161  double hv2(FRONT ps)
162  // returns the hypervolume of ps[0 ..] in 2D
163  // assumes that ps is sorted improving
164  {
165  double volume = fabs((ps.points[0].objectives[0] - ref.objectives[0]) *
166  (ps.points[0].objectives[1] - ref.objectives[1]));
167  for (int i = 1; i < ps.nPoints; i++)
168  volume += fabs((ps.points[i].objectives[0] - ref.objectives[0]) *
169  (ps.points[i].objectives[1] - ps.points[i - 1].objectives[1]));
170  return volume;
171  }
172 
173  double hv3_AVL(FRONT ps)
174  /* hv3_AVL: 3D algorithm code taken from version hv-1.2 available at
175  http://iridia.ulb.ac.be/~manuel/hypervolume and proposed by:
176 
177  Carlos M. Fonseca, Luís Paquete, and Manuel López-Ibáñez. An improved
178  dimension-sweep algorithm for the hypervolume indicator. In IEEE
179  Congress on Evolutionary Computation, pages 1157-1163, Vancouver,
180  Canada, July 2006.
181 
182  Copyright (c) 2009
183  Carlos M. Fonseca <cmfonsec@ualg.pt>
184  Manuel Lopez-Ibanez <manuel.lopez-ibanez@ulb.ac.be>
185  Luis Paquete <paquete@dei.uc.pt>
186  */
187 
188  // returns the hypervolume of ps[0 ..] in 3D
189  // assumes that ps is sorted improving
190  {
192  avl_insert_top(tree,ps.points[ps.nPoints-1].tnode);
193 
194  double hypera = (ref.objectives[0] - ps.points[ps.nPoints-1].objectives[0]) *
195  (ref.objectives[1] - ps.points[ps.nPoints-1].objectives[1]);
196 
197  double height;
198  if (ps.nPoints == 1)
199  height = ref.objectives[2] - ps.points[ps.nPoints-1].objectives[2];
200  else
201  height = ps.points[ps.nPoints-2].objectives[2] - ps.points[ps.nPoints-1].objectives[2];
202 
203  double hyperv = hypera * height;
204 
205  for (int i = ps.nPoints - 2; i >= 0; i--) {
206 
207  if (i == 0)
208  height = ref.objectives[2] - ps.points[i].objectives[2];
209  else
210  height = ps.points[i-1].objectives[2] - ps.points[i].objectives[2];
211 
212  // search tree for point q to the right of current point
213  const double * prv_ip, * nxt_ip;
214  avl_node_t *tnode;
215 
217 
218  if (avl_search_closest(tree, ps.points[i].objectives, &tnode) <= 0) {
219  nxt_ip = (double *)(tnode->item);
220  tnode = tnode->prev;
221  } else {
222  nxt_ip = (tnode->next!=NULL)
223  ? (double *)(tnode->next->item)
224  : ref.objectives;
225  }
226  // if p is not dominated
227  if (nxt_ip[0] > ps.points[i].objectives[0]) {
228 
229  // insert p in tree
230  avl_insert_after(tree, tnode, ps.points[i].tnode);
231 
232  if (tnode !=NULL) {
233  prv_ip = (double *)(tnode->item);
234 
235  if (prv_ip[0] > ps.points[i].objectives[0]) {
236  const double * cur_ip;
237 
238  tnode = ps.points[i].tnode->prev;
239  // cur_ip = point dominated by pp with highest [0]-coordinate
240  cur_ip = (double *)(tnode->item);
241 
242  // for each point in s in tree dominated by p
243  while (tnode->prev) {
244  prv_ip = (double *)(tnode->prev->item);
245  // decrease area by contribution of s
246  hypera -= (prv_ip[1] - cur_ip[1])*(nxt_ip[0] - cur_ip[0]);
247  if (prv_ip[0] < ps.points[i].objectives[0])
248  break; // prv is not dominated by pp
249  cur_ip = prv_ip;
250  // remove s from tree
251  avl_unlink_node(tree,tnode);
252  tnode = tnode->prev;
253  }
254 
255  // remove s from tree
256  avl_unlink_node(tree,tnode);
257 
258  if (!tnode->prev) {
259  // decrease area by contribution of s
260  hypera -= (ref.objectives[1] - cur_ip[1])*(nxt_ip[0] - cur_ip[0]);
261  prv_ip = ref.objectives;
262  }
263  }
264  } else
265  prv_ip = ref.objectives;
266 
267  // increase area by contribution of p
268  hypera += (prv_ip[1] -
269  ps.points[i].objectives[1])*(nxt_ip[0] -
270  ps.points[i].objectives[0]);
271 
272  }
273 
274  if (height > 0)
275  hyperv += hypera * height;
276  }
277  avl_clear_tree(tree);
278  return hyperv;
279  }
280 
281 
282  double inclhv(POINT p)
283  // returns the inclusive hypervolume of p
284  {
285  double volume = 1;
286  for (int i = 0; i < n; i++)
287  volume *= fabs(p.objectives[i] - ref.objectives[i]);
288  return volume;
289  }
290 
291 
292  double exclhv(FRONT ps, int p)
293  // returns the exclusive hypervolume of ps[p] relative to ps[p+1 ..]
294  {
295  double volume = inclhv(ps.points[p]);
296  if (ps.nPoints > p + 1)
297  {
298  makeDominatedBit(ps, p);
299  volume -= hv(fs[fr - 1]);
300  fr--;
301  }
302  return volume;
303  }
304 
305 
306  double hv(FRONT ps)
307  // returns the hypervolume of ps[0 ..]
308  {
309 #if hyper_opt > 0
310  qsort(ps.points, ps.nPoints, sizeof(POINT), greater);
311 #endif
312 
313 #if hyper_opt == 2
314  if (n == 2) return hv2(ps);
315 #elif hyper_opt == 3
316  if (n == 3) return hv3_AVL(ps);
317 #endif
318 
319  double volume = 0;
320 
321 #if hyper_opt <= 1
322  for (int i = 0; i < ps.nPoints; i++) volume += exclhv(ps, i);
323 #else
324  n--;
325  for (int i = ps.nPoints - 1; i >= 0; i--)
326  // we can ditch dominated points here,
327  // but they will be ditched anyway in dominatedBit
328  volume += fabs(ps.points[i].objectives[n] - ref.objectives[n]) * exclhv(ps, i);
329 
330  n++;
331 #endif
332 
333  return volume;
334  }
335 
336  double FromFile(std::string file, const std::vector<double>& referencePoint)
337  // processes each front from the file
338  {
339  FILECONTENTS *f = readFile(file.c_str());
340 
341  // find the biggest fronts
342  for (int i = 0; i < f->nFronts; i++)
343  {
344  if (f->fronts[i].nPoints > maxm) maxm = f->fronts[i].nPoints;
345  if (f->fronts[i].n > maxn) maxn = f->fronts[i].n;
346  }
347 
348  // allocate memory
349 #if hyper_opt == 0
350  fs = (FRONT*) malloc(sizeof(FRONT) * maxm);
351 #else
352 
353  // slicing (hyper_opt > 1) saves a level of recursion
354  int maxd = maxn - (hyper_opt / 2 + 1);
355  fs = (FRONT*) malloc(sizeof(FRONT) * maxd);
356 
357  // 3D base (hyper_opt = 3) needs space for the sentinels
358  int maxp = maxm + 2 * (hyper_opt / 3);
359  //int maxp = 100000;
360  for (int i = 0; i < maxd; i++)
361  {fs[i].points = (POINT*)malloc(sizeof(POINT) * maxp);
362  for (int j = 0; j < maxp; j++)
363  {
364  fs[i].points[j].tnode = (avl_node_t*)malloc(sizeof(avl_node_t));
365  // slicing (hyper_opt > 1) saves one extra objective at each level
366  fs[i].points[j].objectives = (OBJECTIVE*)malloc(sizeof(OBJECTIVE) * (maxn - (i + 1) * (hyper_opt / 2)));
367  }
368  }
369 #endif
370 
371  tree = avl_alloc_tree ((avl_compare_t) compare_tree_asc,
372  (avl_freeitem_t) free);
373 
374  // initialise the reference point
375  ref.objectives = (OBJECTIVE*) malloc(sizeof(OBJECTIVE) * maxn);
376  ref.tnode = (avl_node_t*) malloc(sizeof(avl_node_t));
377 
378  // initialise to zero (origin)
379  for (int i = 0; i < maxn; i++) ref.objectives[i] = 0;
380 
381  if (referencePoint.empty()) {
382  printf("No reference point provided: using the origin\n");
383  } else if ((int)referencePoint.size() != maxn) {
384  printf("Your reference point should have %d values: using the origin\n", maxn);
385  } else {
386  for (int i = 0; i < maxn; i++) ref.objectives[i] = referencePoint[i];
387  }
388 
389  for (int i = 0; i < f->nFronts; i++)
390  {
391  struct timeval tv1, tv2;
392  struct rusage ru_before, ru_after;
393  getrusage (RUSAGE_SELF, &ru_before);
394 
395  n = f->fronts[i].n;
396 #if hyper_opt >= 3
397  if (n == 2)
398  {qsort(f->fronts[i].points, f->fronts[i].nPoints, sizeof(POINT), greater);
399  //printf("hv(%d) = %1.10f\n", i+1, hv2(f->fronts[i]));
400  return hv2(f->fronts[i]);
401  }
402  else
403 #endif
404  //printf("hv(%d) = %1.10f\n", i+1, hv(f->fronts[i]));
405  return hv(f->fronts[i]);
406 
407  getrusage (RUSAGE_SELF, &ru_after);
408  tv1 = ru_before.ru_utime;
409  tv2 = ru_after.ru_utime;
410  printf("Time: %f (s)\n", tv2.tv_sec + tv2.tv_usec * 1e-6 - tv1.tv_sec - tv1.tv_usec * 1e-6);
411  }
412 
413  return 0;
414  }
415 }
416 
417 #undef MAXIMISING
418 #undef BEATS
419 #undef WORSE
avl_tree_t * avl_alloc_tree(avl_compare_t cmp, avl_freeitem_t freeitem)
Definition: avl.cpp:189
constexpr double e
The value of .
Definition: Physics.h:40
Definition: wfg.h:16
struct avl_node_t * prev
Definition: avl.h:54
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:815
int nPoints
Definition: wfg.h:18
Definition: avl.h:52
FRONT * fs
Definition: hypervolume.cpp:59
int nFronts
Definition: wfg.h:25
double hv2(FRONT ps)
struct avl_node_t * tnode
Definition: wfg.h:13
POINT * points
Definition: wfg.h:20
int avl_search_closest(const avl_tree_t *avltree, const void *item, avl_node_t **avlnode)
Definition: avl.cpp:134
int n
Definition: wfg.h:19
void avl_unlink_node(avl_tree_t *avltree, avl_node_t *avlnode)
Definition: avl.cpp:340
avl_node_t * avl_insert_after(avl_tree_t *avltree, avl_node_t *node, avl_node_t *newnode)
Definition: avl.cpp:274
int(* avl_compare_t)(const void *, const void *)
Definition: avl.h:45
OBJECTIVE * objectives
Definition: wfg.h:12
FILECONTENTS * readFile(const char filename[])
Definition: read.cpp:40
FRONT * fronts
Definition: wfg.h:26
#define WORSE(x, y)
Definition: hypervolume.cpp:53
double inclhv(POINT p)
#define hyper_opt
Definition: hypervolume.h:38
Definition: wfg.h:10
double hv3_AVL(FRONT ps)
void makeDominatedBit(FRONT ps, int p)
void(* avl_freeitem_t)(void *)
Definition: avl.h:50
avl_node_t * avl_insert_top(avl_tree_t *avltree, avl_node_t *newnode)
Definition: avl.cpp:241
double hv(FRONT)
void * item
Definition: avl.h:58
double FromFile(std::string file, const std::vector< double > &referencePoint)
struct avl_node_t * next
Definition: avl.h:53
double exclhv(FRONT ps, int p)
int dominates2way(POINT p, POINT q)
Definition: hypervolume.cpp:95
void avl_clear_tree(avl_tree_t *avltree)
Definition: avl.cpp:193
int greater(const void *v1, const void *v2)
Definition: hypervolume.cpp:78
double OBJECTIVE
Definition: wfg.h:8
#define BEATS(x, y)
Definition: hypervolume.cpp:50
avl_node_t * avl_init_node(avl_node_t *newnode, void *item)
Definition: avl.cpp:233
Definition: avl.h:67