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