Fully ported tree-based orthogonal range search
This commit is contained in:
@@ -2,9 +2,19 @@
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
NaiveOrthoRangeSearch search;
|
||||
fx_init(argc, argv);
|
||||
bool do_naive = fx_param_exists(NULL, "do_naive");
|
||||
|
||||
search.Init();
|
||||
OrthoRangeSearch fast_search;
|
||||
fast_search.Init();
|
||||
fast_search.Compute();
|
||||
|
||||
// if naive option is specified, do naive algorithm
|
||||
if(do_naive) {
|
||||
NaiveOrthoRangeSearch search;
|
||||
search.Init();
|
||||
search.Compute();
|
||||
}
|
||||
fx_done();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ORTHO_RANGE_SEARCH_H
|
||||
#define ORTHO_RANGE_SEARCH_H
|
||||
|
||||
#include <values.h>
|
||||
#include "fastlib/fastlib_int.h"
|
||||
|
||||
/** Naive orthogonal range search class */
|
||||
@@ -20,7 +21,7 @@ class NaiveOrthoRangeSearch {
|
||||
* orthogonal range to search in: this will be generalized to a list
|
||||
* of orthogonal ranges
|
||||
*/
|
||||
DHrectBound<2> range_;
|
||||
ArrayList<DRange> range_;
|
||||
|
||||
public:
|
||||
|
||||
@@ -32,13 +33,54 @@ class NaiveOrthoRangeSearch {
|
||||
void Init() {
|
||||
|
||||
const char *fname = fx_param_str(NULL, "data", NULL);
|
||||
|
||||
const char *dname = fx_param_str(NULL, "range", NULL);
|
||||
|
||||
// read in the dataset
|
||||
Dataset dataset_;
|
||||
dataset_.InitFromFile(fname);
|
||||
data_.Own(&(dataset_.matrix()));
|
||||
|
||||
// read the orthogonal query range text file
|
||||
range_.Init(data_.n_rows());
|
||||
TextTokenizer tokenizer;
|
||||
tokenizer.Open(dname);
|
||||
|
||||
for(index_t i = 0; i < data_.n_rows(); i++) {
|
||||
|
||||
tokenizer.Gobble();
|
||||
if((tokenizer.Current().c_str())[0] == '-') {
|
||||
tokenizer.Gobble();
|
||||
if(strncmp(tokenizer.Current().c_str(), "INFINITY", 8) == 0) {
|
||||
range_[i].lo = -MAXDOUBLE;
|
||||
}
|
||||
}
|
||||
else if(tokenizer.Match("INFINITY")) {
|
||||
range_[i].lo = MAXDOUBLE;
|
||||
}
|
||||
else {
|
||||
range_[i].lo = atof(tokenizer.Current().c_str());
|
||||
}
|
||||
tokenizer.Gobble();
|
||||
if((tokenizer.Current().c_str())[0] == ',') {
|
||||
tokenizer.Gobble();
|
||||
}
|
||||
|
||||
if((tokenizer.Current().c_str())[0] == '-') {
|
||||
tokenizer.Gobble();
|
||||
if(strncmp(tokenizer.Current().c_str(), "INFINITY", 8) == 0) {
|
||||
range_[i].hi = -MAXDOUBLE;
|
||||
}
|
||||
}
|
||||
else if(strncmp(tokenizer.Current().c_str(), "INFINITY", 8) == 0) {
|
||||
range_[i].hi = MAXDOUBLE;
|
||||
}
|
||||
else {
|
||||
range_[i].hi = atof(tokenizer.Current().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// re-initialize boolean flag
|
||||
in_range_.Init(data_.n_cols());
|
||||
for(index_t i = 0; i < data_.n_cols(); i++) {
|
||||
in_range_[i] = false;
|
||||
}
|
||||
@@ -47,19 +89,25 @@ class NaiveOrthoRangeSearch {
|
||||
/** the main computation of naive orthogonal range search */
|
||||
void Compute() {
|
||||
|
||||
fx_timer_start(NULL, "naive_search");
|
||||
for(index_t i = 0; i < data_.n_cols(); i++) {
|
||||
|
||||
Vector pt;
|
||||
bool flag = true;
|
||||
data_.MakeColumnVector(i, &pt);
|
||||
|
||||
// determine which one of the two cases we have: EXCLUDE, SUBSUME
|
||||
// first the EXCLUDE case: when dist is above the upper bound distance
|
||||
// of this dimension, or dist is below the lower bound distance of
|
||||
// this dimension
|
||||
if(range_.Contains(pt)) {
|
||||
in_range_[i] = true;
|
||||
for(index_t d = 0; d < data_.n_rows(); d++) {
|
||||
if(pt[d] < range_[d].lo || pt[d] > range_[d].hi) {
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
in_range_[i] = flag;
|
||||
}
|
||||
fx_timer_stop(NULL, "naive_search");
|
||||
}
|
||||
|
||||
};
|
||||
@@ -67,7 +115,199 @@ class NaiveOrthoRangeSearch {
|
||||
/** Faster orthogonal range search class */
|
||||
class OrthoRangeSearch {
|
||||
|
||||
public:
|
||||
typedef BinarySpaceTree<DHrectBound<2>, Matrix> Tree;
|
||||
|
||||
// getters and setters
|
||||
|
||||
/** get the result of the search by expanding the node list */
|
||||
void get_results(ArrayList<int> &results) const {
|
||||
|
||||
for(index_t i = 0; i < candidate_nodes_.size(); i++) {
|
||||
|
||||
Tree *node = candidate_nodes_[i];
|
||||
for(index_t r = node->begin(); r < node->end(); r++) {
|
||||
*results.AddBack() = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// interesting functions...
|
||||
|
||||
/** perform the orthogonal range search */
|
||||
void Compute() {
|
||||
fx_timer_start(NULL, "tree_range_search");
|
||||
ortho_range_search(root_, 0);
|
||||
fx_timer_stop(NULL, "tree_range_search");
|
||||
}
|
||||
|
||||
/** initialization function - to read the data and to construct tree */
|
||||
void Init() {
|
||||
|
||||
const char *fname = fx_param_str(NULL, "data", NULL);
|
||||
const char *dname = fx_param_str(NULL, "range", NULL);
|
||||
int leaflen = fx_param_int(NULL, "leaflen", 20);
|
||||
|
||||
// read in the dataset
|
||||
Dataset dataset_;
|
||||
dataset_.InitFromFile(fname);
|
||||
data_.Own(&(dataset_.matrix()));
|
||||
fx_timer_start(NULL, "tree_d");
|
||||
root_ = tree::MakeKdTreeMidpoint<Tree>(data_, leaflen, NULL);
|
||||
fx_timer_stop(NULL, "tree_d");
|
||||
|
||||
// read in the query range
|
||||
search_range_.Init(data_.n_rows());
|
||||
TextTokenizer tokenizer;
|
||||
tokenizer.Open(dname);
|
||||
|
||||
for(index_t i = 0; i < data_.n_rows(); i++) {
|
||||
|
||||
tokenizer.Gobble();
|
||||
if((tokenizer.Current().c_str())[0] == '-') {
|
||||
tokenizer.Gobble();
|
||||
if(strncmp(tokenizer.Current().c_str(), "INFINITY", 8) == 0) {
|
||||
search_range_[i].lo = -MAXDOUBLE;
|
||||
}
|
||||
}
|
||||
else if(tokenizer.Match("INFINITY")) {
|
||||
search_range_[i].lo = MAXDOUBLE;
|
||||
}
|
||||
else {
|
||||
search_range_[i].lo = atof(tokenizer.Current().c_str());
|
||||
}
|
||||
tokenizer.Gobble();
|
||||
if((tokenizer.Current().c_str())[0] == ',') {
|
||||
tokenizer.Gobble();
|
||||
}
|
||||
|
||||
if((tokenizer.Current().c_str())[0] == '-') {
|
||||
tokenizer.Gobble();
|
||||
if(strncmp(tokenizer.Current().c_str(), "INFINITY", 8) == 0) {
|
||||
search_range_[i].hi = -MAXDOUBLE;
|
||||
}
|
||||
}
|
||||
else if(strncmp(tokenizer.Current().c_str(), "INFINITY", 8) == 0) {
|
||||
search_range_[i].hi = MAXDOUBLE;
|
||||
}
|
||||
else {
|
||||
search_range_[i].hi = atof(tokenizer.Current().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// initialize candidate nodes and points */
|
||||
candidate_nodes_.Init(0);
|
||||
candidate_points_.Init(0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** flag determining a prune */
|
||||
enum PruneStatus {SUBSUME, INCONCLUSIVE};
|
||||
|
||||
// member variables
|
||||
/** pointer to the dataset */
|
||||
Matrix data_;
|
||||
|
||||
/**
|
||||
* orthogonal range to search in: this will be generalized to a list
|
||||
* of orthogonal ranges
|
||||
*/
|
||||
ArrayList<DRange> search_range_;
|
||||
|
||||
/** the root of the tree */
|
||||
Tree *root_;
|
||||
|
||||
/**
|
||||
* list of candidates nodes that contain the points that are in the range
|
||||
* - this needs to be generalized to be a list of lists for multiple
|
||||
* orthogonal range queries.
|
||||
*/
|
||||
ArrayList<Tree *> candidate_nodes_;
|
||||
|
||||
/**
|
||||
* List of candidate points
|
||||
*/
|
||||
ArrayList<int> candidate_points_;
|
||||
|
||||
// member functions
|
||||
|
||||
/** base case */
|
||||
void ortho_slow_range_search(Tree *node) {
|
||||
PruneStatus prune_flag;
|
||||
|
||||
for(index_t row = node->begin(); row < node->end(); row++) {
|
||||
prune_flag = SUBSUME;
|
||||
|
||||
for(index_t d = 0; d < data_.n_rows(); d++) {
|
||||
// determine which one of the two cases we have: EXCLUDE, SUBSUME
|
||||
|
||||
DRange search_dir_range = search_range_[d];
|
||||
|
||||
// first the EXCLUDE case: when dist is above the upper bound distance
|
||||
// of this dimension, or dist is below the lower bound distance of
|
||||
// this dimension
|
||||
if(data_.get(d, row) > search_dir_range.hi ||
|
||||
data_.get(d, row) < search_dir_range.lo) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(prune_flag == SUBSUME) {
|
||||
*candidate_points_.AddBack() = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** the workhorse algorithm for fast orthgonal range search */
|
||||
void ortho_range_search(Tree *node, int start_dim) {
|
||||
|
||||
PruneStatus prune_flag = SUBSUME;
|
||||
|
||||
// loop over each dimension to determine inclusion/exclusion by
|
||||
// determining the lower and the upper bound distance per each dimension
|
||||
// for the given reference node, kn
|
||||
for(index_t d = start_dim; d < data_.n_rows(); d++) {
|
||||
|
||||
DRange search_dir_range = search_range_[d];
|
||||
DRange node_dir_range = node->bound().get(d);
|
||||
|
||||
// determine which one of the three cases we have: EXCLUDE, SUBSUME, or
|
||||
// INCONCLUSIVE.
|
||||
|
||||
// first the EXCLUDE case: when mindist is above the upper bound
|
||||
// distance of this dimension, or maxdist is below the lower bound
|
||||
// distance of this dimension
|
||||
if(node_dir_range.lo > search_dir_range.hi ||
|
||||
node_dir_range.hi < search_dir_range.lo) {
|
||||
return;
|
||||
}
|
||||
// otherwise, check for SUBSUME case
|
||||
else if(search_dir_range.lo <= node_dir_range.lo &&
|
||||
node_dir_range.hi <= search_dir_range.hi) {
|
||||
}
|
||||
// if any dimension turns out to be inconclusive, then break.
|
||||
else {
|
||||
start_dim = d;
|
||||
prune_flag = INCONCLUSIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// in case of subsume, then add all points owned by this node to
|
||||
// candidates
|
||||
if(prune_flag == SUBSUME) {
|
||||
candidate_nodes_.AddBackItem(node);
|
||||
return;
|
||||
}
|
||||
else if(node->is_leaf()) {
|
||||
ortho_slow_range_search(node);
|
||||
}
|
||||
else {
|
||||
ortho_range_search(node->left(), start_dim);
|
||||
ortho_range_search(node->right(), start_dim);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user