Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions c/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ EXE_CFLAGS += $(SANITIZERS) $(OPT_CFLAGS) $(MARCH_FLAGS) $(WARN_CFLAGS) \

# List of test target base names
TEST_NAMES := eaf nondominated hv hvapprox igd epsilon dominatedsets ndsort
ASAN_TARGETS := $(addprefix asan-,$(TEST_NAMES))
TEST_TARGETS := $(addprefix test-,$(TEST_NAMES))
TIME_TARGETS := $(addprefix time-,$(TEST_NAMES))
ALL_NAMES := $(TEST_NAMES)
Expand Down Expand Up @@ -242,6 +243,27 @@ clean:
@$(RM) $(OBJS) $(HV_OBJS) $(HV_LIB)

TESTSUITE:="../testsuite"
asan: OPT_CFLAGS ?=-Og -g3
asan: DEBUG=1
asan: SANITIZERS ?=-fsanitize=undefined,address,pointer-compare,pointer-subtract,float-cast-overflow,float-divide-by-zero
asan:
@if ! test -d $(TESTSUITE); then \
echo "Error: Testsuite not found in $(TESTSUITE)" && exit 1; \
fi
$(MAKE) all DEBUG=1 SANITIZERS="$(SANITIZERS)" OPT_CFLAGS="$(OPT_CFLAGS)"
$(MAKE) $(ASAN_TARGETS)

$(ASAN_TARGETS): target=$(subst asan-,,$@)
$(ASAN_TARGETS): DEBUG=1
$(ASAN_TARGETS): OPT_CFLAGS ?=-Og -g3
$(ASAN_TARGETS): SANITIZERS ?=-fsanitize=undefined,address,pointer-compare,pointer-subtract,float-cast-overflow,float-divide-by-zero
$(ASAN_TARGETS):
$(MAKE) $(target) DEBUG=1 SANITIZERS="$(SANITIZERS)" OPT_CFLAGS="$(OPT_CFLAGS)"
$(call ECHO,---> Testing $(target) for errors (DEBUG=1, $(SANITIZERS)) <--- )
export ASAN_OPTIONS='detect_invalid_pointer_pairs=2:abort_on_error=1'
export UBSAN_OPTIONS='print_stacktrace=1:halt_on_error=1'
@cd $(TESTSUITE)/$(target) && ../regtest.py $(BINDIR)/$(target)$(EXE) && cd $(PWD)

test: OPT_CFLAGS ?=-Og -g3
test: SANITIZERS ?=
test:
Expand Down
2 changes: 1 addition & 1 deletion c/gcc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ ifeq ($(DEBUG), 0)
# -ftree-loop-im -ftree-loop-ivcanon -fivopts -ftree-vectorize -funroll-loops -fipa-pta
#
else
SANITIZERS ?= -fsanitize=undefined -fsanitize=address -fsanitize=float-cast-overflow -fsanitize=float-divide-by-zero
SANITIZERS ?= -fsanitize=undefined,address,pointer-compare,pointer-subtract,float-cast-overflow,float-divide-by-zero
OPT_CFLAGS ?= -g3 -O0
endif

Expand Down
142 changes: 90 additions & 52 deletions c/hv.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@
#include <float.h>
#include "common.h"
#include "hv.h"
#include "nondominated.h"
#define HV_RECURSIVE
#include "hv4d_priv.h"

#define STOP_DIMENSION 3 // default: stop on dimension 4.
#define MAX_ROWS_HV_INEX 15
#define STOP_DIMENSION 3 // stop on dimension 4.
#define MAX_ROWS_HV_INEX 2

static int compare_node(const void * restrict p1, const void * restrict p2)
{
Expand All @@ -64,23 +65,25 @@ fpli_setup_cdllist(const double * restrict data, dimension_t d,
dimension_t d_stop = d - STOP_DIMENSION;
size_t n = *size;

bool * nondom = is_nondominated_minimise(data, n, d, /* keep_weakly=*/false);

dlnode_t * head = malloc((n+1) * sizeof(*head));
size_t i = 1;
for (size_t j = 0; j < n; j++) {
/* Filters those points that do not strictly dominate the reference
point. This is needed to assure that the points left are only those
that are needed to calculate the hypervolume. */
const double * restrict px = data + j * d;
if (likely(strongly_dominates(px, ref, d))) {
if (nondom[j] && likely(strongly_dominates(px, ref, d))) {
head[i].x = px; // this will be fixed a few lines below...
i++;
}
}
free(nondom);
n = i - 1;
if (unlikely(n <= MAX_ROWS_HV_INEX))
goto finish;


// Allocate single blocks of memory as much as possible.
// We need space in r_next and r_prev for dimension 5 and above (d_stop - 1).
head->r_next = malloc(2 * (d_stop - 1) * (n+1) * sizeof(head));
Expand All @@ -96,7 +99,13 @@ fpli_setup_cdllist(const double * restrict data, dimension_t d,
// Link head and list4d; head is not used by HV4D, so next[0] and prev[0]
// should remain untouched.
head->next[0] = list4d;
head->prev[0] = list4d; // Save it twice so we can use assert() later.

// Reserve space for auxiliar list and auxiliar x vector used in onec4dplusU.
// This list will store at most n-1 points.
dlnode_t * list_aux = (dlnode_t *) malloc(n * sizeof(*list_aux));
list_aux->vol = malloc(3 * n * sizeof(double));
head->prev[0] = list_aux;
list_aux->next[0] = list4d;

for (i = 1; i <= n; i++) {
// Shift x because qsort() cannot take the dimension to sort as an argument.
Expand All @@ -120,33 +129,36 @@ fpli_setup_cdllist(const double * restrict data, dimension_t d,
*/
// Sort each dimension independently.
qsort(scratch, n, sizeof(*scratch), compare_node);
if (j == -1) {
(list4d+1)->next[1] = scratch[0];
scratch[0]->prev[1] = list4d+1;
if (j <= -1) {
const int j2 = j + 2;
(list4d+1)->next[j2] = scratch[0];
scratch[0]->prev[j2] = list4d+1;
for (i = 1; i < n; i++) {
scratch[i-1]->next[1] = scratch[i];
scratch[i]->prev[1] = scratch[i-1];
scratch[i-1]->next[j2] = scratch[i];
scratch[i]->prev[j2] = scratch[i-1];
}
scratch[n-1]->next[1] = list4d+2;
(list4d+2)->prev[1] = scratch[n-1];
break;
}
head->r_next[j] = scratch[0];
scratch[0]->r_prev[j] = head;
for (i = 1; i < n; i++) {
scratch[i-1]->r_next[j] = scratch[i];
scratch[i]->r_prev[j] = scratch[i-1];
scratch[n-1]->next[j2] = list4d+2;
(list4d+2)->prev[j2] = scratch[n-1];
if (j == -2)
break;
} else {
head->r_next[j] = scratch[0];
scratch[0]->r_prev[j] = head;
for (i = 1; i < n; i++) {
scratch[i-1]->r_next[j] = scratch[i];
scratch[i]->r_prev[j] = scratch[i-1];
}
scratch[n-1]->r_next[j] = head;
head->r_prev[j] = scratch[n-1];
}
scratch[n-1]->r_next[j] = head;
head->r_prev[j] = scratch[n-1];
j--;
// Consider next objective (in reverse order).
for (i = 1; i <= n; i++)
head[i].x--;
}
// Reset x to point to the first objective.
for (i = 1; i <= n; i++)
head[i].x -= STOP_DIMENSION;
head[i].x -= STOP_DIMENSION - 1;

free(scratch);

Expand All @@ -161,8 +173,10 @@ fpli_setup_cdllist(const double * restrict data, dimension_t d,

static void fpli_free_cdllist(dlnode_t * head)
{
assert(head->next[0] == head->prev[0]);
assert(head->next[0] == head->prev[0]->next[0]);
free_cdllist(head->next[0]); // free 4D sentinels
free(head->prev[0]->vol); // free x_aux (4D basecase)
free(head->prev[0]); // free list_aux (4D basecase)
free(head->r_next);
free(head->area);
free(head);
Expand All @@ -180,6 +194,34 @@ update_bound(double * restrict bound, const double * restrict x, dimension_t dim
}
}

static void
delete_4d(dlnode_t * restrict nodep)
{
nodep->prev[1]->next[1] = nodep->next[1];
nodep->next[1]->prev[1] = nodep->prev[1];
}

static void
delete_3d(dlnode_t * restrict nodep)
{
nodep->prev[0]->next[0] = nodep->next[0];
nodep->next[0]->prev[0] = nodep->prev[0];
}

static void
reinsert_4d(dlnode_t * restrict nodep)
{
nodep->prev[1]->next[1] = nodep;
nodep->next[1]->prev[1] = nodep;
}

static void
reinsert_3d(dlnode_t * restrict nodep)
{
nodep->prev[0]->next[0] = nodep;
nodep->next[0]->prev[0] = nodep;
}

static void
delete_dom(dlnode_t * restrict nodep, dimension_t dim)
{
Expand All @@ -190,9 +232,8 @@ delete_dom(dlnode_t * restrict nodep, dimension_t dim)
nodep->r_prev[d]->r_next[d] = nodep->r_next[d];
nodep->r_next[d]->r_prev[d] = nodep->r_prev[d];
}
// Dimension 4.
nodep->prev[1]->next[1] = nodep->next[1];
nodep->next[1]->prev[1] = nodep->prev[1];
delete_4d(nodep);
delete_3d(nodep);
}

static void
Expand All @@ -213,9 +254,8 @@ reinsert_nobound(dlnode_t * restrict nodep, dimension_t dim)
nodep->r_prev[d]->r_next[d] = nodep;
nodep->r_next[d]->r_prev[d] = nodep;
}
// Dimension 4.
nodep->prev[1]->next[1] = nodep;
nodep->next[1]->prev[1] = nodep;
reinsert_4d(nodep);
reinsert_3d(nodep);
}

static void
Expand All @@ -226,15 +266,14 @@ reinsert(dlnode_t * restrict nodep, dimension_t dim, double * restrict bound)
}

static double
fpli_hv4d(dlnode_t * restrict list, size_t c _attr_maybe_unused)
fpli_onec4d(dlnode_t * restrict list, size_t c _attr_maybe_unused, dlnode_t * restrict the_point)
{
ASSUME(c > 1);
assert(list->next[0] == list->prev[0]);
assert(list->next[0] == list->prev[0]->next[0]);
dlnode_t * restrict list4d = list->next[0];
// hv4dplusU() will change the sentinels for 3D, so we need to reset them.
reset_sentinels_3d(list4d);
double hv = hv4dplusU(list4d);
return hv;
dlnode_t * restrict list_aux = list->prev[0];
double contrib = onec4dplusU(list4d, list_aux, the_point);
return contrib;
}

_attr_optimize_finite_and_associative_math // Required for auto-vectorization: https://gcc.gnu.org/PR122687
Expand Down Expand Up @@ -321,7 +360,7 @@ hv_inex_list(const dlnode_t * restrict list, int n, dimension_t dim,
child = subset_max[top];
upper_bound(child, list[idx].x, parent, dim);
// Inclusion–exclusion accumulation.
hv[top & 1] += one_point_hv(child, ref, dim);
hv[top % 2] += one_point_hv(child, ref, dim);
idx++;
} else if (top > 0) {
--top;
Expand Down Expand Up @@ -357,15 +396,15 @@ _attr_maybe_unused static size_t debug_counter[6] = { 0 };

static double
hv_recursive(dlnode_t * restrict list, dimension_t dim, size_t c,
const double * restrict ref, double * restrict bound)
const double * restrict ref, double * restrict bound, dlnode_t * the_point)
{
ASSUME(c > 1);
ASSUME(dim >= STOP_DIMENSION);
if (dim == STOP_DIMENSION) {
/*---------------------------------------
base case of dimension 4
--------------------------------------*/
return fpli_hv4d(list, c);
return fpli_onec4d(list, c, the_point);
}
ASSUME(dim > STOP_DIMENSION);
/* ------------------------------------------------------
Expand Down Expand Up @@ -434,7 +473,10 @@ hv_recursive(dlnode_t * restrict list, dimension_t dim, size_t c,
DEBUG1(debug_counter[1]++);
hypera = p1_prev->area[d_stop];
} else {
hypera = hv_recursive(list, dim - 1, c, ref, bound);
hypera = hv_recursive(list, dim - 1, c, ref, bound, p1);
if (dim - 1 == STOP_DIMENSION) { // hypera only has the contribution of p1
hypera += p1_prev->area[d_stop];
}
/* At this point, p1 is the point with the highest value in
dimension dim in the list: If it is dominated in dimension
dim-1, then it is also dominated in dimension dim. */
Expand Down Expand Up @@ -495,19 +537,19 @@ fpli_hv_ge5d(dlnode_t * restrict list, dimension_t dim, size_t c,
// FIXME: This is never used?
// bound[d_stop] = p0->x[dim];
reinsert_nobound(p0, dim);
p1 = p0;
dlnode_t * p1_prev = p0->r_prev[d_stop - 1];
p0 = p0->r_next[d_stop - 1];
c++;

assert(c > 1);
while (true) {
// FIXME: This is not true in the first iteration if c > 1 previously.
//assert(p0 == p1->r_prev[d_stop]);
assert(p1_prev == p1->r_prev[d_stop - 1]);
dlnode_t * p1_prev = p0->r_prev[d_stop - 1];
p1 = p0;
p0 = p0->r_next[d_stop - 1];
p1->vol[d_stop] = hyperv;
assert(p1->ignore == 0);
double hypera = hv_recursive(list, dim - 1, c, ref, bound);
c++;

double hypera = hv_recursive(list, dim - 1, c, ref, bound, p1);
if (dim - 1 == STOP_DIMENSION) { //hypera only has the contribution of p1
hypera += p1_prev->area[d_stop];
}
/* At this point, p1 is the point with the highest value in
dimension dim in the list: If it is dominated in dimension
dim-1, then it is also dominated in dimension dim. */
Expand All @@ -533,10 +575,6 @@ fpli_hv_ge5d(dlnode_t * restrict list, dimension_t dim, size_t c,
// bound[d_stop] = p0->x[dim];
// FIXME: Does updating the bound here matters?
reinsert(p0, dim, bound);
p1 = p0;
p1_prev = p0->r_prev[d_stop - 1];
p0 = p0->r_next[d_stop - 1];
c++;
}
}

Expand Down
Loading
Loading