OPAL (Object Oriented Parallel Accelerator Library) 2022.1
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
55namespace 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
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:732
FILECONTENTS * readFile(const char filename[])
Definition: read.cpp:40
#define hyper_opt
Definition: hypervolume.h:38
avl_node_t * avl_insert_after(avl_tree_t *avltree, avl_node_t *node, avl_node_t *newnode)
Definition: avl.cpp:274
int avl_search_closest(const avl_tree_t *avltree, const void *item, avl_node_t **avlnode)
Definition: avl.cpp:134
void avl_unlink_node(avl_tree_t *avltree, avl_node_t *avlnode)
Definition: avl.cpp:340
avl_node_t * avl_init_node(avl_node_t *newnode, void *item)
Definition: avl.cpp:233
avl_node_t * avl_insert_top(avl_tree_t *avltree, avl_node_t *newnode)
Definition: avl.cpp:241
avl_tree_t * avl_alloc_tree(avl_compare_t cmp, avl_freeitem_t freeitem)
Definition: avl.cpp:189
void avl_clear_tree(avl_tree_t *avltree)
Definition: avl.cpp:193
void(* avl_freeitem_t)(void *)
Definition: avl.h:50
int(* avl_compare_t)(const void *, const void *)
Definition: avl.h:45
#define BEATS(x, y)
Definition: hypervolume.cpp:50
#define WORSE(x, y)
Definition: hypervolume.cpp:53
double OBJECTIVE
Definition: wfg.h:8
constexpr double e
The value of.
Definition: Physics.h:39
int dominates2way(POINT p, POINT q)
Definition: hypervolume.cpp:95
double inclhv(POINT p)
double hv(FRONT)
int greater(const void *v1, const void *v2)
Definition: hypervolume.cpp:78
double hv3_AVL(FRONT ps)
FRONT * fs
Definition: hypervolume.cpp:59
double FromFile(std::string file, const std::vector< double > &referencePoint)
void makeDominatedBit(FRONT ps, int p)
double exclhv(FRONT ps, int p)
double hv2(FRONT ps)
Definition: avl.h:52
struct avl_node_t * prev
Definition: avl.h:54
struct avl_node_t * next
Definition: avl.h:53
void * item
Definition: avl.h:58
Definition: avl.h:67
Definition: wfg.h:11
struct avl_node_t * tnode
Definition: wfg.h:13
OBJECTIVE * objectives
Definition: wfg.h:12
Definition: wfg.h:17
int n
Definition: wfg.h:19
POINT * points
Definition: wfg.h:20
int nPoints
Definition: wfg.h:18
int nFronts
Definition: wfg.h:25
FRONT * fronts
Definition: wfg.h:26