Fully ported tree-based orthogonal range search

This commit is contained in:
Dongryeol Lee
2007-09-28 03:17:21 +00:00
parent e867f7f852
commit a25ecde120
2 changed files with 256 additions and 6 deletions
+12 -2
View File
@@ -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