#include #include #include #include #include #include #include #include #include #include #include #include "ckdtree_decl.h" #include "rectangle.h" struct WeightedTree { const ckdtree *tree; double *weights; double *node_weights; }; struct CNBParams { double *r; void * results; /* will be casted inside */ WeightedTree self, other; int cumulative; }; template static void traverse( RectRectDistanceTracker *tracker, const CNBParams *params, double *start, double *end, const ckdtreenode *node1, const ckdtreenode *node2) { static void (* const next)(RectRectDistanceTracker *tracker, const CNBParams *params, double *start, double *end, const ckdtreenode *node1, const ckdtreenode *node2) = traverse; ResultType *results = (ResultType*) params->results; /* * Speed through pairs of nodes all of whose children are close * and see if any work remains to be done */ double * new_start = std::lower_bound(start, end, tracker->min_distance); double * new_end = std::lower_bound(start, end, tracker->max_distance); /* since max_distance >= min_distance, end < start never happens */ if (params->cumulative) { double * i; if (new_end != end) { ResultType nn = WeightType::get_weight(¶ms->self, node1) * WeightType::get_weight(¶ms->other, node2); for (i = new_end; i < end; ++i) { results[i - params->r] += nn; } } /* any bins larger than end have been correctly counted, thus * thus we can truncate the queries in future of this branch of the traversal*/ start = new_start; end = new_end; } else { start = new_start; end = new_end; if (end == start) { ResultType nn = WeightType::get_weight(¶ms->self, node1) * WeightType::get_weight(¶ms->other, node2); results[start - params->r] += nn; } } if (end == start) { /* this pair falls into exactly one bin, no need to probe deeper. */ return; } /* OK, need to probe a bit deeper */ if (node1->split_dim == -1) { /* 1 is leaf node */ if (node2->split_dim == -1) { /* 1 & 2 are leaves */ ckdtree_intp_t i, j; const double p = tracker->p; const double tmd = tracker->max_distance; const double *sdata = params->self.tree->raw_data; const ckdtree_intp_t *sindices = params->self.tree->raw_indices; const double *odata = params->other.tree->raw_data; const ckdtree_intp_t *oindices = params->other.tree->raw_indices; const ckdtree_intp_t m = params->self.tree->m; const ckdtree_intp_t start1 = node1->start_idx; const ckdtree_intp_t start2 = node2->start_idx; const ckdtree_intp_t end1 = node1->end_idx; const ckdtree_intp_t end2 = node2->end_idx; CKDTREE_PREFETCH(sdata + sindices[start1] * m, 0, m); if (start1 < end1 - 1) CKDTREE_PREFETCH(sdata + sindices[start1+1] * m, 0, m); /* brute-force */ for (i = start1; i < end1; ++i) { if (i < end1 - 2) CKDTREE_PREFETCH(sdata + sindices[i+2] * m, 0, m); CKDTREE_PREFETCH(odata + oindices[start2] * m, 0, m); if (start2 < end2 - 1) CKDTREE_PREFETCH(odata + oindices[start2+1] * m, 0, m); for (j = start2; j < end2; ++j) { if (j < end2 - 2) CKDTREE_PREFETCH(odata + oindices[j+2] * m, 0, m); double d = MinMaxDist::point_point_p(params->self.tree, sdata + sindices[i] * m, odata + oindices[j] * m, p, m, tmd); if (params->cumulative) { /* * I think it's usually cheaper to test d against all * r's than to generate a distance array, sort it, then * search for all r's via binary search */ double * l; for (l = start; l < end; ++l) { if (d <= *l) { results[l - params->r] += WeightType::get_weight(¶ms->self, sindices[i]) * WeightType::get_weight(¶ms->other, oindices[j]); } } } else { const double *l = std::lower_bound(start, end, d); results[l - params->r] += WeightType::get_weight(¶ms->self, sindices[i]) * WeightType::get_weight(¶ms->other, oindices[j]); } } } } else { /* 1 is a leaf node, 2 is inner node */ tracker->push_less_of(2, node2); next(tracker, params, start, end, node1, node2->less); tracker->pop(); tracker->push_greater_of(2, node2); next(tracker, params, start, end, node1, node2->greater); tracker->pop(); } } else { /* 1 is an inner node */ if (node2->split_dim == -1) { /* 1 is an inner node, 2 is a leaf node */ tracker->push_less_of(1, node1); next(tracker, params, start, end, node1->less, node2); tracker->pop(); tracker->push_greater_of(1, node1); next(tracker, params, start, end, node1->greater, node2); tracker->pop(); } else { /* 1 and 2 are inner nodes */ tracker->push_less_of(1, node1); tracker->push_less_of(2, node2); next(tracker, params, start, end, node1->less, node2->less); tracker->pop(); tracker->push_greater_of(2, node2); next(tracker, params, start, end, node1->less, node2->greater); tracker->pop(); tracker->pop(); tracker->push_greater_of(1, node1); tracker->push_less_of(2, node2); next(tracker, params, start, end, node1->greater, node2->less); tracker->pop(); tracker->push_greater_of(2, node2); next(tracker, params, start, end, node1->greater, node2->greater); tracker->pop(); tracker->pop(); } } } template void count_neighbors(struct CNBParams *params, ckdtree_intp_t n_queries, const double p) { const ckdtree *self = params->self.tree; const ckdtree *other = params->other.tree; #define HANDLE(cond, kls) \ if (cond) { \ RectRectDistanceTracker tracker(self, r1, r2, p, 0.0, 0.0);\ traverse(&tracker, params, params->r, params->r+n_queries, \ self->ctree, other->ctree); \ } else Rectangle r1(self->m, self->raw_mins, self->raw_maxes); Rectangle r2(other->m, other->raw_mins, other->raw_maxes); if (CKDTREE_LIKELY(self->raw_boxsize_data == NULL)) { HANDLE(CKDTREE_LIKELY(p == 2), MinkowskiDistP2) HANDLE(p == 1, MinkowskiDistP1) HANDLE(ckdtree_isinf(p), MinkowskiDistPinf) HANDLE(1, MinkowskiDistPp) {} } else { HANDLE(CKDTREE_LIKELY(p == 2), BoxMinkowskiDistP2) HANDLE(p == 1, BoxMinkowskiDistP1) HANDLE(ckdtree_isinf(p), BoxMinkowskiDistPinf) HANDLE(1, BoxMinkowskiDistPp) {} } } struct Unweighted { /* the interface for accessing weights of unweighted data. */ static inline ckdtree_intp_t get_weight(const WeightedTree *wt, const ckdtreenode * node) { return node->children; } static inline ckdtree_intp_t get_weight(const WeightedTree *wt, const ckdtree_intp_t i) { return 1; } }; int count_neighbors_unweighted(const ckdtree *self, const ckdtree *other, ckdtree_intp_t n_queries, double *real_r, intptr_t *results, const double p, int cumulative) { CNBParams params = {0}; params.r = real_r; params.results = (void*) results; params.self.tree = self; params.other.tree = other; params.cumulative = cumulative; count_neighbors(¶ms, n_queries, p); return 0; } struct Weighted { /* the interface for accessing weights of weighted data. */ static inline double get_weight(const WeightedTree *wt, const ckdtreenode * node) { return (wt->weights != NULL) ? wt->node_weights[node - wt->tree->ctree] : node->children; } static inline double get_weight(const WeightedTree *wt, const ckdtree_intp_t i) { return (wt->weights != NULL)?wt->weights[i]:1; } }; int count_neighbors_weighted(const ckdtree *self, const ckdtree *other, double *self_weights, double *other_weights, double *self_node_weights, double *other_node_weights, ckdtree_intp_t n_queries, double *real_r, double *results, const double p, int cumulative) { CNBParams params = {0}; params.r = real_r; params.results = (void*) results; params.cumulative = cumulative; params.self.tree = self; params.other.tree = other; if (self_weights) { params.self.weights = self_weights; params.self.node_weights = self_node_weights; } if (other_weights) { params.other.weights = other_weights; params.other.node_weights = other_node_weights; } count_neighbors(¶ms, n_queries, p); return 0; }