diff --git a/fastlib/u/dongryel/range_search/main.cc b/fastlib/u/dongryel/range_search/main.cc index 21b430ff5e..deac02daf0 100644 --- a/fastlib/u/dongryel/range_search/main.cc +++ b/fastlib/u/dongryel/range_search/main.cc @@ -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; } diff --git a/fastlib/u/dongryel/range_search/ortho_range_search.h b/fastlib/u/dongryel/range_search/ortho_range_search.h index c1c33105d6..a928b3e707 100644 --- a/fastlib/u/dongryel/range_search/ortho_range_search.h +++ b/fastlib/u/dongryel/range_search/ortho_range_search.h @@ -1,6 +1,7 @@ #ifndef ORTHO_RANGE_SEARCH_H #define ORTHO_RANGE_SEARCH_H +#include #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 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, Matrix> Tree; + + // getters and setters + + /** get the result of the search by expanding the node list */ + void get_results(ArrayList &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(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 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 candidate_nodes_; + + /** + * List of candidate points + */ + ArrayList 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