Skip to content

Commit bc62b59

Browse files
committed
* c/nondominated.h (find_nondominated_set_3d_helper): Rename as
find_nondominated_3d_impl. (find_nondominated_3d_impl_sorted): New. Assumes input is sorted. Simplify implementation by removing nondom and setting rows to NULL instead. * c/nondominated_kung.h (find_nondominated_set_3d_helper): Copy that keeps the previous behavior.
1 parent 801a193 commit bc62b59

2 files changed

Lines changed: 154 additions & 26 deletions

File tree

c/nondominated.h

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -339,18 +339,16 @@ find_nondominated_set_2d_(const double * restrict points, size_t size,
339339
D. Jesus, Luís Paquete, A software library for archiving nondominated
340340
points, GECCO 2021. https://github.com/TLDart/nondLib/blob/main/nondlib.hpp
341341
342-
rows will be sorted by cmp_ppdouble_asc_rev_3d().
343-
max_dim will be != 3 only if the rows pointers have been shifted.
342+
rows should be already sorted by cmp_ppdouble_asc_rev_3d().
343+
344+
When find_one_dominated, return as soon as it finds one dominated point.
344345
*/
345-
static inline size_t
346-
find_nondominated_set_3d_helper(const double * restrict points,
347-
const double ** rows,
348-
size_t size, dimension_t max_dim,
349-
const bool keep_weakly, boolvec * restrict nondom)
346+
static __force_inline__ size_t
347+
find_nondominated_3d_impl_sorted(const double ** restrict rows, size_t size,
348+
const bool keep_weakly,
349+
const bool find_one_dominated)
350350
{
351351
ASSUME(size > 1);
352-
// Sort in ascending lexicographic order from the last dimension.
353-
qsort_typesafe(rows, size, cmp_ppdouble_asc_rev_3d);
354352

355353
avl_tree_t tree;
356354
avl_init_tree(&tree, qsort_cmp_pdouble_asc_x_nonzero);
@@ -364,11 +362,12 @@ find_nondominated_set_3d_helper(const double * restrict points,
364362
avl_insert_after(&tree, node - 1, node);
365363

366364
// In this context, size means "no dominated solution found".
367-
size_t new_size = size, j = 1;
365+
size_t new_size = size, j = 1, k = 0;
368366
const double * last_dom = NULL;
369367
const double * restrict pk = rows[0];
370368
do {
371369
bool dominated;
370+
size_t pos_dom = j;
372371
const double * restrict pj = rows[j];
373372
DEBUG2(printf_point("pj = [ ", pj, 3, " ], "));
374373
if (pk[0] > pj[0] || pk[1] > pj[1]) {
@@ -418,8 +417,10 @@ find_nondominated_set_3d_helper(const double * restrict points,
418417
const bool k_eq_j = (pk[0] == pj[0]) & (pk[1] == pj[1]) & (pk[2] == pj[2]);
419418
if (!keep_weakly) { // We don't keep duplicates;
420419
dominated = true;
421-
if (unlikely(k_eq_j) && pj < pk) // Only the first duplicated point is kept.
422-
SWAP(pk, pj);
420+
if (unlikely(k_eq_j) && pj < pk) { // Only the first duplicated point is kept.
421+
pos_dom = k;
422+
pj = pk;
423+
}
423424
} else { // or it is not a duplicate, so it is non-weakly dominated;
424425
dominated = likely(!k_eq_j)
425426
// or pk was dominated, then this one is also dominated.
@@ -428,19 +429,17 @@ find_nondominated_set_3d_helper(const double * restrict points,
428429
DEBUG2(printf_point("weakly dominated by pk: ", pk, 3, "\n"));
429430
}
430431
if (dominated) { // pj is dominated by a point in the tree or by prev.
431-
/* Map the order in rows[], which is sorted, to the original order
432-
in points. */
433-
size_t pos_last_dom = row_index_from_ptr(points, pj, max_dim);
434-
if (unlikely(nondom == NULL)) {
432+
if (find_one_dominated) {
435433
// In this context, it means "position of the first dominated solution found".
436-
new_size = pos_last_dom;
434+
new_size = pos_dom;
437435
goto early_end;
438436
}
439-
nondom[pos_last_dom] = false;
440437
last_dom = pj;
438+
rows[pos_dom] = NULL;
441439
new_size--;
442440
} else {
443441
pk = pj;
442+
k = j;
444443
}
445444
j++;
446445
} while (j < size);
@@ -450,13 +449,26 @@ find_nondominated_set_3d_helper(const double * restrict points,
450449
return new_size;
451450
}
452451

452+
static __force_inline__ size_t
453+
find_nondominated_3d_impl(const double ** restrict rows, size_t size,
454+
const bool keep_weakly,
455+
const bool find_one_dominated)
456+
{
457+
// Sort in ascending lexicographic order from the last dimension.
458+
qsort_typesafe(rows, size, cmp_ppdouble_asc_rev_3d);
459+
// Help GCC generate all possible specializations of this function.
460+
return keep_weakly
461+
? find_nondominated_3d_impl_sorted(rows, size, true, find_one_dominated)
462+
: find_nondominated_3d_impl_sorted(rows, size, false, find_one_dominated);
463+
}
464+
453465
static inline size_t
454466
find_dominated_3d_(const double * restrict points, size_t size, bool keep_weakly)
455467
{
456468
const double ** rows = generate_row_pointers(points, size, 3);
457-
size_t pos = keep_weakly
458-
? find_nondominated_set_3d_helper(points, rows, size, 3, true, /* nondom=*/NULL)
459-
: find_nondominated_set_3d_helper(points, rows, size, 3, false, /* nondom=*/NULL);
469+
size_t pos = find_nondominated_3d_impl(rows, size, keep_weakly, /* find_one_dominated=*/true);
470+
if (pos < size)
471+
pos = row_index_from_ptr(points, rows[pos], 3);
460472
free(rows);
461473
return pos;
462474
}
@@ -469,11 +481,20 @@ static inline size_t
469481
find_nondominated_set_3d_(const double * restrict points, size_t size,
470482
const bool keep_weakly, boolvec * restrict nondom)
471483
{
472-
const double ** rows = generate_row_pointers(points, size, 3);
473484
ASSUME(nondom != NULL);
474-
size_t new_size = keep_weakly
475-
? find_nondominated_set_3d_helper(points, rows, size, 3, true, nondom)
476-
: find_nondominated_set_3d_helper(points, rows, size, 3, false, nondom);
485+
const double ** rows = generate_row_pointers(points, size, 3);
486+
size_t new_size = find_nondominated_3d_impl(rows, size, keep_weakly, /* find_one_dominated=*/false);
487+
488+
if (new_size < size) {
489+
memset(nondom, false, size * sizeof(*nondom));
490+
size_t k = 0, n = 0;
491+
do {
492+
while (rows[k] == NULL) k++; // Find next nondominated (there must be at least one).
493+
nondom[row_index_from_ptr(points, rows[k], 3)] = true;
494+
n++;
495+
k++;
496+
} while (n < new_size);
497+
}
477498
free(rows);
478499
return new_size;
479500
}

c/nondominated_kung.h

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,112 @@ kung_merge(const double * points,
618618
}
619619
}
620620

621+
static inline size_t
622+
find_nondominated_set_3d_helper(const double * restrict points,
623+
const double ** rows,
624+
size_t size, dimension_t max_dim,
625+
const bool keep_weakly, boolvec * restrict nondom)
626+
{
627+
ASSUME(size > 1);
628+
629+
avl_tree_t tree;
630+
avl_init_tree(&tree, qsort_cmp_pdouble_asc_x_nonzero);
631+
avl_node_t * tnodes = malloc((size+1) * sizeof(*tnodes));
632+
avl_node_t * node = tnodes;
633+
node->item = rows[0];
634+
avl_insert_top(&tree, node);
635+
636+
const double sentinel[] = { INFINITY, -INFINITY };
637+
(++node)->item = sentinel;
638+
avl_insert_after(&tree, node - 1, node);
639+
640+
// In this context, size means "no dominated solution found".
641+
size_t new_size = size, j = 1;
642+
const double * last_dom = NULL;
643+
const double * restrict pk = rows[0];
644+
do {
645+
bool dominated;
646+
const double * restrict pj = rows[j];
647+
DEBUG2(printf_point("pj = [ ", pj, 3, " ], "));
648+
if (pk[0] > pj[0] || pk[1] > pj[1]) {
649+
avl_node_t * nodeaux;
650+
int res = avl_search_closest(&tree, pj, &nodeaux);
651+
assert(res != 0);
652+
if (res > 0) { // nodeaux goes before pj
653+
const double * restrict prev = nodeaux->item;
654+
assert(prev[0] != sentinel[0]);
655+
assert(prev[0] <= pj[0]);
656+
dominated = prev[1] <= pj[1];
657+
DEBUG2(printf_point("res > 0: prev: ", prev, 3, "\n"));
658+
nodeaux = nodeaux->next;
659+
} else if (nodeaux->prev) { // nodeaux goes after pj, so move to the next one.
660+
const double * restrict prev = nodeaux->prev->item;
661+
assert(prev[0] != sentinel[0]);
662+
assert(prev[0] <= pj[0]);
663+
dominated = prev[1] <= pj[1];
664+
DEBUG2(printf_point("res < 0: prev: ", prev, 3, "\n"));
665+
} else {
666+
dominated = false;
667+
}
668+
669+
if (!dominated) { // pj is NOT dominated by a point in the tree.
670+
const double * restrict point = nodeaux->item;
671+
assert(pj[0] <= point[0]);
672+
// Delete everything in the tree that is dominated by pj.
673+
while (pj[1] <= point[1]) {
674+
DEBUG2(printf_point("delete point: ", point, 3, "\n"));
675+
assert(pj[0] <= point[0]);
676+
nodeaux = nodeaux->next;
677+
point = nodeaux->item;
678+
/* FIXME: A possible speed up is to delete without
679+
rebalancing the tree because avl_insert_before() will
680+
rebalance. */
681+
avl_unlink_node(&tree, nodeaux->prev);
682+
}
683+
DEBUG2((point == sentinel)
684+
? printf_point("insert before sentinel: ", sentinel, 2, "\n")
685+
: printf_point("insert before point: ", point, 3, "\n"));
686+
(++node)->item = pj;
687+
avl_insert_before(&tree, nodeaux, node);
688+
}
689+
} else {
690+
// Handle duplicates and points that are dominated by the immediate
691+
// previous one.
692+
const bool k_eq_j = (pk[0] == pj[0]) & (pk[1] == pj[1]) & (pk[2] == pj[2]);
693+
if (!keep_weakly) { // We don't keep duplicates;
694+
dominated = true;
695+
if (unlikely(k_eq_j) && pj < pk) // Only the first duplicated point is kept.
696+
SWAP(pk, pj);
697+
} else { // or it is not a duplicate, so it is non-weakly dominated;
698+
dominated = likely(!k_eq_j)
699+
// or pk was dominated, then this one is also dominated.
700+
|| last_dom == pk;
701+
}
702+
DEBUG2(printf_point("weakly dominated by pk: ", pk, 3, "\n"));
703+
}
704+
if (dominated) { // pj is dominated by a point in the tree or by prev.
705+
/* Map the order in rows[], which is sorted, to the original order
706+
in points. */
707+
size_t pos_last_dom = row_index_from_ptr(points, pj, max_dim);
708+
if (unlikely(nondom == NULL)) {
709+
// In this context, it means "position of the first dominated solution found".
710+
new_size = pos_last_dom;
711+
goto early_end;
712+
}
713+
nondom[pos_last_dom] = false;
714+
last_dom = pj;
715+
new_size--;
716+
} else {
717+
pk = pj;
718+
}
719+
j++;
720+
} while (j < size);
721+
722+
early_end:
723+
free(tnodes);
724+
return new_size;
725+
}
726+
621727
static size_t
622728
maxima_top(const double * points, const double ** rows, size_t size,
623729
dimension_t dim, dimension_t max_dim,
@@ -637,7 +743,8 @@ maxima_rec_dim(const double * points, const double ** rows, size_t size,
637743
shift_to_next_dimension(r, rows, size);
638744
const double * const points_shifted = points + 1;
639745
if (dim == 4) { // We can reach this base case if one dimension has all equal values.
640-
// find_nondominated_set_3d_helper() will sort using cmp_ppdouble_asc_rev_3d().
746+
// Sort in ascending lexicographic order from the last dimension.
747+
qsort_typesafe(r, size, cmp_ppdouble_asc_rev_3d);
641748
new_size = keep_weakly // Help GCC generate specialized code for true/false.
642749
? find_nondominated_set_3d_helper(points_shifted, r, size, max_dim, true, nondom)
643750
: find_nondominated_set_3d_helper(points_shifted, r, size, max_dim, false, nondom);

0 commit comments

Comments
 (0)