PaGMO  1.1.5
hv4d_cpp_original/hv.c
1 /*************************************************************************
2 
3  hv: main program
4 
5  ---------------------------------------------------------------------
6 
7  Copyright (c) 2011
8  Andreia P. Guerreiro <apg@dei.uc.pt>
9  Carlos M. Fonseca <cmfonsec@dei.uc.pt>
10  Michael T. M. Emmerich <emmerich@liacs.nl>
11 
12  This program is free software (software libre); you can redistribute
13  it and/or modify it under the terms of the GNU General Public License
14  as published by the Free Software Foundation; either version 2 of the
15  License, or (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful, but
18  WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, you can obtain a copy of the GNU
24  General Public License at:
25  http://www.gnu.org/copyleft/gpl.html
26  or by writing to:
27  Free Software Foundation, Inc., 59 Temple Place,
28  Suite 330, Boston, MA 02111-1307 USA
29 
30 
31 *************************************************************************/
32 
33 /*************************************************************************
34 
35  References:
36 
37  ---------------------------------------------------------------------
38  [1] Andreia P. Guerreiro. Efficient algorithms for the assessment
39  of stochastic multiobjective optimizers. Master’s thesis, IST,
40  Technical University of Lisbon, Portugal, 2011.
41 
42  [2] Andreia P. Guerreiro, Carlos M. Fonseca, and Michael T. M. Emmerich.
43  A fast dimension-sweep algorithm for the hypervolume indicator in four
44  dimensions. In CCCG, pages 77–82, 2012.
45 
46 
47 *************************************************************************/
48 
49 
50 
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <limits.h>
54 #include <float.h>
55 
56 
57 #if __GNUC__ >= 3
58 # define __hv_unused __attribute__ ((unused))
59 #else
60 # define __hv_unused /* no 'unused' attribute available */
61 #endif
62 
63 
64 
65 typedef struct dlnode {
66  double x[4]; /* The data vector */
67  struct dlnode *nexty;
68  struct dlnode *prevy;
69  struct dlnode *nextz;
70  struct dlnode *prevz;
71  double xrightbelow; //x value of the closest point (according to x) with lower y and z values
72 } dlnode_t;
73 
74 
75 static int compare_node(const void *p1, const void* p2)
76 {
77  const double x1 = *((*(const dlnode_t **)p1)->x+3);
78  const double x2 = *((*(const dlnode_t **)p2)->x+3);
79 
80  return (x1 < x2) ? -1 : (x1 > x2) ? 1 : 0;
81 }
82 
83 /*
84  * Setup circular double-linked list in each dimension
85  */
86 
87 
88 static dlnode_t *
89 setup_cdllist(double *data, int n, const double *ref)
90 {
91  int d = 4;
92  dlnode_t *head;
93  dlnode_t **scratch;
94  int i, j;
95  double * data2;
96 
97  head = malloc ((n+1) * sizeof(dlnode_t));
98 
99 
100 
101  for (i = 1; i <= n; i++) {
102  for(j = 0; j < d; j++)
103  head[i-1].x[j] = data[(i-1)*d + j];
104 
105  }
106 
107  head[n].x[0] = head[n].x[1] = head[n].x[2] = -DBL_MAX;
108  head[n].x[3] = ref[3];
109 
110 
111  scratch = malloc(n * sizeof(dlnode_t*));
112 
113  for (i = 0; i < n; i++){
114  scratch[i] = head + i;
115  }
116 
117  qsort(scratch, n, sizeof(dlnode_t*), compare_node);
118 
119  data2 = (double *) malloc (n * 4 * sizeof(double));
120  for(i = 0; i < n; i++){
121  for(j = 0; j < d; j++){
122  data2[i*d + j] = scratch[i]->x[j];
123  }
124  }
125 
126 
127  for(i = 0; i < n; i++){
128  for(j = 0; j < d; j++)
129  head[i].x[j] = data2[i*d + j];
130  }
131 
132  free(data2);
133 
134  free(scratch);
135 
136  return head;
137 }
138 
139 static void free_cdllist(dlnode_t * head)
140 {
141  free(head);
142 }
143 
144 static void free_sentinels(dlnode_t * list){
145 
146  free(list->nexty);
147  free(list->prevy);
148 
149  free(list->nextz);
150  free(list->prevz);
151 
152 }
153 
154 //box
155 typedef struct bx{
156  double lx, ly, lz;
157  double ux, uy, uz;
158 
159  struct bx * next;
160  struct bx * prev;
161 }box;
162 
163 //box list
164 typedef struct bxl{
165  box * head;
166  box * tail;
167 }boxlist;
168 
169 //note: it is not necessary to define uz
170 static box * new_box(double lx, double ly, double lz, double ux, double uy, double uz){
171  box * b = (box *)malloc(sizeof(box));
172 
173  b->lx = lx;
174  b->ly = ly;
175  b->lz = lz;
176 
177  b->ux = ux;
178  b->uy = uy;
179  b->uz = uz;
180 
181  return b;
182 }
183 
184 //push_left == push_head
185 static void push_left(boxlist * bl, box * b){
186  b->next = bl->head;
187  bl->head->prev = b;
188  b->prev = NULL;
189 
190  bl->head = b;
191 }
192 
193 
194 //may be empty
195 static void push_left_e(boxlist * bl, box * b){
196  if(bl->head == NULL){
197  bl->head = b;
198  bl->tail = b;
199 
200  b->next = NULL;
201  b->prev = NULL;
202 
203  }else{
204 
205  b->next = bl->head;
206  bl->head->prev = b;
207  b->prev = NULL;
208 
209  bl->head = b;
210  }
211 }
212 
213 
214 //box b is freed and its volume is returned
215 static double close_box(box *b){
216  double volume = (b->ux - b->lx) * (b->uy - b->ly) * (b->uz - b->lz);
217  free(b);
218 
219  return volume;
220 }
221 
222 static double shrink_box(box *b, double ux){
223  double volume = (b->ux - ux) * (b->uy - b->ly) * (b->uz - b->lz);
224  b->ux = ux;
225  return volume;
226 }
227 
228 static double close_boxes_right(boxlist *bl, double x, double uz){
229  double volume = 0;
230  box * b = bl->tail;
231  while(b->lx > x){
232  bl->tail = b->prev;
233  bl->tail->next = NULL;
234  b->uz = uz;
235  volume += close_box(b);
236  b = bl->tail;
237  }
238  //b = bl->tail;
239  if(x < b->ux){
240  b->uz = uz;
241  volume += shrink_box(b, x);
242 
243  }
244 
245  return volume;
246 
247 }
248 
249 static double close_boxes_left(boxlist *bl, double y, double uz){
250  double volume;
251  box * b;
252 
253  double lastlx;
254  double lx;
255  double ly;
256  double lz;
257 
258  if(y >= bl->head->uy)
259  return 0;
260 
261  volume = 0;
262  b = bl->head;
263 
264  lastlx = b->ux;
265  lx = b->lx;
266  ly = b->ly;
267  lz = b->lz;
268 
269  while(b != NULL && b->uy >= y){
270 
271  lastlx = b->ux;
272  b->uz = uz;
273  b->ly = y;
274  bl->head = b->next;
275 
276  volume += close_box(b);
277  b = bl->head;
278  }
279 
280  if(bl->head != NULL)
281  bl->head->prev = NULL;
282  else
283  bl->tail = NULL;
284 
285  b = new_box(lx, ly, lz, lastlx, y, 0);
286  push_left_e(bl, b);
287 
288  return volume;
289 
290 }
291 
292 
293 static double close_all_boxes(boxlist *bl, double uz){
294  double volume = 0;
295  box * b = bl->head;
296  while(b != NULL){
297  b->uz = uz;
298  bl->head = b->next;
299  volume += close_box(b);
300  b = bl->head;
301  }
302 
303  bl->head = bl->tail = NULL;
304  return volume;
305 }
306 
307 
308 
309 static void insert_after_y(dlnode_t *new, dlnode_t *prev){
310 
311  new->prevy = prev;
312  new->nexty = prev->nexty;
313  new->nexty->prevy = new;
314  prev->nexty = new;
315 
316 }
317 
318 static void insert_after_z(dlnode_t *new, dlnode_t *prev){
319 
320  new->prevz = prev;
321  new->nextz = prev->nextz;
322  new->nextz->prevz = new;
323  prev->nextz = new;
324 
325 }
326 
327 
328 static void remove_y(dlnode_t *p){
329 
330  p->prevy->nexty = p->nexty;
331  p->nexty->prevy = p->prevy;
332 
333 }
334 
335 static void remove_z(dlnode_t *p){
336 
337  p->prevz->nextz = p->nextz;
338  p->nextz->prevz = p->prevz;
339 
340 }
341 
342 
343 static boxlist * init_box_list(dlnode_t *p, dlnode_t * ynext){
344 
345 
346  double previous_x = p->xrightbelow;
347 
348  dlnode_t * q = ynext; // y is swept in ascending order
349 
350  boxlist * bl = (boxlist *)malloc(sizeof(boxlist));
351  box * b = (box *)malloc(sizeof(box));
352 
353  b->prev = b->next = NULL;
354  bl->head = bl->tail = b; //box b (sentinel) will be removed later
355 
356  //box limits in x and y are defined (vertical boxes)
357  while(q->x[0] > p->x[0] || q->x[2] > p->x[2]){
358 
359  // =
360  if(q->x[2] <= p->x[2] && q->x[0] < previous_x && q->x[0] > p->x[0]){
361  b = new_box(q->x[0], p->x[1], p->x[2], previous_x, q->x[1], 0);
362  push_left(bl, b);
363 
364  previous_x = q->x[0];
365  }
366 
367  q = q->nexty;
368  }
369 
370  b = new_box(p->x[0], p->x[1], p->x[2], previous_x, q->x[1], 0);
371  push_left(bl, b);
372 
373 
374  //the box created in the beginning is removed
375  bl->tail = bl->tail->prev;
376  free(bl->tail->next);
377  bl->tail->next = NULL;
378 
379 
380  return bl;
381 
382 }
383 
384 
385 static double update_boxes(boxlist * bl, dlnode_t *p, dlnode_t * znext){
386 
387  double volume = 0;
388  dlnode_t * q;
389 
390  q = znext;
391 
392  while(bl->head != NULL){
393  if(q->x[0] <= p->x[0]){
394 
395  if(q->x[1] <= p->x[1]){ // p is dominated in x and y
396 
397  //close all boxes
398  //update volume
399  //last iteration
400  volume += close_all_boxes(bl, q->x[2]);
401  }else{
402 
403  //update z
404  //close boxes in the left
405  //update volume
406  volume += close_boxes_left(bl, q->x[1], q->x[2]);
407  }
408 
409  if(q->x[1] >= p->x[1] && p->x[0] < q->xrightbelow){
410  q->xrightbelow = p->x[0];
411  }
412 
413 
414  }else{ //since q does not dominate p and p does not dominate q, q has to
415  //to be to the right of p and below p
416 
417 
418  //update z
419  //closes boxes in the right
420  //update volume
421  volume += close_boxes_right(bl, q->x[0], q->x[2]);
422 
423  }
424 
425  q = q->nextz;
426 
427  }
428 
429  free(bl);
430 
431  return volume;
432 
433 }
434 
435 
436 //corresponds to HV4D - contribution function of the pseudocode
437 static double
438 hv_increment3DA(dlnode_t *p, dlnode_t * ynext, dlnode_t *znext){
439 
440  boxlist * bl = init_box_list(p, ynext);
441 // if(bl == NULL)
442 // return 0;
443 
444  return update_boxes(bl, p, znext);
445 }
446 
447 
448 
449 //inner cycle (sweeps points according to coordinate z)
450 //note: returns the contribution of p only
451 //Function responsible for keeping points sorted according to coordinates y and z,
452 //for updating xrightbelow and for removing dominated points
453 static double
454 hv_increment3D(dlnode_t *list, dlnode_t *p, const double * ref){
455  double loss = 0, gain;
456  dlnode_t * q = list->prevz;
457  dlnode_t * yprev = list->nexty;
458  double xrightbelow = ref[0];
459  dlnode_t * zprev = list->nextz;
460  dlnode_t * head3 = list->nextz;
461 
462  while(q != head3){
463 
464  if(q->x[2] >= p->x[2] && q->x[1] >= p->x[1] && q->x[0] >= p->x[0]){
465  remove_y(q);
466  remove_z(q);
467  loss += hv_increment3DA(q, q->nexty, q->nextz);
468 
469  }else{
470 
471  if(q->x[2] > zprev->x[2] && (q->x[2] < p->x[2] || (q->x[2] == p->x[2] && q->x[1] <= p->x[1]))){
472  //because of xrightbelow, in order to avoid height 0 boxes (in spite of not causing wrong results)
473  zprev = q;
474  }
475 
476  if(q->x[1] > yprev->x[1] && (q->x[1] < p->x[1] || (q->x[1] == p->x[1] && q->x[2] <= p->x[2]))){ //testequal.4d.6
477  yprev = q;
478  }
479 
480  if(q->x[2] <= p->x[2] && q->x[1] <= p->x[1]){
481 
482  if(q->x[0] <= p->x[0]){
483  return 0;
484  }else if(q->x[0] < xrightbelow){ //q->x[0] > p->x[0]
485  xrightbelow = q->x[0];
486  }
487  }
488  }
489  q = q->prevz;
490  }
491 
492 
493  p->xrightbelow = xrightbelow;
494 
495 
496  gain = hv_increment3DA(p, yprev->nexty, zprev->nextz);
497 
498  //add p to the lists of coordinates two and three
499  insert_after_z(p, zprev);
500  insert_after_y(p, yprev);
501 
502  return gain - loss;
503 }
504 
505 
506 //outer cycle (sweeps list L_4)
507 static double
508 hv(dlnode_t *list, int c, const double * ref)
509 {
510 
511  dlnode_t *p = list;
512  double hyperv = 0; //hvol4D
513  double hypera = 0; //hvol3D
514 
515  int i;
516  for (i = 0; i < c; i++) {
517 
518  hypera = hypera + hv_increment3D(list+c, p, ref);
519  hyperv = hyperv + hypera * ((p+1)->x[3] - p->x[3]);
520  p++;
521  }
522 
523  return hyperv;
524 }
525 
526 
527 
528 
529 
530 
531 static void add_sentinels(dlnode_t * list, const double * ref){
532 
533  int i;
534 
535  for(i = 0; i <= 1; i++){
536  dlnode_t * sentineli, * sentinelf;
537  sentineli = (dlnode_t *) malloc(sizeof(dlnode_t));
538  sentinelf = (dlnode_t *) malloc(sizeof(dlnode_t));
539 
540  sentineli->xrightbelow = -DBL_MAX;
541  sentinelf->xrightbelow = -DBL_MAX;
542 
543 
544  if(i == 0){
545  sentineli->nexty = sentinelf;
546  sentinelf->prevy = sentineli;
547  list->nexty = sentineli;
548  sentineli->prevy = list;
549  list->prevy = sentinelf;
550  sentinelf->nexty = list;
551 
552 
553  sentineli->x[0] = ref[0];
554  sentineli->x[1] = -DBL_MAX;
555  sentineli->x[2] = -DBL_MAX;
556 
557  sentinelf->x[0] = -DBL_MAX;
558  sentinelf->x[1] = ref[1];
559  sentinelf->x[2] = -DBL_MAX;
560  }else{
561  sentineli->nextz = sentinelf;
562  sentinelf->prevz = sentineli;
563  list->nextz = sentineli;
564  sentineli->prevz = list;
565  list->prevz = sentinelf;
566  sentinelf->nextz = list;
567 
568  sentineli->x[0] = DBL_MAX;
569  sentineli->x[1] = DBL_MAX;
570  sentineli->x[2] = -DBL_MAX;
571 
572  sentinelf->x[0] = -DBL_MAX;
573  sentinelf->x[1] = -DBL_MAX;
574  sentinelf->x[2] = ref[2];
575 
576  }
577 
578  sentineli->x[3] = DBL_MAX;
579  sentinelf->x[3] = DBL_MAX;
580 
581 
582  }
583 
584 }
585 
586 
587 double guerreiro_hv4d(double *data, int n, const double *ref)
588 {
589  dlnode_t *list;
590  double hyperv;
591 
592  if (n == 0) {
593  /* Returning here would leak memory. */
594  hyperv = 0.0;
595  } else {
596 
597  list = setup_cdllist(data, n, ref);
598 
599  add_sentinels(list+n, ref);
600 
601  hyperv = hv(list, n, ref);
602 
603  free_sentinels(list+n);
604  /* Clean up. */
605  free_cdllist (list);
606 
607 
608  }
609 
610  return hyperv;
611 }
612