@@ -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 * * 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,99 +362,123 @@ 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 ;
368- const double * last_dom = NULL ;
365+ size_t new_size = size , k = 0 ;
366+ size_t last_dom_pos = size ;
369367 const double * restrict pk = rows [0 ];
370- do {
371- bool dominated ;
368+ for (size_t j = 1 ; j < size ; j ++ ) {
372369 const double * restrict pj = rows [j ];
373370 DEBUG2 (printf_point ("pj = [ " , pj , 3 , " ], " ));
374- if (pk [0 ] > pj [0 ] || pk [1 ] > pj [1 ]) {
371+
372+ // First, handle duplicates and points that are dominated by the
373+ // immediate previous one.
374+ if (unlikely (pk [0 ] <= pj [0 ] && pk [1 ] <= pj [1 ])) {
375+ DEBUG2 (printf_point ("weakly dominated by pk: " , pk , 3 , "\n" ));
376+ const bool k_eq_j = (pk [0 ] == pj [0 ]) & (pk [1 ] == pj [1 ]) & (pk [2 ] == pj [2 ]);
377+ if (likely (!k_eq_j )) { // it is not a duplicate, so it is non-weakly dominated.
378+ goto j_is_dominated ;
379+ } else if (!keep_weakly ) { // Don't keep duplicates
380+ // Only the first duplicated point is kept.
381+ if (pk < pj ) {
382+ goto j_is_dominated ;
383+ }
384+ if (find_one_dominated ) {
385+ // In this context, it means "position of the first dominated solution found".
386+ new_size = k ;
387+ goto early_end ;
388+ }
389+ last_dom_pos = k ;
390+ rows [k ] = NULL ;
391+ new_size -- ;
392+ // Fall to j_is_NOT_dominated
393+ } else if (last_dom_pos == k ) // pk was dominated, then this one is also dominated;
394+ goto j_is_dominated ;
395+ // Fall to j_is_NOT_dominated
396+
397+ } else {
398+ // Check if pj is dominated by a point in the tree.
399+ assert (pk [0 ] > pj [0 ] || pk [1 ] > pj [1 ]);
375400 avl_node_t * nodeaux ;
376401 int res = avl_search_closest (& tree , pj , & nodeaux );
377402 assert (res != 0 );
378403 if (res > 0 ) { // nodeaux goes before pj
379404 const double * restrict prev = nodeaux -> item ;
405+ DEBUG2 (printf_point ("res > 0: prev: " , prev , 3 , "\n" ));
380406 assert (prev [0 ] != sentinel [0 ]);
381407 assert (prev [0 ] <= pj [0 ]);
382- dominated = prev [1 ] <= pj [1 ];
383- DEBUG2 ( printf_point ( "res > 0: prev: " , prev , 3 , "\n" )) ;
408+ if ( prev [1 ] <= pj [1 ])
409+ goto j_is_dominated ;
384410 nodeaux = nodeaux -> next ;
385411 } else if (nodeaux -> prev ) { // nodeaux goes after pj, so move to the next one.
386412 const double * restrict prev = nodeaux -> prev -> item ;
413+ DEBUG2 (printf_point ("res < 0: prev: " , prev , 3 , "\n" ));
387414 assert (prev [0 ] != sentinel [0 ]);
388415 assert (prev [0 ] <= pj [0 ]);
389- dominated = prev [1 ] <= pj [1 ];
390- DEBUG2 (printf_point ("res < 0: prev: " , prev , 3 , "\n" ));
391- } else {
392- dominated = false;
416+ if (prev [1 ] <= pj [1 ])
417+ goto j_is_dominated ;
393418 }
394419
395- if (!dominated ) { // pj is NOT dominated by a point in the tree.
396- const double * restrict point = nodeaux -> item ;
420+ // pj is NOT dominated by a point in the tree.
421+ const double * restrict point = nodeaux -> item ;
422+ assert (pj [0 ] <= point [0 ]);
423+ // Delete everything in the tree that is dominated by pj.
424+ while (pj [1 ] <= point [1 ]) {
425+ DEBUG2 (printf_point ("delete point: " , point , 3 , "\n" ));
397426 assert (pj [0 ] <= point [0 ]);
398- // Delete everything in the tree that is dominated by pj.
399- while (pj [1 ] <= point [1 ]) {
400- DEBUG2 (printf_point ("delete point: " , point , 3 , "\n" ));
401- assert (pj [0 ] <= point [0 ]);
402- nodeaux = nodeaux -> next ;
403- point = nodeaux -> item ;
404- /* FIXME: A possible speed up is to delete without
405- rebalancing the tree because avl_insert_before() will
406- rebalance. */
407- avl_unlink_node (& tree , nodeaux -> prev );
408- }
409- DEBUG2 ((point == sentinel )
410- ? printf_point ("insert before sentinel: " , sentinel , 2 , "\n" )
411- : printf_point ("insert before point: " , point , 3 , "\n" ));
412- (++ node )-> item = pj ;
413- avl_insert_before (& tree , nodeaux , node );
414- }
415- } else {
416- // Handle duplicates and points that are dominated by the immediate
417- // previous one.
418- const bool k_eq_j = (pk [0 ] == pj [0 ]) & (pk [1 ] == pj [1 ]) & (pk [2 ] == pj [2 ]);
419- if (!keep_weakly ) { // We don't keep duplicates;
420- dominated = true;
421- if (unlikely (k_eq_j ) && pj < pk ) // Only the first duplicated point is kept.
422- SWAP (pk , pj );
423- } else { // or it is not a duplicate, so it is non-weakly dominated;
424- dominated = likely (!k_eq_j )
425- // or pk was dominated, then this one is also dominated.
426- || last_dom == pk ;
427+ nodeaux = nodeaux -> next ;
428+ point = nodeaux -> item ;
429+ /* FIXME: A possible speed up is to delete without
430+ rebalancing the tree because avl_insert_before() will
431+ rebalance. */
432+ avl_unlink_node (& tree , nodeaux -> prev );
427433 }
428- DEBUG2 (printf_point ("weakly dominated by pk: " , pk , 3 , "\n" ));
434+ DEBUG2 ((point == sentinel )
435+ ? printf_point ("insert before sentinel: " , sentinel , 2 , "\n" )
436+ : printf_point ("insert before point: " , point , 3 , "\n" ));
437+ (++ node )-> item = pj ;
438+ avl_insert_before (& tree , nodeaux , node );
439+ // Fall to j_is_NOT_dominated
429440 }
430- 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 )) {
435- // In this context, it means "position of the first dominated solution found".
436- new_size = pos_last_dom ;
437- goto early_end ;
438- }
439- nondom [pos_last_dom ] = false;
440- last_dom = pj ;
441- new_size -- ;
442- } else {
443- pk = pj ;
441+ // j_is_NOT_dominated
442+ pk = pj ;
443+ k = j ;
444+ continue ;
445+
446+ j_is_dominated : // pj is dominated by a point in the tree or by pk.
447+ if (find_one_dominated ) {
448+ // In this context, it means "position of the first dominated solution found".
449+ new_size = j ;
450+ goto early_end ;
444451 }
445- j ++ ;
446- } while (j < size );
452+ last_dom_pos = j ;
453+ rows [j ] = NULL ;
454+ new_size -- ;
455+ }
447456
448457early_end :
449458 free (tnodes );
450459 return new_size ;
451460}
452461
462+ static __force_inline__ size_t
463+ find_nondominated_3d_impl (const double * * restrict rows , size_t size ,
464+ const bool keep_weakly ,
465+ const bool find_one_dominated )
466+ {
467+ // Sort in ascending lexicographic order from the last dimension.
468+ qsort_typesafe (rows , size , cmp_ppdouble_asc_rev_3d );
469+ // Help GCC generate all possible specializations of this function.
470+ return keep_weakly
471+ ? find_nondominated_3d_impl_sorted (rows , size , true, find_one_dominated )
472+ : find_nondominated_3d_impl_sorted (rows , size , false, find_one_dominated );
473+ }
474+
453475static inline size_t
454476find_dominated_3d_ (const double * restrict points , size_t size , bool keep_weakly )
455477{
456478 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 );
479+ size_t pos = find_nondominated_3d_impl ( rows , size , keep_weakly , /* find_one_dominated=*/ true);
480+ if ( pos < size )
481+ pos = row_index_from_ptr (points , rows [ pos ], 3 );
460482 free (rows );
461483 return pos ;
462484}
@@ -469,11 +491,20 @@ static inline size_t
469491find_nondominated_set_3d_ (const double * restrict points , size_t size ,
470492 const bool keep_weakly , boolvec * restrict nondom )
471493{
472- const double * * rows = generate_row_pointers (points , size , 3 );
473494 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 );
495+ const double * * rows = generate_row_pointers (points , size , 3 );
496+ size_t new_size = find_nondominated_3d_impl (rows , size , keep_weakly , /* find_one_dominated=*/ false);
497+
498+ if (new_size < size ) {
499+ memset (nondom , false, size * sizeof (* nondom ));
500+ size_t k = 0 , n = 0 ;
501+ do {
502+ while (rows [k ] == NULL ) k ++ ; // Find next nondominated (there must be at least one).
503+ nondom [row_index_from_ptr (points , rows [k ], 3 )] = true;
504+ n ++ ;
505+ k ++ ;
506+ } while (n < new_size );
507+ }
477508 free (rows );
478509 return new_size ;
479510}
0 commit comments