[mlpack] 194/324: R* tree split and descent heuristic.
Barak A. Pearlmutter
barak+git at cs.nuim.ie
Sun Aug 17 08:22:09 UTC 2014
This is an automated email from the git hooks/post-receive script.
bap pushed a commit to branch svn-trunk
in repository mlpack.
commit f23c0315be4d214957543b18372cec9a4526c722
Author: andrewmw94 <andrewmw94 at 9d5b8971-822b-0410-80eb-d18c1038ef23>
Date: Fri Jul 18 20:25:45 2014 +0000
R* tree split and descent heuristic.
git-svn-id: http://svn.cc.gatech.edu/fastlab/mlpack/trunk@16836 9d5b8971-822b-0410-80eb-d18c1038ef23
---
src/mlpack/core/tree/CMakeLists.txt | 2 +
src/mlpack/core/tree/rectangle_tree.hpp | 2 +
.../r_star_tree_descent_heuristic.hpp | 44 ++
.../r_star_tree_descent_heuristic_impl.hpp | 149 +++++++
.../core/tree/rectangle_tree/r_star_tree_split.hpp | 64 ++-
.../tree/rectangle_tree/r_star_tree_split_impl.hpp | 464 ++++++++++++++++++++-
.../r_tree_descent_heuristic_impl.hpp | 66 ++-
.../core/tree/rectangle_tree/r_tree_split.hpp | 3 +-
src/mlpack/methods/neighbor_search/allknn_main.cpp | 20 +-
src/mlpack/tests/rectangle_tree_test.cpp | 8 +-
10 files changed, 789 insertions(+), 33 deletions(-)
diff --git a/src/mlpack/core/tree/CMakeLists.txt b/src/mlpack/core/tree/CMakeLists.txt
index 43b6117..a4bd182 100644
--- a/src/mlpack/core/tree/CMakeLists.txt
+++ b/src/mlpack/core/tree/CMakeLists.txt
@@ -40,6 +40,8 @@ set(SOURCES
rectangle_tree/r_tree_split_impl.hpp
rectangle_tree/r_tree_descent_heuristic.hpp
rectangle_tree/r_tree_descent_heuristic_impl.hpp
+ rectangle_tree/r_star_tree_descent_heuristic.hpp
+ rectangle_tree/r_star_tree_descent_heuristic_impl.hpp
statistic.hpp
traversal_info.hpp
tree_traits.hpp
diff --git a/src/mlpack/core/tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree.hpp
index b7992e1..526b600 100644
--- a/src/mlpack/core/tree/rectangle_tree.hpp
+++ b/src/mlpack/core/tree/rectangle_tree.hpp
@@ -18,7 +18,9 @@
#include "rectangle_tree/dual_tree_traverser.hpp"
#include "rectangle_tree/dual_tree_traverser_impl.hpp"
#include "rectangle_tree/r_tree_split.hpp"
+//#include "rectangle_tree/r_star_tree_split.hpp"
#include "rectangle_tree/r_tree_descent_heuristic.hpp"
+#include "rectangle_tree/r_star_tree_descent_heuristic.hpp"
#include "rectangle_tree/traits.hpp"
#endif
diff --git a/src/mlpack/core/tree/rectangle_tree/r_star_tree_descent_heuristic.hpp b/src/mlpack/core/tree/rectangle_tree/r_star_tree_descent_heuristic.hpp
new file mode 100644
index 0000000..035b85f
--- /dev/null
+++ b/src/mlpack/core/tree/rectangle_tree/r_star_tree_descent_heuristic.hpp
@@ -0,0 +1,44 @@
+/**
+ * @file r_star_tree_descent_heuristic.hpp
+ * @author Andrew Wells
+ *
+ * Definition of RStarTreeDescentHeuristic, a class that chooses the best child of a node in
+ * an R tree when inserting a new point.
+ */
+#ifndef __MLPACK_CORE_TREE_RECTANGLE_TREE_R_STAR_TREE_DESCENT_HEURISTIC_HPP
+#define __MLPACK_CORE_TREE_RECTANGLE_TREE_R_STAR_TREE_DESCENT_HEURISTIC_HPP
+
+#include <mlpack/core.hpp>
+
+namespace mlpack {
+namespace tree /** Trees and tree-building procedures. */ {
+
+/**
+ * When descending a Rectangle tree to insert a point, we need to have a way to choose
+ * a child node when the point isn't enclosed by any of them. This heuristic is used to do so.
+ */
+class RStarTreeDescentHeuristic
+{
+ public:
+ /**
+ * Evaluate the node using a hueristic. The heuristic guarantees two things:
+ * 1. If point is contained in (or on) bound, the value returned is zero.
+ * 2. If the point is not contained in (or on) bound, the value returned is greater than zero.
+ *
+ * @param bound The bound used for the node that is being evaluated.
+ * @param point The point that is being inserted.
+ */
+ template<typename TreeType>
+ static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point);
+
+ template<typename TreeType>
+ static size_t ChooseDescentNode(const TreeType* node, const TreeType* insertedNode);
+};
+
+}; // namespace tree
+}; // namespace mlpack
+
+// Include implementation.
+#include "r_star_tree_descent_heuristic_impl.hpp"
+
+#endif
diff --git a/src/mlpack/core/tree/rectangle_tree/r_star_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/r_star_tree_descent_heuristic_impl.hpp
new file mode 100644
index 0000000..da6d425
--- /dev/null
+++ b/src/mlpack/core/tree/rectangle_tree/r_star_tree_descent_heuristic_impl.hpp
@@ -0,0 +1,149 @@
+/**
+ * @file r_star_tree_descent_heuristic_impl.hpp
+ * @author Andrew Wells
+ *
+ * Implementation of RStarTreeDescentHeuristic, a class that chooses the best child of a node in
+ * an R tree when inserting a new point.
+ */
+#ifndef __MLPACK_CORE_TREE_RECTANGLE_TREE_R_STAR_TREE_DESCENT_HEURISTIC_IMPL_HPP
+#define __MLPACK_CORE_TREE_RECTANGLE_TREE_R_STAR_TREE_DESCENT_HEURISTIC_IMPL_HPP
+
+#include "r_star_tree_descent_heuristic.hpp"
+
+namespace mlpack {
+namespace tree {
+
+template<typename TreeType>
+inline size_t RStarTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, const arma::vec& point)
+{
+ bool tiedOne = false;
+ std::vector<double> originalScores(node->NumChildren());
+ double origMinScore = DBL_MAX;
+
+ if (node->Child(0)->IsLeaf()) { // If its children are leaf nodes, use minimum overlap to choose.
+ double bestIndex = 0;
+
+ for (size_t i = 0; i < node->NumChildren(); i++) {
+ double sc = 0;
+ for (size_t j = 0; j < node->NumChildren(); j++) {
+ if (j != i) {
+ double overlap = 1.0;
+ double newOverlap = 1.0;
+ for (size_t k = 0; k < node->Bound().Dim(); k++) {
+ double newHigh = std::max(point[k], node->Child(i)->Bound()[k].Hi());
+ double newLow = std::min(point[k], node->Child(i)->Bound()[k].Lo());
+ overlap *= node->Child(i)->Bound()[k].Hi() < node->Child(j)->Bound()[k].Lo() || node->Child(i)->Bound()[k].Lo() > node->Child(j)->Bound()[k].Hi() ? 0 : std::min(node->Child(i)->Bound()[k].Hi(), node->Child(j)->Bound()[k].Hi()) - std::max(node->Child(i)->Bound()[k].Lo(), node->Child(j)->Bound()[k].Lo());
+ newOverlap *= newHigh < node->Child(j)->Bound()[k].Lo() || newLow > node->Child(j)->Bound()[k].Hi() ? 0 : std::min(newHigh, node->Child(j)->Bound()[k].Hi()) - std::max(newLow, node->Child(j)->Bound()[k].Lo());
+ }
+ sc += newOverlap - overlap;
+ }
+ }
+ originalScores[i] = sc;
+ if (sc < origMinScore) {
+ origMinScore = sc;
+ bestIndex = i;
+ } else if (sc == origMinScore)
+ tiedOne = true;
+ }
+
+ if (!tiedOne)
+ return bestIndex;
+ }
+
+ // We do this if it is not on the second level or if there was a tie.
+ std::vector<double> scores(node->NumChildren());
+ if(tiedOne) { // If the first heuristic was tied, we need to eliminate garbage values.
+ for(size_t i = 0; i < scores.size(); i++)
+ scores[i] = DBL_MAX;
+ }
+ std::vector<int> vols(node->NumChildren());
+ double minScore = DBL_MAX;
+ int bestIndex = 0;
+ bool tied = false;
+
+ for (size_t i = 0; i < node->NumChildren(); i++) {
+ if (!tiedOne || originalScores[i] == origMinScore) {
+ double v1 = 1.0;
+ double v2 = 1.0;
+ for (size_t j = 0; j < node->Bound().Dim(); j++) {
+ v1 *= node->Child(i)->Bound()[j].Width();
+ v2 *= node->Child(i)->Bound()[j].Contains(point[j]) ? node->Child(i)->Bound()[j].Width() : (node->Child(i)->Bound()[j].Hi() < point[j] ? (point[j] - node->Child(i)->Bound()[j].Lo()) :
+ (node->Child(i)->Bound()[j].Hi() - point[j]));
+ }
+ assert(v2 - v1 >= 0);
+ vols[i] = v1;
+ scores[i] = v2 - v1;
+ if (v2 - v1 < minScore) {
+ minScore = v2 - v1;
+ bestIndex = i;
+ } else if (v2 - v1 == minScore)
+ tied = true;
+ }
+ }
+ if (tied) { // We break ties by choosing the smallest bound.
+ double minVol = DBL_MAX;
+ bestIndex = 0;
+ for (int i = 0; i < scores.size(); i++) {
+ if (scores[i] == minScore) {
+ if (vols[i] < minVol) {
+ minVol = vols[i];
+ bestIndex = i;
+ }
+ }
+ }
+ }
+
+ return bestIndex;
+}
+
+/**
+ * We simplify this to the same code as is used in the regular R tree, since the inserted node should always be above
+ * the leaf level. If the tree is eventually changed to support rectangles, this could be changed to match the above code;
+ * however, the paper's explanation for their algorithm seems to indicate the above is more for points than for rectangles.
+ */
+template<typename TreeType>
+inline size_t RStarTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, const TreeType* insertedNode)
+{
+ std::vector<double> scores(node->NumChildren());
+ std::vector<int> vols(node->NumChildren());
+ double minScore = DBL_MAX;
+ int bestIndex = 0;
+ bool tied = false;
+
+ for (size_t i = 0; i < node->NumChildren(); i++) {
+ double v1 = 1.0;
+ double v2 = 1.0;
+ for (size_t j = 0; j < node->Child(i)->Bound().Dim(); j++) {
+ v1 *= node->Child(i)->Bound()[j].Width();
+ v2 *= node->Child(i)->Bound()[j].Contains(insertedNode->Bound()[j]) ? node->Child(i)->Bound()[j].Width() :
+ (insertedNode->Bound()[j].Contains(node->Child(i)->Bound()[j]) ? insertedNode->Bound()[j].Width() : (insertedNode->Bound()[j].Lo() < node->Child(i)->Bound()[j].Lo() ? (node->Child(i)->Bound()[j].Hi() - insertedNode->Bound()[j].Lo()) : (insertedNode->Bound()[j].Hi() - node->Child(i)->Bound()[j].Lo())));
+ }
+ assert(v2 - v1 >= 0);
+ vols[i] = v1;
+ scores[i] = v2-v1;
+ if (v2 - v1 < minScore) {
+ minScore = v2 - v1;
+ bestIndex = i;
+ } else if(v2 - v1 == minScore)
+ tied = true;
+ }
+ if(tied) { // We break ties by choosing the smallest bound.
+ double minVol = DBL_MAX;
+ bestIndex = 0;
+ for(int i = 0; i < scores.size(); i++) {
+ if(scores[i] == minScore) {
+ if(vols[i] < minVol) {
+ minVol = vols[i];
+ bestIndex = i;
+ }
+ }
+ }
+ }
+
+ return bestIndex;
+}
+
+}; // namespace tree
+}; // namespace mlpack
+
+#endif
\ No newline at end of file
diff --git a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp
index 8d1c8b6..646eba9 100644
--- a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp
+++ b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split.hpp
@@ -1 +1,63 @@
-
+/**
+ * @file r_tree_star_split.hpp
+ * @author Andrew Wells
+ *
+ * Defintion of the RStarTreeSplit class, a class that splits the nodes of an R tree, starting
+ * at a leaf node and moving upwards if necessary.
+ */
+#ifndef __MLPACK_CORE_TREE_RECTANGLE_TREE_R_STAR_TREE_SPLIT_HPP
+#define __MLPACK_CORE_TREE_RECTANGLE_TREE_R_STAR_TREE_SPLIT_HPP
+
+#include <mlpack/core.hpp>
+
+namespace mlpack {
+namespace tree /** Trees and tree-building procedures. */ {
+
+/**
+ * A Rectangle Tree has new points inserted at the bottom. When these
+ * nodes overflow, we split them, moving up the tree and splitting nodes
+ * as necessary.
+ */
+template<typename DescentType,
+ typename StatisticType,
+ typename MatType>
+class RStarTreeSplit
+{
+public:
+
+/**
+ * Split a leaf node using the algorithm described in "The R*-tree: An Efficient and Robust Access method
+ * for Points and Rectangles." If necessary, this split will propagate
+ * upwards through the tree.
+ */
+static void SplitLeafNode(RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* tree);
+
+/**
+ * Split a non-leaf node using the "default" algorithm. If this is a root node, the
+ * tree increases in depth.
+ */
+static bool SplitNonLeafNode(RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* tree);
+
+private:
+/**
+ * Class to allow for faster sorting.
+ */
+class sortStruct {
+ double d;
+ int n;
+};
+
+/**
+ * Comparator for sorting with sortStruct.
+ */
+static bool structComp(const sortStruct& s1, const sortStruct& s2) {
+ return s1.d < s2.d;
+}
+
+}; // namespace tree
+}; // namespace mlpack
+
+// Include implementation
+#include "r_star_tree_split_impl.hpp"
+
+#endif
diff --git a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp
index 8d1c8b6..b80d8c9 100644
--- a/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp
+++ b/src/mlpack/core/tree/rectangle_tree/r_star_tree_split_impl.hpp
@@ -1 +1,463 @@
-
+/**
+ * @file r_star_tree_split_impl.hpp
+ * @author Andrew Wells
+ *
+ * Implementation of class (RStarTreeSplit) to split a RectangleTree.
+ */
+#ifndef __MLPACK_CORE_TREE_RECTANGLE_TREE_R_STAR_TREE_SPLIT_IMPL_HPP
+#define __MLPACK_CORE_TREE_RECTANGLE_TREE_R_STAR_TREE_SPLIT_IMPL_HPP
+
+#include "r_star_tree_split.hpp"
+#include "rectangle_tree.hpp"
+#include <mlpack/core/math/range.hpp>
+
+namespace mlpack {
+namespace tree {
+
+/**
+ * We call GetPointSeeds to get the two points which will be the initial points in the new nodes
+ * We then call AssignPointDestNode to assign the remaining points to the two new nodes.
+ * Finally, we delete the old node and insert the new nodes into the tree, spliting the parent
+ * if necessary.
+ */
+template<typename DescentType,
+typename StatisticType,
+typename MatType>
+void RStarTreeSplit<DescentType, StatisticType, MatType>::SplitLeafNode(
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* tree)
+{
+ // If we are splitting the root node, we need will do things differently so that the constructor
+ // and other methods don't confuse the end user by giving an address of another node.
+ if (tree->Parent() == NULL) {
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* copy =
+ new RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>(*tree); // We actually want to copy this way. Pointers and everything.
+ copy->Parent() = tree;
+ tree->Count() = 0;
+ tree->NullifyData();
+ tree->Child((tree->NumChildren())++) = copy; // Because this was a leaf node, numChildren must be 0.
+ assert(tree->NumChildren() == 1);
+ RStarTreeSplit<DescentType, StatisticType, MatType>::SplitLeafNode(copy);
+ return;
+ }
+
+ int bestOverlapIndexOnBestAxis;
+ int bestAreaIndexOnBestAxis;
+ bool tiedOnOverlap = false;
+ int bestAxis = 0;
+ double bestAxisScore = DBL_MAX;
+ for(int j = 0; j < tree->Bound().Dim(); j++) {
+ double axisScore = 0.0;
+ // Since we only have points in the leaf nodes, we only need to sort once.
+ std::vector<sortStruct> sorted(tree->Count());
+ for(int i = 0; i < sorted.size(); i++) {
+ sorted[i].d = tree->LocalDataset().col(i)[j];
+ sorted[i].n = i;
+ }
+
+ std::sort(sorted.begin(), sorted.end(), structComp);
+
+ // We'll store each of the three scores for each distribution.
+ std::vector<double> areas(tree->MaxLeafSize() - 2*tree->MinLeafSize() + 2);
+ std::vector<double> margins(tree->MaxLeafSize() - 2*tree->MinLeafSize() + 2);
+ std::vector<double> overlapedAreas(tree->MaxLeafSize() - 2*tree->MinLeafSize() + 2);
+ for(int i = 0; i < areas.size(); i++) {
+ areas[i] = 0.0;
+ margins[i] = 0.0;
+ overlapedAreas[i] = 0.0;
+ }
+
+ for(int i = 0; i < areas.size(); i++) {
+ // The ith arrangement is obtained by placing the first tree->MinLeafSize() + i
+ // points in one rectangle and the rest in another. Then we calculate the three
+ // scores for that distribution.
+
+ int cutOff = tree->MinLeafSize() + i;
+ // We'll calculate the max and min in each dimension by hand to save time.
+ std::vector<double> maxG1(tree->Bound().Dim());
+ std::vector<double> minG1(maxG1.size());
+ std::vector<double> maxG2(maxG1.size());
+ std::vector<double> minG2(maxG1.size());
+ for(int k = 0; k < tree->Bound().Dim(); k++) {
+ minG1[k] = maxG1[k] = tree->LocalDataset().col(sorted[0].n)[k];
+ minG2[k] = maxG2[k] = tree->LocalDataset().col(sorted[sorted.size()-1])[k];
+ for(int l = 1; l < tree->Count()-1; l++) {
+ if(l < cutOff) {
+ if(tree->LocalDataset().col(sorted[l].n)[k] < minG1[k])
+ minG1[k] = tree->LocalDataset().col(sorted[l].n)[k];
+ else if(tree->LocalDataset().col(sorted[l].n)[k] > maxG1[k])
+ maxG1[k] = tree->LocalDataset().col(sorted[l].n)[k];
+ } else {
+ if(tree->LocalDataset().col(sorted[l].n)[k] < minG2[k])
+ minG2[k] = tree->LocalDataset().col(sorted[l].n)[k];
+ else if(tree->LocalDataset().col(sorted[l].n)[k] > maxG2[k])
+ maxG2[k] = tree->LocalDataset().col(sorted[l].n)[k];
+ }
+ }
+ }
+ double area1 = 1.0, area2 = 1.0;
+ double oArea = 1.0;
+ for(int k = 0; k < maxG1.size(); k++) {
+ margins[i] += maxG1[k] - minG1[k] + maxG2[k] - minG2[k];
+ area1 *= maxG1[k] - minG1[k];
+ area2 *= maxG2[k] - minG2[k];
+ oArea *= maxG1[k] < minG2[k] || maxG2[k] < minG1[k] ? 0.0 : std::min(maxG1[k], maxG2[k]) - std::max(minG1[k], minG2[k]);
+ }
+ areas[i] += area1 + area2;
+ overlapedAreas[i] += oArea;
+ axisScore += margins[i];
+ }
+ if(axisScore < bestAxisScore) {
+ bestAxisScore = axisScore;
+ bestAxis = j;
+ double bestOverlapIndexOnBestAxis = 0;
+ double bestAreaIndexOnBestAxis = 0;
+ for(int i = 1; i < areas.size(); i++) {
+ if(overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis]) {
+ tiedOnOverlap = false;
+ bestAreaIndexOnBestAxis = i;
+ bestOverlapIndexOnBestAxis = i;
+ }
+ else if(overlapedAreas[i] == overlapedAreas[bestOverlapIndexOnBestAxis]) {
+ tiedOnOverlap = true;
+ if(areas[i] < areas[bestAreaIndexOnBestAxis])
+ bestAreaIndexOnBestAxis = i;
+ }
+ }
+ }
+ }
+
+
+ std::vector<sortStruct> sorted(tree->Count());
+ for(int i = 0; i < sorted.size(); i++) {
+ sorted[i].d = tree->LocalDataset().col(i)[bestAxis];
+ sorted[i].n = i;
+ }
+
+ std::sort(sorted.begin(), sorted.end(), structComp);
+
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType> *treeOne = new
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>(tree->Parent());
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType> *treeTwo = new
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>(tree->Parent());
+
+ if(tiedOnOverlap) {
+ for(int i = 0; i < tree.Count(); i++) {
+ if(i < bestAreaIndexOnBestAxis)
+ treeOne->InsertPoint(tree->Points()[sorted[i].n]);
+ else
+ treeTwo->InsertPoint(tree->Points()[sorted[i].n]);
+ }
+ } else {
+ for(int i = 0; i < tree.Count(); i++) {
+ if(i < bestOverlapIndexOnBestAxis)
+ treeOne->InsertPoint(tree->Points()[sorted[i].n]);
+ else
+ treeTwo->InsertPoint(tree->Points()[sorted[i].n]);
+ }
+ }
+
+ //Remove this node and insert treeOne and treeTwo
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* par = tree->Parent();
+ int index = 0;
+ for (int i = 0; i < par->NumChildren(); i++) {
+ if (par->Child(i) == tree) {
+ index = i;
+ break;
+ }
+ }
+ par->Child(index) = treeOne;
+ par->Child(par->NumChildren()++) = treeTwo;
+
+ // we only add one at a time, so we should only need to test for equality
+ // just in case, we use an assert.
+ assert(par->NumChildren() <= par->MaxNumChildren());
+ if (par->NumChildren() == par->MaxNumChildren()) {
+ SplitNonLeafNode(par);
+ }
+
+ assert(treeOne->Parent()->NumChildren() < treeOne->MaxNumChildren());
+ assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren());
+ assert(treeTwo->Parent()->NumChildren() < treeTwo->MaxNumChildren());
+ assert(treeTwo->Parent()->NumChildren() >= treeTwo->MinNumChildren());
+
+ tree->SoftDelete();
+
+ return;
+}
+
+/**
+ * We call GetBoundSeeds to get the two new nodes that this one will be broken
+ * into. Then we call AssignNodeDestNode to move the children of this node
+ * into either of those two nodes. Finally, we delete the now unused information
+ * and recurse up the tree if necessary. We don't need to worry about the bounds
+ * higher up the tree because they were already updated if necessary.
+ */
+template<typename DescentType,
+typename StatisticType,
+typename MatType>
+bool RStarTreeSplit<DescentType, StatisticType, MatType>::SplitNonLeafNode(
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* tree)
+{
+ // If we are splitting the root node, we need will do things differently so that the constructor
+ // and other methods don't confuse the end user by giving an address of another node.
+ if (tree->Parent() == NULL) {
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* copy =
+ new RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>(*tree); // We actually want to copy this way. Pointers and everything.
+ copy->Parent() = tree;
+ tree->NumChildren() = 0;
+ tree->NullifyData();
+ tree->Child((tree->NumChildren())++) = copy;
+ RStarTreeSplit<DescentType, StatisticType, MatType>::SplitNonLeafNode(copy);
+ return true;
+ }
+
+ int bestOverlapIndexOnBestAxis;
+ int bestAreaIndexOnBestAxis;
+ bool tiedOnOverlap = false;
+ bool lowIsBest = true;
+ int bestAxis = 0;
+ double bestAxisScore = DBL_MAX;
+ for(int j = 0; j < tree->Bound().Dim(); j++) {
+ double axisScore = 0.0;
+
+ // We'll do Bound().Lo() now and use Bound().Hi() later.
+ std::vector<sortStruct> sorted(tree->Count());
+ for(int i = 0; i < sorted.size(); i++) {
+ sorted[i].d = tree->Child(i)->Bound()[j].Lo();
+ sorted[i].n = i;
+ }
+
+ std::sort(sorted.begin(), sorted.end(), structComp);
+
+ // We'll store each of the three scores for each distribution.
+ std::vector<double> areas(tree->MaxNumChildren() - 2*tree->MinNumChildren() + 2);
+ std::vector<double> margins(tree->MaxNumChildren() - 2*tree->MinNumChildren() + 2);
+ std::vector<double> overlapedAreas(tree->MaxNumChildren() - 2*tree->MinNumChildren() + 2);
+ for(int i = 0; i < areas.size(); i++) {
+ areas[i] = 0.0;
+ margins[i] = 0.0;
+ overlapedAreas[i] = 0.0;
+ }
+
+ for(int i = 0; i < areas.size(); i++) {
+ // The ith arrangement is obtained by placing the first tree->MinNumChildren() + i
+ // points in one rectangle and the rest in another. Then we calculate the three
+ // scores for that distribution.
+
+ int cutOff = tree->MinNumChildren() + i;
+ // We'll calculate the max and min in each dimension by hand to save time.
+ std::vector<double> maxG1(tree->Bound().Dim());
+ std::vector<double> minG1(maxG1.size());
+ std::vector<double> maxG2(maxG1.size());
+ std::vector<double> minG2(maxG1.size());
+ for(int k = 0; k < tree->Bound().Dim(); k++) {
+ minG1[k] = tree->Child(sorted[0].n)->Bound()[k].Lo();
+ maxG1[k] = tree->Child(sorted[0].n)->Bound()[k].Hi();
+ minG2[k] = tree->Child(sorted[sorted.size()-1])->Bound()[k].Lo();
+ maxG2[k] = tree->Child(sorted[sorted.size()-1])->Bound()[k].Hi();
+ for(int l = 1; l < tree->Count()-1; l++) {
+ if(l < cutOff) {
+ if(tree->Child(sorted[l].n)->Bound()[k].Lo() < minG1[k])
+ minG1[k] = tree->Child(sorted[l].n)->Bound()[k].Lo();
+ else if(tree->Child(sorted[l].n)->Bound()[k].Hi() > maxG1[k])
+ maxG1[k] = tree->Child(sorted[l].n)->Bound()[k].Hi();
+ } else {
+ if(tree->Child(sorted[l].n)->Bound()[k].Lo() < minG2[k])
+ minG2[k] = tree->Child(sorted[l].n)->Bound()[k].Lo();
+ else if(tree->Child(sorted[l].n)->Bound()[k].Hi() > maxG2[k])
+ maxG2[k] = tree->Child(sorted[l].n)->Bound()[k].Hi();
+ }
+ }
+ }
+ double area1 = 1.0, area2 = 1.0;
+ double oArea = 1.0;
+ for(int k = 0; k < maxG1.size(); k++) {
+ margins[i] += maxG1[k] - minG1[k] + maxG2[k] - minG2[k];
+ area1 *= maxG1[k] - minG1[k];
+ area2 *= maxG2[k] - minG2[k];
+ oArea *= maxG1[k] < minG2[k] || maxG2[k] < minG1[k] ? 0.0 : std::min(maxG1[k], maxG2[k]) - std::max(minG1[k], minG2[k]);
+ }
+ areas[i] += area1 + area2;
+ overlapedAreas[i] += oArea;
+ axisScore += margins[i];
+ }
+ if(axisScore < bestAxisScore) {
+ bestAxisScore = axisScore;
+ bestAxis = j;
+ double bestOverlapIndexOnBestAxis = 0;
+ double bestAreaIndexOnBestAxis = 0;
+ for(int i = 1; i < areas.size(); i++) {
+ if(overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis]) {
+ tiedOnOverlap = false;
+ bestAreaIndexOnBestAxis = i;
+ bestOverlapIndexOnBestAxis = i;
+ }
+ else if(overlapedAreas[i] == overlapedAreas[bestOverlapIndexOnBestAxis]) {
+ tiedOnOverlap = true;
+ if(areas[i] < areas[bestAreaIndexOnBestAxis])
+ bestAreaIndexOnBestAxis = i;
+ }
+ }
+ }
+ }
+ //Now we do the same thing using Bound().Hi() and choose the best of the two.
+ for(int j = 0; j < tree->Bound().Dim(); j++) {
+ double axisScore = 0.0;
+
+ // We'll do Bound().Lo() now and use Bound().Hi() later.
+ std::vector<sortStruct> sorted(tree->Count());
+ for(int i = 0; i < sorted.size(); i++) {
+ sorted[i].d = tree->Child(i)->Bound()[j].Hi();
+ sorted[i].n = i;
+ }
+
+ std::sort(sorted.begin(), sorted.end(), structComp);
+
+ // We'll store each of the three scores for each distribution.
+ std::vector<double> areas(tree->MaxNumChildren() - 2*tree->MinNumChildren() + 2);
+ std::vector<double> margins(tree->MaxNumChildren() - 2*tree->MinNumChildren() + 2);
+ std::vector<double> overlapedAreas(tree->MaxNumChildren() - 2*tree->MinNumChildren() + 2);
+ for(int i = 0; i < areas.size(); i++) {
+ areas[i] = 0.0;
+ margins[i] = 0.0;
+ overlapedAreas[i] = 0.0;
+ }
+
+ for(int i = 0; i < areas.size(); i++) {
+ // The ith arrangement is obtained by placing the first tree->MinNumChildren() + i
+ // points in one rectangle and the rest in another. Then we calculate the three
+ // scores for that distribution.
+
+ int cutOff = tree->MinNumChildren() + i;
+ // We'll calculate the max and min in each dimension by hand to save time.
+ std::vector<double> maxG1(tree->Bound().Dim());
+ std::vector<double> minG1(maxG1.size());
+ std::vector<double> maxG2(maxG1.size());
+ std::vector<double> minG2(maxG1.size());
+ for(int k = 0; k < tree->Bound().Dim(); k++) {
+ minG1[k] = tree->Child(sorted[0].n)->Bound()[k].Lo();
+ maxG1[k] = tree->Child(sorted[0].n)->Bound()[k].Hi();
+ minG2[k] = tree->Child(sorted[sorted.size()-1])->Bound()[k].Lo();
+ maxG2[k] = tree->Child(sorted[sorted.size()-1])->Bound()[k].Hi();
+ for(int l = 1; l < tree->Count()-1; l++) {
+ if(l < cutOff) {
+ if(tree->Child(sorted[l].n)->Bound()[k].Lo() < minG1[k])
+ minG1[k] = tree->Child(sorted[l].n)->Bound()[k].Lo();
+ else if(tree->Child(sorted[l].n)->Bound()[k].Hi() > maxG1[k])
+ maxG1[k] = tree->Child(sorted[l].n)->Bound()[k].Hi();
+ } else {
+ if(tree->Child(sorted[l].n)->Bound()[k].Lo() < minG2[k])
+ minG2[k] = tree->Child(sorted[l].n)->Bound()[k].Lo();
+ else if(tree->Child(sorted[l].n)->Bound()[k].Hi() > maxG2[k])
+ maxG2[k] = tree->Child(sorted[l].n)->Bound()[k].Hi();
+ }
+ }
+ }
+ double area1 = 1.0, area2 = 1.0;
+ double oArea = 1.0;
+ for(int k = 0; k < maxG1.size(); k++) {
+ margins[i] += maxG1[k] - minG1[k] + maxG2[k] - minG2[k];
+ area1 *= maxG1[k] - minG1[k];
+ area2 *= maxG2[k] - minG2[k];
+ oArea *= maxG1[k] < minG2[k] || maxG2[k] < minG1[k] ? 0.0 : std::min(maxG1[k], maxG2[k]) - std::max(minG1[k], minG2[k]);
+ }
+ areas[i] += area1 + area2;
+ overlapedAreas[i] += oArea;
+ axisScore += margins[i];
+ }
+ if(axisScore < bestAxisScore) {
+ bestAxisScore = axisScore;
+ bestAxis = j;
+ lowIsBest = false;
+ double bestOverlapIndexOnBestAxis = 0;
+ double bestAreaIndexOnBestAxis = 0;
+ for(int i = 1; i < areas.size(); i++) {
+ if(overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis]) {
+ tiedOnOverlap = false;
+ bestAreaIndexOnBestAxis = i;
+ bestOverlapIndexOnBestAxis = i;
+ }
+ else if(overlapedAreas[i] == overlapedAreas[bestOverlapIndexOnBestAxis]) {
+ tiedOnOverlap = true;
+ if(areas[i] < areas[bestAreaIndexOnBestAxis])
+ bestAreaIndexOnBestAxis = i;
+ }
+ }
+ }
+ }
+
+
+
+
+ std::vector<sortStruct> sorted(tree->NumChildren());
+ if(lowIsBest) {
+ for(int i = 0; i < sorted.size(); i++) {
+ sorted[i].d = tree->Child()->Bound().Lo()[bestAxis];
+ sorted[i].n = i;
+ }
+ } else {
+ for(int i = 0; i < sorted.size(); i++) {
+ sorted[i].d = tree->Child()->Bound().Hi()[bestAxis];
+ sorted[i].n = i;
+ }
+ }
+
+ std::sort(sorted.begin(), sorted.end(), structComp);
+
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType> *treeOne = new
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>(tree->Parent());
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType> *treeTwo = new
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>(tree->Parent());
+
+ if(tiedOnOverlap) {
+ for(int i = 0; i < tree.Count(); i++) {
+ if(i < bestAreaIndexOnBestAxis)
+ treeOne->InsertPoint(tree->Points()[sorted[i].n]);
+ else
+ treeTwo->InsertPoint(tree->Points()[sorted[i].n]);
+ }
+ } else {
+ for(int i = 0; i < tree.Count(); i++) {
+ if(i < bestOverlapIndexOnBestAxis)
+ treeOne->InsertPoint(tree->Points()[sorted[i].n]);
+ else
+ treeTwo->InsertPoint(tree->Points()[sorted[i].n]);
+ }
+ }
+
+ //Remove this node and insert treeOne and treeTwo
+ RectangleTree<RStarTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* par = tree->Parent();
+ int index = 0;
+ for (int i = 0; i < par->NumChildren(); i++) {
+ if (par->Child(i) == tree) {
+ index = i;
+ break;
+ }
+ }
+ par->Child(index) = treeOne;
+ par->Child(par->NumChildren()++) = treeTwo;
+
+ // we only add one at a time, so we should only need to test for equality
+ // just in case, we use an assert.
+ assert(par->NumChildren() <= par->MaxNumChildren());
+ if (par->NumChildren() == par->MaxNumChildren()) {
+ SplitNonLeafNode(par);
+ }
+
+ assert(treeOne->Parent()->NumChildren() < treeOne->MaxNumChildren());
+ assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren());
+ assert(treeTwo->Parent()->NumChildren() < treeTwo->MaxNumChildren());
+ assert(treeTwo->Parent()->NumChildren() >= treeTwo->MinNumChildren());
+
+ tree->SoftDelete();
+
+ return false;
+}
+
+
+
+
+}; // namespace tree
+}; // namespace mlpack
+
+#endif
diff --git a/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic_impl.hpp
index 3d61c75..337e232 100644
--- a/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic_impl.hpp
+++ b/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic_impl.hpp
@@ -13,51 +13,87 @@
namespace mlpack {
namespace tree {
-// Return the increase in volume required when inserting point into bound.
-
template<typename TreeType>
inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, const arma::vec& point)
{
+ std::vector<double> scores(node->NumChildren());
+ std::vector<int> vols(node->NumChildren());
double minScore = DBL_MAX;
int bestIndex = 0;
+ bool tied = false;
- for (size_t j = 0; j < node->NumChildren(); j++) {
+ for (size_t i = 0; i < node->NumChildren(); i++) {
double v1 = 1.0;
double v2 = 1.0;
- for (size_t i = 0; i < node->Child(j)->Bound().Dim(); i++) {
- v1 *= node->Child(j)->Bound()[i].Width();
- v2 *= node->Child(j)->Bound()[i].Contains(point[i]) ? node->Child(j)->Bound()[i].Width() : (node->Child(j)->Bound()[i].Hi() < point[i] ? (point[i] - node->Child(j)->Bound()[i].Lo()) :
- (node->Child(j)->Bound()[i].Hi() - point[i]));
+ for (size_t j = 0; j < node->Child(i)->Bound().Dim(); j++) {
+ v1 *= node->Child(i)->Bound()[j].Width();
+ v2 *= node->Child(i)->Bound()[j].Contains(point[j]) ? node->Child(i)->Bound()[j].Width() : (node->Child(i)->Bound()[j].Hi() < point[j] ? (point[j] - node->Child(i)->Bound()[j].Lo()) :
+ (node->Child(i)->Bound()[j].Hi() - point[j]));
}
assert(v2 - v1 >= 0);
+ vols[i] = v1;
+ scores[i] = v2-v1;
if (v2 - v1 < minScore) {
minScore = v2 - v1;
- bestIndex = j;
+ bestIndex = i;
+ } else if(v2 - v1 == minScore)
+ tied = true;
+ }
+ if(tied) { // We break ties by choosing the smallest bound.
+ double minVol = DBL_MAX;
+ bestIndex = 0;
+ for(int i = 0; i < scores.size(); i++) {
+ if(scores[i] == minScore) {
+ if(vols[i] < minVol) {
+ minVol = vols[i];
+ bestIndex = i;
+ }
+ }
}
}
+
return bestIndex;
}
template<typename TreeType>
inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, const TreeType* insertedNode)
{
+ std::vector<double> scores(node->NumChildren());
+ std::vector<int> vols(node->NumChildren());
double minScore = DBL_MAX;
int bestIndex = 0;
-
- for (size_t j = 0; j < node->NumChildren(); j++) {
+ bool tied = false;
+
+ for (size_t i = 0; i < node->NumChildren(); i++) {
double v1 = 1.0;
double v2 = 1.0;
- for (size_t i = 0; i < node->Child(j)->Bound().Dim(); i++) {
- v1 *= node->Child(j)->Bound()[i].Width();
- v2 *= node->Child(j)->Bound()[i].Contains(insertedNode->Bound()[i]) ? node->Child(j)->Bound()[i].Width() :
- (insertedNode->Bound()[i].Contains(node->Child(j)->Bound()[i]) ? insertedNode->Bound()[i].Width() : (insertedNode->Bound()[i].Lo() < node->Child(j)->Bound()[i].Lo() ? (node->Child(j)->Bound()[i].Hi() - insertedNode->Bound()[i].Lo()) : (insertedNode->Bound()[i].Hi() - node->Child(j)->Bound()[i].Lo())));
+ for (size_t j = 0; j < node->Child(i)->Bound().Dim(); j++) {
+ v1 *= node->Child(i)->Bound()[j].Width();
+ v2 *= node->Child(i)->Bound()[j].Contains(insertedNode->Bound()[j]) ? node->Child(i)->Bound()[j].Width() :
+ (insertedNode->Bound()[j].Contains(node->Child(i)->Bound()[j]) ? insertedNode->Bound()[j].Width() : (insertedNode->Bound()[j].Lo() < node->Child(i)->Bound()[j].Lo() ? (node->Child(i)->Bound()[j].Hi() - insertedNode->Bound()[j].Lo()) : (insertedNode->Bound()[j].Hi() - node->Child(i)->Bound()[j].Lo())));
}
assert(v2 - v1 >= 0);
+ vols[i] = v1;
+ scores[i] = v2-v1;
if (v2 - v1 < minScore) {
minScore = v2 - v1;
- bestIndex = j;
+ bestIndex = i;
+ } else if(v2 - v1 == minScore)
+ tied = true;
+ }
+ if(tied) { // We break ties by choosing the smallest bound.
+ double minVol = DBL_MAX;
+ bestIndex = 0;
+ for(int i = 0; i < scores.size(); i++) {
+ if(scores[i] == minScore) {
+ if(vols[i] < minVol) {
+ minVol = vols[i];
+ bestIndex = i;
+ }
+ }
}
}
+
return bestIndex;
}
diff --git a/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp
index e57ec63..3b30834 100644
--- a/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp
+++ b/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp
@@ -27,8 +27,7 @@ public:
/**
* Split a leaf node using the "default" algorithm. If necessary, this split will propagate
- * upwards through the tree. The methods for splitting non-leaf nodes are private since
- * they should only be called if a leaf node overflows.
+ * upwards through the tree.
*/
static void SplitLeafNode(RectangleTree<RTreeSplit<DescentType, StatisticType, MatType>, DescentType, StatisticType, MatType>* tree);
diff --git a/src/mlpack/methods/neighbor_search/allknn_main.cpp b/src/mlpack/methods/neighbor_search/allknn_main.cpp
index 83a0be9..58de810 100644
--- a/src/mlpack/methods/neighbor_search/allknn_main.cpp
+++ b/src/mlpack/methods/neighbor_search/allknn_main.cpp
@@ -272,8 +272,8 @@ int main(int argc, char *argv[])
// Because we may construct it differently, we need a pointer.
NeighborSearch<NearestNeighborSort, metric::LMetric<2, true>,
- RectangleTree<tree::RTreeSplit<tree::RTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
- tree::RTreeDescentHeuristic,
+ RectangleTree<tree::RTreeSplit<tree::RStarTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
+ tree::RStarTreeDescentHeuristic,
NeighborSearchStat<NearestNeighborSort>,
arma::mat> >* allknn = NULL;
@@ -282,14 +282,14 @@ int main(int argc, char *argv[])
Log::Info << "Building reference tree..." << endl;
Timer::Start("tree_building");
- RectangleTree<tree::RTreeSplit<tree::RTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
- tree::RTreeDescentHeuristic,
+ RectangleTree<tree::RTreeSplit<tree::RStarTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
+ tree::RStarTreeDescentHeuristic,
NeighborSearchStat<NearestNeighborSort>,
arma::mat>
refTree(referenceData, leafSize, leafSize/3, 5, 2, 0);
- RectangleTree<tree::RTreeSplit<tree::RTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
- tree::RTreeDescentHeuristic,
+ RectangleTree<tree::RTreeSplit<tree::RStarTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
+ tree::RStarTreeDescentHeuristic,
NeighborSearchStat<NearestNeighborSort>,
arma::mat>*
queryTree = NULL; // Empty for now.
@@ -302,16 +302,16 @@ int main(int argc, char *argv[])
<< queryData.n_rows << " x " << queryData.n_cols << ")." << endl;
allknn = new NeighborSearch<NearestNeighborSort, metric::LMetric<2, true>,
- RectangleTree<tree::RTreeSplit<tree::RTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
- tree::RTreeDescentHeuristic,
+ RectangleTree<tree::RTreeSplit<tree::RStarTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
+ tree::RStarTreeDescentHeuristic,
NeighborSearchStat<NearestNeighborSort>,
arma::mat> >(&refTree, queryTree,
referenceData, queryData, singleMode);
} else
{
allknn = new NeighborSearch<NearestNeighborSort, metric::LMetric<2, true>,
- RectangleTree<tree::RTreeSplit<tree::RTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
- tree::RTreeDescentHeuristic,
+ RectangleTree<tree::RTreeSplit<tree::RStarTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
+ tree::RStarTreeDescentHeuristic,
NeighborSearchStat<NearestNeighborSort>,
arma::mat> >(&refTree,
referenceData, singleMode);
diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp
index 3903d0b..5bf6208 100644
--- a/src/mlpack/tests/rectangle_tree_test.cpp
+++ b/src/mlpack/tests/rectangle_tree_test.cpp
@@ -209,15 +209,15 @@ BOOST_AUTO_TEST_CASE(SingleTreeTraverserTest) {
arma::Mat<size_t> neighbors2;
arma::mat distances2;
- RectangleTree<tree::RTreeSplit<tree::RTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
- tree::RTreeDescentHeuristic,
+ RectangleTree<tree::RTreeSplit<tree::RStarTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
+ tree::RStarTreeDescentHeuristic,
NeighborSearchStat<NearestNeighborSort>,
arma::mat> RTree(dataset, 20, 6, 5, 2, 0);
// nearest neighbor search with the R tree.
mlpack::neighbor::NeighborSearch<NearestNeighborSort, metric::LMetric<2, true>,
- RectangleTree<tree::RTreeSplit<tree::RTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
- tree::RTreeDescentHeuristic,
+ RectangleTree<tree::RTreeSplit<tree::RStarTreeDescentHeuristic, NeighborSearchStat<NearestNeighborSort>, arma::mat>,
+ tree::RStarTreeDescentHeuristic,
NeighborSearchStat<NearestNeighborSort>,
arma::mat> > allknn1(&RTree,
dataset, true);
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/mlpack.git
More information about the debian-science-commits
mailing list