diff --git a/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser.hpp b/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser.hpp index fd69f95809..31067c28d7 100644 --- a/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser.hpp +++ b/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser.hpp @@ -19,7 +19,7 @@ namespace tree { template class SplitType, typename DescentType> template class RectangleTree class SplitType, typename DescentType> template RectangleTree:: @@ -35,7 +35,7 @@ DualTreeTraverser::DualTreeTraverser(RuleType& rule) : template class SplitType, typename DescentType> template void RectangleTree:: 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 898d9fc773..d17abf64f4 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 @@ -18,23 +18,31 @@ namespace tree /** Trees and tree-building procedures. */ { * nodes overflow, we split them, moving up the tree and splitting nodes * as necessary. */ +template class RStarTreeSplit { public: + //! Default constructor + RStarTreeSplit(); + + //! Construct this with the specified node. + RStarTreeSplit(const TreeType *node); + + //! Create a copy of the other.split. + RStarTreeSplit(const TreeType &other); + /** * 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. */ - template - static void SplitLeafNode(TreeType* tree, std::vector& relevels); + void SplitLeafNode(TreeType *tree,std::vector& relevels); /** * Split a non-leaf node using the "default" algorithm. If this is a root * node, the tree increases in depth. */ - template - static bool SplitNonLeafNode(TreeType* tree, std::vector& relevels); + bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); private: /** @@ -60,8 +68,14 @@ class RStarTreeSplit /** * Insert a node into another node. */ - template static void InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode); + + public: + /** + * Serialize the split. + */ + template + void Serialize(Archive &, const unsigned int /* version */) { }; }; } // namespace tree 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 3c66b32b88..49adbe8377 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 @@ -15,6 +15,25 @@ namespace mlpack { namespace tree { +template +RStarTreeSplit::RStarTreeSplit() +{ + +} + +template +RStarTreeSplit::RStarTreeSplit(const TreeType *) +{ + +} + +template +RStarTreeSplit::RStarTreeSplit(const TreeType &) +{ + +} + + /** * 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 @@ -22,7 +41,7 @@ namespace tree { * new nodes into the tree, spliting the parent if necessary. */ template -void RStarTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) +void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -41,7 +60,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) tree->Children()[(tree->NumChildren())++] = copy; assert(tree->NumChildren() == 1); - SplitLeafNode(copy, relevels); + copy->Split().SplitLeafNode(copy,relevels); return; } @@ -58,7 +77,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) size_t p = tree->MaxLeafSize() * 0.3; // The paper says this works the best. if (p == 0) { - SplitLeafNode(tree, relevels); + tree->Split().SplitLeafNode(tree,relevels); return; } @@ -95,7 +114,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) int bestAreaIndexOnBestAxis = 0; bool tiedOnOverlap = false; int bestAxis = 0; - ElemType bestAxisScore = DBL_MAX; + ElemType bestAxisScore = std::numeric_limits::max(); for (size_t j = 0; j < tree->Bound().Dim(); j++) { @@ -251,7 +270,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) // just in case, we use an assert. assert(par->NumChildren() <= par->MaxNumChildren() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) - SplitNonLeafNode(par, relevels); + par->Split().SplitNonLeafNode(par,relevels); assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren()); assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren()); @@ -269,8 +288,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) * higher up the tree because they were already updated if necessary. */ template -bool RStarTreeSplit::SplitNonLeafNode(TreeType* tree, - std::vector& relevels) +bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -288,7 +306,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType* tree, tree->NullifyData(); tree->Children()[(tree->NumChildren())++] = copy; - SplitNonLeafNode(copy, relevels); + copy->Split().SplitNonLeafNode(copy,relevels); return true; } @@ -359,7 +377,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType* tree, bool tiedOnOverlap = false; bool lowIsBest = true; int bestAxis = 0; - ElemType bestAxisScore = DBL_MAX; + ElemType bestAxisScore = std::numeric_limits::max(); for (size_t j = 0; j < tree->Bound().Dim(); j++) { ElemType axisScore = 0.0; @@ -450,8 +468,8 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType* tree, { bestAxisScore = axisScore; bestAxis = j; - ElemType bestOverlapIndexOnBestAxis = 0; - ElemType bestAreaIndexOnBestAxis = 0; + bestOverlapIndexOnBestAxis = 0; + bestAreaIndexOnBestAxis = 0; for (size_t i = 1; i < areas.size(); i++) { if (overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis]) @@ -565,8 +583,8 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType* tree, bestAxisScore = axisScore; bestAxis = j; lowIsBest = false; - ElemType bestOverlapIndexOnBestAxis = 0; - ElemType bestAreaIndexOnBestAxis = 0; + bestOverlapIndexOnBestAxis = 0; + bestAreaIndexOnBestAxis = 0; for (size_t i = 1; i < areas.size(); i++) { @@ -644,7 +662,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType* tree, assert(par->NumChildren() <= par->MaxNumChildren() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) { - SplitNonLeafNode(par, relevels); + par->Split().SplitNonLeafNode(par,relevels); } // We have to update the children of each of these new nodes so that they @@ -673,7 +691,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType* tree, * numberOfChildren. */ template -void RStarTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) +void RStarTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) { destTree->Bound() |= srcNode->Bound(); destTree->Children()[destTree->NumChildren()++] = srcNode; 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 7401ea6801..75994b15bd 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 @@ -20,7 +20,7 @@ inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, // Convenience typedef. typedef typename TreeType::ElemType ElemType; - ElemType minScore = DBL_MAX; + ElemType minScore = std::numeric_limits::max(); int bestIndex = 0; ElemType bestVol = 0.0; @@ -64,7 +64,7 @@ inline size_t RTreeDescentHeuristic::ChooseDescentNode( // Convenience typedef. typedef typename TreeType::ElemType ElemType; - ElemType minScore = DBL_MAX; + ElemType minScore = std::numeric_limits::max(); int bestIndex = 0; ElemType bestVol = 0.0; 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 f551fe17c7..a77308a9a7 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp @@ -18,42 +18,45 @@ namespace tree /** Trees and tree-building procedures. */ { * nodes overflow, we split them, moving up the tree and splitting nodes * as necessary. */ +template class RTreeSplit { public: + //! Default constructor + RTreeSplit(); + + //! Construct this with the specified node. + RTreeSplit(const TreeType *node); + + //! Create a copy of the other.split. + RTreeSplit(const TreeType &other); + /** * Split a leaf node using the "default" algorithm. If necessary, this split * will propagate upwards through the tree. */ - template - static void SplitLeafNode(TreeType* tree, - std::vector& relevels); + void SplitLeafNode(TreeType *tree,std::vector& relevels); /** * Split a non-leaf node using the "default" algorithm. If this is a root * node, the tree increases in depth. */ - template - static bool SplitNonLeafNode(TreeType* tree, - std::vector& relevels); + bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); private: /** * Get the seeds for splitting a leaf node. */ - template - static void GetPointSeeds(const TreeType& tree, int& i, int& j); + static void GetPointSeeds(const TreeType *tree,int& i, int& j); /** * Get the seeds for splitting a non-leaf node. */ - template - static void GetBoundSeeds(const TreeType& tree, int& i, int& j); + static void GetBoundSeeds(const TreeType *tree,int& i, int& j); /** * Assign points to the two new nodes. */ - template static void AssignPointDestNode(TreeType* oldTree, TreeType* treeOne, TreeType* treeTwo, @@ -63,7 +66,6 @@ class RTreeSplit /** * Assign nodes to the two new nodes. */ - template static void AssignNodeDestNode(TreeType* oldTree, TreeType* treeOne, TreeType* treeTwo, @@ -73,8 +75,15 @@ class RTreeSplit /** * Insert a node into another node. */ - template static void InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode); + + public: + /** + * Serialize the split. + */ + template + void Serialize(Archive &, const unsigned int /* version */) { }; + }; } // namespace tree diff --git a/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp index c9920675e4..442e49f7ed 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_tree_split_impl.hpp @@ -14,6 +14,24 @@ namespace mlpack { namespace tree { +template +RTreeSplit::RTreeSplit() +{ + +} + +template +RTreeSplit::RTreeSplit(const TreeType *) +{ + +} + +template +RTreeSplit::RTreeSplit(const TreeType &) +{ + +} + /** * 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 @@ -21,7 +39,7 @@ namespace tree { * new nodes into the tree, spliting the parent if necessary. */ template -void RTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) +void RTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { // 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 @@ -35,7 +53,7 @@ void RTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) tree->NullifyData(); // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; - SplitLeafNode(copy, relevels); + copy->Split().SplitLeafNode(copy,relevels); return; } @@ -46,7 +64,7 @@ void RTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) // rectangles, only points. We assume that the tree uses Euclidean Distance. int i = 0; int j = 0; - GetPointSeeds(*tree, i, j); + RTreeSplit::GetPointSeeds(tree,i, j); TreeType* treeOne = new TreeType(tree->Parent()); TreeType* treeTwo = new TreeType(tree->Parent()); @@ -66,7 +84,7 @@ void RTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) // just in case, we use an assert. assert(par->NumChildren() <= par->MaxNumChildren() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) - SplitNonLeafNode(par, relevels); + par->Split().SplitNonLeafNode(par,relevels); assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren()); assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren()); @@ -85,7 +103,7 @@ void RTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) * higher up the tree because they were already updated if necessary. */ template -bool RTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) +bool RTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) { // 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 @@ -98,13 +116,13 @@ bool RTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) tree->NumChildren() = 0; tree->NullifyData(); tree->Children()[(tree->NumChildren())++] = copy; - SplitNonLeafNode(copy, relevels); + copy->Split().SplitNonLeafNode(copy,relevels); return true; } int i = 0; int j = 0; - GetBoundSeeds(*tree, i, j); + RTreeSplit::GetBoundSeeds(tree,i, j); assert(i != j); @@ -131,7 +149,7 @@ bool RTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) assert(par->NumChildren() <= par->MaxNumChildren() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) - SplitNonLeafNode(par, relevels); + par->Split().SplitNonLeafNode(par,relevels); // We have to update the children of each of these new nodes so that they // record the correct parent. @@ -157,18 +175,18 @@ bool RTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) * The indices of these points will be stored in iRet and jRet. */ template -void RTreeSplit::GetPointSeeds(const TreeType& tree, int& iRet, int& jRet) +void RTreeSplit::GetPointSeeds(const TreeType *tree,int& iRet, int& jRet) { // Here we want to find the pair of points that it is worst to place in the // same node. Because we are just using points, we will simply choose the two // that would create the most voluminous hyperrectangle. typename TreeType::ElemType worstPairScore = -1.0; - for (size_t i = 0; i < tree.Count(); i++) + for (size_t i = 0; i < tree->Count(); i++) { - for (size_t j = i + 1; j < tree.Count(); j++) + for (size_t j = i + 1; j < tree->Count(); j++) { const typename TreeType::ElemType score = arma::prod(arma::abs( - tree.LocalDataset().col(i) - tree.LocalDataset().col(j))); + tree->LocalDataset().col(i) - tree->LocalDataset().col(j))); if (score > worstPairScore) { @@ -185,23 +203,23 @@ void RTreeSplit::GetPointSeeds(const TreeType& tree, int& iRet, int& jRet) * indices of the bounds will be stored in iRet and jRet. */ template -void RTreeSplit::GetBoundSeeds(const TreeType& tree, int& iRet, int& jRet) +void RTreeSplit::GetBoundSeeds(const TreeType *tree,int& iRet, int& jRet) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; ElemType worstPairScore = -1.0; - for (size_t i = 0; i < tree.NumChildren(); i++) + for (size_t i = 0; i < tree->NumChildren(); i++) { - for (size_t j = i + 1; j < tree.NumChildren(); j++) + for (size_t j = i + 1; j < tree->NumChildren(); j++) { ElemType score = 1.0; - for (size_t k = 0; k < tree.Bound().Dim(); k++) + for (size_t k = 0; k < tree->Bound().Dim(); k++) { - const ElemType hiMax = std::max(tree.Children()[i]->Bound()[k].Hi(), - tree.Children()[j]->Bound()[k].Hi()); - const ElemType loMin = std::min(tree.Children()[i]->Bound()[k].Lo(), - tree.Children()[j]->Bound()[k].Lo()); + const ElemType hiMax = std::max(tree->Children()[i]->Bound()[k].Hi(), + tree->Children()[j]->Bound()[k].Hi()); + const ElemType loMin = std::min(tree->Children()[i]->Bound()[k].Lo(), + tree->Children()[j]->Bound()[k].Lo()); score *= (hiMax - loMin); } @@ -216,7 +234,7 @@ void RTreeSplit::GetBoundSeeds(const TreeType& tree, int& iRet, int& jRet) } template -void RTreeSplit::AssignPointDestNode(TreeType* oldTree, +void RTreeSplit::AssignPointDestNode(TreeType* oldTree, TreeType* treeOne, TreeType* treeTwo, const int intI, @@ -270,7 +288,7 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, std::min(numAssignedOne, numAssignedTwo))) { int bestIndex = 0; - ElemType bestScore = DBL_MAX; + ElemType bestScore = std::numeric_limits::max(); int bestRect = 1; // Calculate the increase in volume for assigning this point to each @@ -357,7 +375,7 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, } template -void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, +void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, TreeType* treeOne, TreeType* treeTwo, const int intI, @@ -414,7 +432,7 @@ void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, std::min(numAssignTreeOne, numAssignTreeTwo))) { int bestIndex = 0; - ElemType bestScore = DBL_MAX; + ElemType bestScore = std::numeric_limits::max(); int bestRect = 0; // Calculate the increase in volume for assigning this node to each of the @@ -522,7 +540,7 @@ void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, * numberOfChildren. */ template -void RTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) +void RTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) { destTree->Bound() |= srcNode->Bound(); destTree->Children()[destTree->NumChildren()++] = srcNode; diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index e9faf0f129..8432f44233 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -35,10 +35,11 @@ namespace tree /** Trees and tree-building procedures. */ { * @tparam DescentType The heuristic to use when descending the tree to insert * points. */ + template class SplitType = RTreeSplit, typename DescentType = RTreeDescentHeuristic> class RectangleTree { @@ -52,29 +53,6 @@ class RectangleTree //! The element type held by the matrix type. typedef typename MatType::elem_type ElemType; - /** - * The X tree requires that the tree records it's "split history". To make - * this easy, we use the following structure. - */ - typedef struct SplitHistoryStruct - { - int lastDimension; - std::vector history; - - SplitHistoryStruct(int dim) : lastDimension(0), history(dim) - { - for (int i = 0; i < dim; i++) - history[i] = false; - } - - template - void Serialize(Archive& ar, const unsigned int /* version */) - { - ar & data::CreateNVP(lastDimension, "lastDimension"); - ar & data::CreateNVP(history, "history"); - } - } SplitHistoryStruct; - private: //! The max number of child nodes a non-leaf node can have. size_t maxNumChildren; @@ -102,8 +80,6 @@ class RectangleTree bound::HRectBound bound; //! Any extra data contained in the node. StatisticType stat; - //! A struct to store the "split history" for X trees. - SplitHistoryStruct splitHistory; //! The distance from the centroid of this node to the centroid of the parent. ElemType parentDistance; //! The dataset. @@ -115,6 +91,8 @@ class RectangleTree std::vector points; //! The local dataset MatType* localDataset; + //! The class that performs the split of the node. + SplitType split; public: //! A single traverser for rectangle type trees. See @@ -173,8 +151,10 @@ class RectangleTree * firstDataIndex) from the parent. * * @param parentNode The parent of the node that is being constructed. + * @param numMaxChildren The max number of child nodes (used in x-trees). */ - explicit RectangleTree(RectangleTree* parentNode); + explicit RectangleTree(RectangleTree* parentNode, + const size_t numMaxChildren = 0); /** * Create a rectangle tree by copying the other tree. Be careful! This can @@ -311,10 +291,10 @@ class RectangleTree //! Modify the statistic object for this node. StatisticType& Stat() { return stat; } - //! Return the split history object of this node. - const SplitHistoryStruct& SplitHistory() const { return splitHistory; } - //! Modify the split history object of this node. - SplitHistoryStruct& SplitHistory() { return splitHistory; } + //! Return the split object of this node. + const SplitType& Split() const { return split; } + //! Modify the split object of this node. + SplitType& Split() { return split; } //! Return whether or not this node is a leaf (true if it has no children). bool IsLeaf() const; diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp index 142844faf0..8184f89c06 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -20,7 +20,7 @@ namespace tree { template class SplitType, typename DescentType> RectangleTree:: RectangleTree(const MatType& data, @@ -39,7 +39,6 @@ RectangleTree(const MatType& data, maxLeafSize(maxLeafSize), minLeafSize(minLeafSize), bound(data.n_rows), - splitHistory(bound.Dim()), parentDistance(0), dataset(new MatType(data)), ownsDataset(true), @@ -49,6 +48,8 @@ RectangleTree(const MatType& data, { stat = StatisticType(*this); + split = SplitType(this); + // For now, just insert the points in order. RectangleTree* root = this; @@ -59,7 +60,7 @@ RectangleTree(const MatType& data, template class SplitType, typename DescentType> RectangleTree:: RectangleTree(MatType&& data, @@ -78,7 +79,6 @@ RectangleTree(MatType&& data, maxLeafSize(maxLeafSize), minLeafSize(minLeafSize), bound(data.n_rows), - splitHistory(bound.Dim()), parentDistance(0), dataset(new MatType(std::move(data))), ownsDataset(true), @@ -88,6 +88,8 @@ RectangleTree(MatType&& data, { stat = StatisticType(*this); + split = SplitType(this); + // For now, just insert the points in order. RectangleTree* root = this; @@ -98,13 +100,13 @@ RectangleTree(MatType&& data, template class SplitType, typename DescentType> RectangleTree:: RectangleTree( RectangleTree* - parentNode) : - maxNumChildren(parentNode->MaxNumChildren()), + parentNode,const size_t numMaxChildren) : + maxNumChildren(numMaxChildren > 0 ? numMaxChildren : parentNode->MaxNumChildren()), minNumChildren(parentNode->MinNumChildren()), numChildren(0), children(maxNumChildren + 1), @@ -114,7 +116,6 @@ RectangleTree( maxLeafSize(parentNode->MaxLeafSize()), minLeafSize(parentNode->MinLeafSize()), bound(parentNode->Bound().Dim()), - splitHistory(bound.Dim()), parentDistance(0), dataset(&parentNode->Dataset()), ownsDataset(false), @@ -123,6 +124,7 @@ RectangleTree( maxLeafSize + 1))) { stat = StatisticType(*this); + split = SplitType(this); } /** @@ -132,7 +134,7 @@ RectangleTree( template class SplitType, typename DescentType> RectangleTree:: RectangleTree( @@ -148,13 +150,13 @@ RectangleTree( maxLeafSize(other.MaxLeafSize()), minLeafSize(other.MinLeafSize()), bound(other.bound), - splitHistory(other.SplitHistory()), parentDistance(other.ParentDistance()), - dataset(new MatType(*other.dataset)), - ownsDataset(true), + dataset(deepCopy ? new MatType(*other.dataset) : &other.Dataset()), + ownsDataset(deepCopy), points(other.Points()), localDataset(NULL) { + split = SplitType(other); if (deepCopy) { if (numChildren > 0) @@ -183,7 +185,7 @@ RectangleTree( template class SplitType, typename DescentType> template RectangleTree:: @@ -204,7 +206,7 @@ RectangleTree( template class SplitType, typename DescentType> RectangleTree:: ~RectangleTree() @@ -225,7 +227,7 @@ RectangleTree:: template class SplitType, typename DescentType> void RectangleTree:: SoftDelete() @@ -245,7 +247,7 @@ void RectangleTree:: template class SplitType, typename DescentType> void RectangleTree:: NullifyData() @@ -260,7 +262,7 @@ void RectangleTree:: template class SplitType, typename DescentType> void RectangleTree:: InsertPoint(const size_t point) @@ -297,7 +299,7 @@ void RectangleTree:: template class SplitType, typename DescentType> void RectangleTree:: InsertPoint(const size_t point, std::vector& relevels) @@ -332,7 +334,7 @@ void RectangleTree:: template class SplitType, typename DescentType> void RectangleTree:: InsertNode(RectangleTree* node, @@ -361,7 +363,7 @@ void RectangleTree:: template class SplitType, typename DescentType> bool RectangleTree:: DeletePoint(const size_t point) @@ -406,7 +408,7 @@ bool RectangleTree:: template class SplitType, typename DescentType> bool RectangleTree:: DeletePoint(const size_t point, std::vector& relevels) @@ -441,7 +443,7 @@ bool RectangleTree:: template class SplitType, typename DescentType> bool RectangleTree:: RemoveNode(const RectangleTree* node, std::vector& relevels) @@ -470,7 +472,7 @@ bool RectangleTree:: template class SplitType, typename DescentType> size_t RectangleTree::TreeSize() const @@ -485,7 +487,7 @@ size_t RectangleTree class SplitType, typename DescentType> size_t RectangleTree::TreeDepth() const @@ -505,7 +507,7 @@ size_t RectangleTree class SplitType, typename DescentType> inline bool RectangleTree::IsLeaf() const @@ -520,7 +522,7 @@ inline bool RectangleTree class SplitType, typename DescentType> inline typename RectangleTree class SplitType, typename DescentType> inline typename RectangleTree class SplitType, typename DescentType> inline size_t RectangleTree::NumPoints() const @@ -581,7 +583,7 @@ inline size_t RectangleTree class SplitType, typename DescentType> inline size_t RectangleTree::NumDescendants() const @@ -605,7 +607,7 @@ inline size_t RectangleTree class SplitType, typename DescentType> inline size_t RectangleTree::Descendant(const size_t index) const @@ -637,7 +639,7 @@ inline size_t RectangleTree class SplitType, typename DescentType> inline size_t RectangleTree::Point(const size_t index) const @@ -652,7 +654,7 @@ inline size_t RectangleTree class SplitType, typename DescentType> void RectangleTree:: SplitNode(std::vector& relevels) @@ -665,7 +667,7 @@ void RectangleTree:: // If we are full, then we need to split (or at least try). The SplitType // takes care of this and of moving up the tree if necessary. - SplitType::SplitLeafNode(this, relevels); + split.SplitLeafNode(this,relevels); } else { @@ -675,7 +677,7 @@ void RectangleTree:: // If we are full, then we need to split (or at least try). The SplitType // takes care of this and of moving up the tree if necessary. - SplitType::SplitNonLeafNode(this, relevels); + split.SplitNonLeafNode(this,relevels); } } @@ -683,7 +685,7 @@ void RectangleTree:: template class SplitType, typename DescentType> RectangleTree:: RectangleTree() : @@ -695,7 +697,6 @@ RectangleTree() : count(0), maxLeafSize(0), minLeafSize(0), - splitHistory(0), parentDistance(0.0), dataset(NULL), ownsDataset(false), @@ -711,7 +712,7 @@ RectangleTree() : template class SplitType, typename DescentType> void RectangleTree:: CondenseTree(const arma::vec& point, @@ -799,6 +800,14 @@ void RectangleTree:: { // If there are multiple children, we can't do anything to the root. RectangleTree* child = children[0]; + + // Required for the X tree. + if(child->NumChildren() > maxNumChildren) + { + maxNumChildren = child->MaxNumChildren(); + children.resize(maxNumChildren+1); + } + for (size_t i = 0; i < child->NumChildren(); i++) { children[i] = child->Children()[i]; children[i]->Parent() = this; @@ -814,7 +823,6 @@ void RectangleTree:: } count = child->Count(); - maxNumChildren = child->MaxNumChildren(); // Required for the X tree. child->SoftDelete(); return; } @@ -833,7 +841,7 @@ void RectangleTree:: template class SplitType, typename DescentType> bool RectangleTree:: ShrinkBoundForPoint(const arma::vec& point) @@ -929,7 +937,7 @@ bool RectangleTree:: template class SplitType, typename DescentType> bool RectangleTree:: ShrinkBoundForBound(const bound::HRectBound& /* b */) @@ -963,7 +971,7 @@ bool RectangleTree:: template class SplitType, typename DescentType> template void RectangleTree:: @@ -1010,7 +1018,6 @@ void RectangleTree:: ar & CreateNVP(minLeafSize, "minLeafSize"); ar & CreateNVP(bound, "bound"); ar & CreateNVP(stat, "stat"); - ar & CreateNVP(splitHistory, "splitHistory"); ar & CreateNVP(parentDistance, "parentDistance"); ar & CreateNVP(dataset, "dataset"); @@ -1020,6 +1027,7 @@ void RectangleTree:: ar & CreateNVP(points, "points"); ar & CreateNVP(localDataset, "localDataset"); + ar & CreateNVP(split, "split"); // Because 'children' holds mlpack types (that have Serialize()), we can't use // the std::vector serialization. diff --git a/src/mlpack/core/tree/rectangle_tree/single_tree_traverser.hpp b/src/mlpack/core/tree/rectangle_tree/single_tree_traverser.hpp index b75b31155f..702d261d0c 100644 --- a/src/mlpack/core/tree/rectangle_tree/single_tree_traverser.hpp +++ b/src/mlpack/core/tree/rectangle_tree/single_tree_traverser.hpp @@ -19,7 +19,7 @@ namespace tree { template class SplitType, typename DescentType> template class RectangleTree class SplitType, typename DescentType> template RectangleTree:: @@ -32,7 +32,7 @@ SingleTreeTraverser::SingleTreeTraverser(RuleType& rule) : template class SplitType, typename DescentType> template void RectangleTree:: diff --git a/src/mlpack/core/tree/rectangle_tree/traits.hpp b/src/mlpack/core/tree/rectangle_tree/traits.hpp index 25da24e271..91923cd77d 100644 --- a/src/mlpack/core/tree/rectangle_tree/traits.hpp +++ b/src/mlpack/core/tree/rectangle_tree/traits.hpp @@ -21,7 +21,7 @@ namespace tree { template class SplitType, typename DescentType> class TreeTraits> diff --git a/src/mlpack/core/tree/rectangle_tree/typedef.hpp b/src/mlpack/core/tree/rectangle_tree/typedef.hpp index d2f11961e4..b5b0fa6da8 100644 --- a/src/mlpack/core/tree/rectangle_tree/typedef.hpp +++ b/src/mlpack/core/tree/rectangle_tree/typedef.hpp @@ -68,15 +68,29 @@ using RStarTree = RectangleTree; /** - * X-tree - * (not yet finished) + * The X-tree, a variant of the R tree with supernodes. This template typedef + * satisfies the TreeType policy API. + * + * @code + * @inproceedings{berchtold1996r, + * title = {The X-Tree: An Index Structure for High--Dimensional Data}, + * author = {Berchtold, Stefan and Keim, Daniel A. and Kriegel, Hans-Peter}, + * booktitle = {Proc. 22th Int. Conf. on Very Large Databases (VLDB'96), Bombay, India}, + * editor = {Vijayaraman, T. and Buchmann, Alex and Mohan, C. and Sarda, N.}, + * pages = {28--39}, + * year = {1996}, + * publisher = {Morgan Kaufmann} + * } + * @endcode + * + * @see @ref trees, RTree, RStarTree */ -//template -//using XTree = RectangleTree; +template +using XTree = RectangleTree; } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp index f8c9214f9b..7b120a9086 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp @@ -28,39 +28,78 @@ const double MAX_OVERLAP = 0.2; * nodes overflow, we split them, moving up the tree and splitting nodes * as necessary. */ +template class XTreeSplit { public: + //! Default constructor + XTreeSplit(); + + //! Construct this with the specified node. + XTreeSplit(const TreeType *node); + + //! Create a copy of the other.split. + XTreeSplit(const TreeType &other); + /** * 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. */ - template - static void SplitLeafNode(TreeType* tree, std::vector& relevels); + void SplitLeafNode(TreeType *tree,std::vector& relevels); /** * Split a non-leaf node using the "default" algorithm. If this is a root * node, the tree increases in depth. */ - template - static bool SplitNonLeafNode(TreeType* tree, std::vector& relevels); + bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + + /** + * The X tree requires that the tree records it's "split history". To make + * this easy, we use the following structure. + */ + typedef struct SplitHistoryStruct + { + int lastDimension; + std::vector history; + + SplitHistoryStruct(int dim) : lastDimension(0), history(dim) + { + for (int i = 0; i < dim; i++) + history[i] = false; + } + + template + void Serialize(Archive& ar, const unsigned int /* version */) + { + ar & data::CreateNVP(lastDimension, "lastDimension"); + ar & data::CreateNVP(history, "history"); + } + } SplitHistoryStruct; private: + //! The max number of child nodes a non-leaf normal node can have. + size_t normalNodeMaxNumChildren; + //! A struct to store the "split history" for X trees. + SplitHistoryStruct splitHistory; + /** * Class to allow for faster sorting. */ + template class sortStruct { public: - double d; + ElemType d; int n; }; /** * Comparator for sorting with sortStruct. */ - static bool structComp(const sortStruct& s1, const sortStruct& s2) + template + static bool structComp(const sortStruct& s1, + const sortStruct& s2) { return s1.d < s2.d; } @@ -68,8 +107,24 @@ class XTreeSplit /** * Insert a node into another node. */ - template static void InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode); + + public: + //! Return the maximum number of a normal node's children. + size_t NormalNodeMaxNumChildren() const { return normalNodeMaxNumChildren; } + //! Modify the maximum number of a normal node's children. + size_t& NormalNodeMaxNumChildren() { return normalNodeMaxNumChildren; } + //! Return the split history of the node assosiated with this object. + const SplitHistoryStruct& SplitHistory() const { return splitHistory; } + //! Modify the split history of the node assosiated with this object. + SplitHistoryStruct& SplitHistory() { return splitHistory; } + + + /** + * Serialize the split. + */ + template + void Serialize(Archive& ar, const unsigned int /* version */); }; } // namespace tree diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp index d04f234bba..a619b725a5 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_split_impl.hpp @@ -14,6 +14,33 @@ namespace mlpack { namespace tree { +template +XTreeSplit::XTreeSplit() : + normalNodeMaxNumChildren(0), + splitHistory(0) +{ + +} + +template +XTreeSplit::XTreeSplit(const TreeType *node) : + normalNodeMaxNumChildren(node->Parent() ? + node->Parent()->Split().NormalNodeMaxNumChildren() : + node->MaxNumChildren()), + splitHistory(node->Bound().Dim()) +{ + +} + +template +XTreeSplit::XTreeSplit(const TreeType &other) : + normalNodeMaxNumChildren(other.Split().NormalNodeMaxNumChildren()), + splitHistory(other.Split().SplitHistory()) +{ + +} + + /** * 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 @@ -21,8 +48,11 @@ namespace tree { * new nodes into the tree, spliting the parent if necessary. */ template -void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) +void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { + // Convenience typedef. + typedef typename TreeType::ElemType ElemType; + // 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. @@ -36,7 +66,7 @@ void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; assert(tree->NumChildren() == 1); - XTreeSplit::SplitLeafNode(copy, relevels); + copy->Split().SplitLeafNode(copy,relevels); return; } @@ -54,21 +84,21 @@ void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) size_t p = tree->MaxLeafSize() * 0.3; if (p == 0) { - SplitLeafNode(tree, relevels); + tree->Split().SplitLeafNode(tree,relevels); return; } - std::vector sorted(tree->Count()); - arma::vec center; + std::vector> sorted(tree->Count()); + arma::Col center; tree->Bound().Center(center); // Modifies centroid. for (size_t i = 0; i < sorted.size(); i++) { - sorted[i].d = tree->Bound().Metric().Evaluate(center, + sorted[i].d = tree->Metric().Evaluate(center, tree->LocalDataset().col(i)); sorted[i].n = i; } - std::sort(sorted.begin(), sorted.end(), structComp); + std::sort(sorted.begin(), sorted.end(), structComp); std::vector pointIndices(p); for (size_t i = 0; i < p; i++) { @@ -103,25 +133,25 @@ void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) int bestAreaIndexOnBestAxis = 0; bool tiedOnOverlap = false; int bestAxis = 0; - double bestAxisScore = DBL_MAX; + ElemType bestAxisScore = std::numeric_limits::max(); for (size_t j = 0; j < tree->Bound().Dim(); j++) { - double axisScore = 0.0; + ElemType axisScore = 0.0; // Since we only have points in the leaf nodes, we only need to sort once. - std::vector sorted(tree->Count()); + std::vector> sorted(tree->Count()); for (size_t 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); + std::sort(sorted.begin(), sorted.end(), structComp); // We'll store each of the three scores for each distribution. - std::vector areas(tree->MaxLeafSize() - + std::vector areas(tree->MaxLeafSize() - 2 * tree->MinLeafSize() + 2); - std::vector margins(tree->MaxLeafSize() - + std::vector margins(tree->MaxLeafSize() - 2 * tree->MinLeafSize() + 2); - std::vector overlapedAreas(tree->MaxLeafSize() - + std::vector overlapedAreas(tree->MaxLeafSize() - 2 * tree->MinLeafSize() + 2); for (size_t i = 0; i < areas.size(); i++) { @@ -137,10 +167,10 @@ void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) size_t cutOff = tree->MinLeafSize() + i; // We'll calculate the max and min in each dimension by hand to save time. - std::vector maxG1(tree->Bound().Dim()); - std::vector minG1(maxG1.size()); - std::vector maxG2(maxG1.size()); - std::vector minG2(maxG1.size()); + std::vector maxG1(tree->Bound().Dim()); + std::vector minG1(maxG1.size()); + std::vector maxG2(maxG1.size()); + std::vector minG2(maxG1.size()); for (size_t k = 0; k < tree->Bound().Dim(); k++) { minG1[k] = maxG1[k] = tree->LocalDataset().col(sorted[0].n)[k]; @@ -166,8 +196,8 @@ void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) } } - double area1 = 1.0, area2 = 1.0; - double oArea = 1.0; + ElemType area1 = 1.0, area2 = 1.0; + ElemType oArea = 1.0; for (size_t k = 0; k < maxG1.size(); k++) { margins[i] += maxG1[k] - minG1[k] + maxG2[k] - minG2[k]; @@ -206,17 +236,17 @@ void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) } } - std::vector sorted(tree->Count()); + std::vector> sorted(tree->Count()); for (size_t 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); + std::sort(sorted.begin(), sorted.end(), structComp); - TreeType* treeOne = new TreeType(tree->Parent()); - TreeType* treeTwo = new TreeType(tree->Parent()); + TreeType* treeOne = new TreeType(tree->Parent(),NormalNodeMaxNumChildren()); + TreeType* treeTwo = new TreeType(tree->Parent(),NormalNodeMaxNumChildren()); // The leaf nodes should never have any overlap introduced by the above method // since a split axis is chosen and then points are assigned based on their @@ -258,16 +288,16 @@ void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) par->Children()[par->NumChildren()++] = treeTwo; // We now update the split history of each new node. - treeOne->SplitHistory().history[bestAxis] = true; - treeOne->SplitHistory().lastDimension = bestAxis; - treeTwo->SplitHistory().history[bestAxis] = true; - treeTwo->SplitHistory().lastDimension = bestAxis; + treeOne->Split().SplitHistory().history[bestAxis] = true; + treeOne->Split().SplitHistory().lastDimension = bestAxis; + treeTwo->Split().SplitHistory().history[bestAxis] = true; + treeTwo->Split().SplitHistory().lastDimension = bestAxis; // 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() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) - SplitNonLeafNode(par, relevels); + par->Split().SplitNonLeafNode(par,relevels); assert(treeOne->Parent()->NumChildren() <= treeOne->Parent()->MaxNumChildren()); @@ -289,8 +319,11 @@ void XTreeSplit::SplitLeafNode(TreeType* tree, std::vector& relevels) * higher up the tree because they were already updated if necessary. */ template -bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) +bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) { + // Convenience typedef. + typedef typename TreeType::ElemType ElemType; + // 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. @@ -303,7 +336,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) tree->NumChildren() = 0; tree->NullifyData(); tree->Children()[(tree->NumChildren())++] = copy; - XTreeSplit::SplitNonLeafNode(copy, relevels); + copy->Split().SplitNonLeafNode(copy,relevels); return true; } @@ -318,7 +351,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) std::vector axes(tree->Bound().Dim()); std::vector dimensionsLastUsed(tree->NumChildren()); for (size_t i = 0; i < tree->NumChildren(); i++) - dimensionsLastUsed[i] = tree->Child(i).SplitHistory().lastDimension; + dimensionsLastUsed[i] = tree->Child(i).Split().SplitHistory().lastDimension; std::sort(dimensionsLastUsed.begin(), dimensionsLastUsed.end()); size_t lastDim = dimensionsLastUsed[dimensionsLastUsed.size()/2]; @@ -329,7 +362,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) { axes[i] = true; for (size_t j = 0; j < tree->NumChildren(); j++) - axes[i] = axes[i] & tree->Child(j).SplitHistory().history[i]; + axes[i] = axes[i] & tree->Child(j).Split().SplitHistory().history[i]; if (axes[i] == true) { minOverlapSplitDimension = i; @@ -342,7 +375,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) { axes[i] = true; for (size_t j = 0; j < tree->NumChildren(); j++) - axes[i] = axes[i] & tree->Child(j).SplitHistory().history[i]; + axes[i] = axes[i] & tree->Child(j).Split().SplitHistory().history[i]; if (axes[i] == true) { minOverlapSplitDimension = i; @@ -352,8 +385,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) } bool minOverlapSplitUsesHi = false; - double bestScoreMinOverlapSplit = DBL_MAX; - double areaOfBestMinOverlapSplit = 0; + ElemType bestScoreMinOverlapSplit = std::numeric_limits::max(); + ElemType areaOfBestMinOverlapSplit = 0; int bestIndexMinOverlapSplit = 0; int bestOverlapIndexOnBestAxis = 0; @@ -361,32 +394,32 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) bool tiedOnOverlap = false; bool lowIsBest = true; int bestAxis = 0; - double bestAxisScore = DBL_MAX; - double overlapBestOverlapAxis = 0; - double areaBestOverlapAxis = 0; - double overlapBestAreaAxis = 0; - double areaBestAreaAxis = 0; + ElemType bestAxisScore = std::numeric_limits::max(); + ElemType overlapBestOverlapAxis = 0; + ElemType areaBestOverlapAxis = 0; + ElemType overlapBestAreaAxis = 0; + ElemType areaBestAreaAxis = 0; for (size_t j = 0; j < tree->Bound().Dim(); j++) { - double axisScore = 0.0; + ElemType axisScore = 0.0; // We'll do Bound().Lo() now and use Bound().Hi() later. - std::vector sorted(tree->NumChildren()); + std::vector> sorted(tree->NumChildren()); for (size_t i = 0; i < sorted.size(); i++) { sorted[i].d = tree->Children()[i]->Bound()[j].Lo(); sorted[i].n = i; } - std::sort(sorted.begin(), sorted.end(), structComp); + std::sort(sorted.begin(), sorted.end(), structComp); // We'll store each of the three scores for each distribution. - std::vector areas(tree->MaxNumChildren() - + std::vector areas(tree->MaxNumChildren() - 2 * tree->MinNumChildren() + 2); - std::vector margins(tree->MaxNumChildren() - + std::vector margins(tree->MaxNumChildren() - 2 * tree->MinNumChildren() + 2); - std::vector overlapedAreas(tree->MaxNumChildren() - + std::vector overlapedAreas(tree->MaxNumChildren() - 2 * tree->MinNumChildren() + 2); for (size_t i = 0; i < areas.size(); i++) { @@ -403,10 +436,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) size_t cutOff = tree->MinNumChildren() + i; // We'll calculate the max and min in each dimension by hand to save time. - std::vector maxG1(tree->Bound().Dim()); - std::vector minG1(maxG1.size()); - std::vector maxG2(maxG1.size()); - std::vector minG2(maxG1.size()); + std::vector maxG1(tree->Bound().Dim()); + std::vector minG1(maxG1.size()); + std::vector maxG2(maxG1.size()); + std::vector minG2(maxG1.size()); for (size_t k = 0; k < tree->Bound().Dim(); k++) { minG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Lo(); @@ -434,8 +467,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) } } - double area1 = 1.0, area2 = 1.0; - double oArea = 1.0; + ElemType area1 = 1.0, area2 = 1.0; + ElemType oArea = 1.0; for (size_t k = 0; k < maxG1.size(); k++) { margins[i] += maxG1[k] - minG1[k] + maxG2[k] - minG2[k]; @@ -453,8 +486,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) { bestAxisScore = axisScore; bestAxis = j; - double bestOverlapIndexOnBestAxis = 0; - double bestAreaIndexOnBestAxis = 0; + bestOverlapIndexOnBestAxis = 0; + bestAreaIndexOnBestAxis = 0; + overlapBestOverlapAxis = overlapedAreas[bestOverlapIndexOnBestAxis]; + areaBestOverlapAxis = areas[bestAreaIndexOnBestAxis]; for (size_t i = 1; i < areas.size(); i++) { if (overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis]) @@ -498,24 +533,24 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) // Now we do the same thing using Bound().Hi() and choose the best of the two. for (size_t j = 0; j < tree->Bound().Dim(); j++) { - double axisScore = 0.0; + ElemType axisScore = 0.0; // We'll do Bound().Lo() now and use Bound().Hi() later. - std::vector sorted(tree->NumChildren()); + std::vector> sorted(tree->NumChildren()); for (size_t i = 0; i < sorted.size(); i++) { sorted[i].d = tree->Children()[i]->Bound()[j].Hi(); sorted[i].n = i; } - std::sort(sorted.begin(), sorted.end(), structComp); + std::sort(sorted.begin(), sorted.end(), structComp); // We'll store each of the three scores for each distribution. - std::vector areas(tree->MaxNumChildren() - + std::vector areas(tree->MaxNumChildren() - 2 * tree->MinNumChildren() + 2); - std::vector margins(tree->MaxNumChildren() - + std::vector margins(tree->MaxNumChildren() - 2 * tree->MinNumChildren() + 2); - std::vector overlapedAreas(tree->MaxNumChildren() - + std::vector overlapedAreas(tree->MaxNumChildren() - 2 * tree->MinNumChildren() + 2); for (size_t i = 0; i < areas.size(); i++) { @@ -532,10 +567,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) size_t cutOff = tree->MinNumChildren() + i; // We'll calculate the max and min in each dimension by hand to save time. - std::vector maxG1(tree->Bound().Dim()); - std::vector minG1(maxG1.size()); - std::vector maxG2(maxG1.size()); - std::vector minG2(maxG1.size()); + std::vector maxG1(tree->Bound().Dim()); + std::vector minG1(maxG1.size()); + std::vector maxG2(maxG1.size()); + std::vector minG2(maxG1.size()); for (size_t k = 0; k < tree->Bound().Dim(); k++) { minG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Lo(); @@ -563,8 +598,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) } } - double area1 = 1.0, area2 = 1.0; - double oArea = 1.0; + ElemType area1 = 1.0, area2 = 1.0; + ElemType oArea = 1.0; for (size_t k = 0; k < maxG1.size(); k++) { margins[i] += maxG1[k] - minG1[k] + maxG2[k] - minG2[k]; @@ -584,8 +619,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) bestAxisScore = axisScore; bestAxis = j; lowIsBest = false; - double bestOverlapIndexOnBestAxis = 0; - double bestAreaIndexOnBestAxis = 0; + bestOverlapIndexOnBestAxis = 0; + bestAreaIndexOnBestAxis = 0; + overlapBestOverlapAxis = overlapedAreas[bestOverlapIndexOnBestAxis]; + areaBestOverlapAxis = areas[bestAreaIndexOnBestAxis]; for (size_t i = 1; i < areas.size(); i++) { if (overlapedAreas[i] < overlapedAreas[bestOverlapIndexOnBestAxis]) @@ -627,7 +664,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) } } - std::vector sorted(tree->NumChildren()); + std::vector> sorted(tree->NumChildren()); if (lowIsBest) { for (size_t i = 0; i < sorted.size(); i++) @@ -645,10 +682,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) } } - std::sort(sorted.begin(), sorted.end(), structComp); + std::sort(sorted.begin(), sorted.end(), structComp); - TreeType* treeOne = new TreeType(tree->Parent()); - TreeType* treeTwo = new TreeType(tree->Parent()); + TreeType* treeOne = new TreeType(tree->Parent(),tree->MaxNumChildren()); + TreeType* treeTwo = new TreeType(tree->Parent(),tree->MaxNumChildren()); // Now as per the X-tree paper, we ensure that this split was good enough. bool useMinOverlapSplit = false; @@ -692,7 +729,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) if ((minOverlapSplitDimension != tree->Bound().Dim()) && (bestScoreMinOverlapSplit / areaOfBestMinOverlapSplit < MAX_OVERLAP)) { - std::vector sorted2(tree->NumChildren()); + std::vector> sorted2(tree->NumChildren()); if (minOverlapSplitUsesHi) { for (size_t i = 0; i < sorted2.size(); i++) @@ -709,7 +746,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) sorted2[i].n = i; } } - std::sort(sorted2.begin(), sorted2.end(), structComp); + std::sort(sorted2.begin(), sorted2.end(), structComp); for (size_t i = 0; i < tree->NumChildren(); i++) { if (i < bestIndexMinOverlapSplit + tree->MinNumChildren()) @@ -733,11 +770,14 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) (tree->Parent()->NumChildren() == 1)) { // We make the root a supernode instead. - tree->Parent()->MaxNumChildren() *= 2; + tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() + NormalNodeMaxNumChildren(); tree->Parent()->Children().resize(tree->Parent()->MaxNumChildren() + 1); tree->Parent()->NumChildren() = tree->NumChildren(); for (size_t i = 0; i < tree->NumChildren(); i++) + { tree->Parent()->Children()[i] = tree->Children()[i]; + tree->Child(i).Parent() = tree->Parent(); + } delete treeOne; delete treeTwo; @@ -747,7 +787,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) } // If we don't have to worry about the root, we just enlarge this node. - tree->MaxNumChildren() *= 2; + tree->MaxNumChildren() += NormalNodeMaxNumChildren(); tree->Children().resize(tree->MaxNumChildren() + 1); for (size_t i = 0; i < tree->NumChildren(); i++) tree->Child(i).Parent() = tree; @@ -760,10 +800,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) } // Update the split history of each child. - treeOne->SplitHistory().history[bestAxis] = true; - treeOne->SplitHistory().lastDimension = bestAxis; - treeTwo->SplitHistory().history[bestAxis] = true; - treeTwo->SplitHistory().lastDimension = bestAxis; + treeOne->Split().SplitHistory().history[bestAxis] = true; + treeOne->Split().SplitHistory().lastDimension = bestAxis; + treeTwo->Split().SplitHistory().history[bestAxis] = true; + treeTwo->Split().SplitHistory().lastDimension = bestAxis; // Remove this node and insert treeOne and treeTwo TreeType* par = tree->Parent(); @@ -790,7 +830,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) if (par->NumChildren() == par->MaxNumChildren() + 1) { - SplitNonLeafNode(par, relevels); + par->Split().SplitNonLeafNode(par,relevels); } // We have to update the children of each of these new nodes so that they @@ -819,13 +859,27 @@ bool XTreeSplit::SplitNonLeafNode(TreeType* tree, std::vector& relevels) * numberOfChildren. */ template -void XTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) +void XTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) { destTree->Bound() |= srcNode->Bound(); - destTree->Children().push_back(srcNode); + destTree->Children()[destTree->NumChildren()] = srcNode; destTree->NumChildren()++; } +/** + * Serialize the split. + */ +template +template +void XTreeSplit::Serialize(Archive& ar,const unsigned int /* version */) +{ + using data::CreateNVP; + + ar & CreateNVP(normalNodeMaxNumChildren, "normalNodeMaxNumChildren"); + ar & CreateNVP(splitHistory, "splitHistory"); + +} + } // namespace tree } // namespace mlpack diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp index f159796530..3c185decaf 100644 --- a/src/mlpack/methods/neighbor_search/kfn_main.cpp +++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp @@ -62,7 +62,7 @@ PARAM_INT("k", "Number of furthest neighbors to find.", "k", 0); // The user may specify the type of tree to use, and a few pararmeters for tree // building. PARAM_STRING("tree_type", "Type of tree to use: 'kd', 'cover', 'r', 'r-star', " - "'ball'.", "t", "kd"); + "'x', 'ball'.", "t", "kd"); PARAM_INT("leaf_size", "Leaf size for tree building.", "l", 20); PARAM_FLAG("random_basis", "Before tree-building, project the data onto a " "random orthogonal basis.", "R"); @@ -160,9 +160,11 @@ int main(int argc, char *argv[]) tree = KFNModel::R_STAR_TREE; else if (treeType == "ball") tree = KFNModel::BALL_TREE; + else if (treeType == "x") + tree = KFNModel::X_TREE; else Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are " - << "'kd', 'cover', 'r', 'r-star', and 'ball'." << endl; + << "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl; kfn.TreeType() = tree; kfn.RandomBasis() = randomBasis; diff --git a/src/mlpack/methods/neighbor_search/knn_main.cpp b/src/mlpack/methods/neighbor_search/knn_main.cpp index 188e92ea77..f02b96c2bc 100644 --- a/src/mlpack/methods/neighbor_search/knn_main.cpp +++ b/src/mlpack/methods/neighbor_search/knn_main.cpp @@ -63,7 +63,7 @@ PARAM_INT("k", "Number of nearest neighbors to find.", "k", 0); // The user may specify the type of tree to use, and a few parameters for tree // building. PARAM_STRING("tree_type", "Type of tree to use: 'kd', 'cover', 'r', 'r-star', " - "'ball'.", "t", "kd"); + "'x', 'ball'.", "t", "kd"); PARAM_INT("leaf_size", "Leaf size for tree building (used for kd-trees, R " "trees, and R* trees).", "l", 20); PARAM_FLAG("random_basis", "Before tree-building, project the data onto a " @@ -164,9 +164,11 @@ int main(int argc, char *argv[]) tree = KNNModel::R_STAR_TREE; else if (treeType == "ball") tree = KNNModel::BALL_TREE; + else if (treeType == "x") + tree = KNNModel::X_TREE; else Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are " - << "'kd', 'cover', 'r', 'r-star', and 'ball'." << endl; + << "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl; knn.TreeType() = tree; knn.RandomBasis() = randomBasis; diff --git a/src/mlpack/methods/neighbor_search/ns_model.hpp b/src/mlpack/methods/neighbor_search/ns_model.hpp index c788145143..df90f97968 100644 --- a/src/mlpack/methods/neighbor_search/ns_model.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model.hpp @@ -47,7 +47,8 @@ class NSModel COVER_TREE, R_TREE, R_STAR_TREE, - BALL_TREE + BALL_TREE, + X_TREE }; private: @@ -75,6 +76,7 @@ class NSModel NSType* rTreeNS; NSType* rStarTreeNS; NSType* ballTreeNS; + NSType* xTreeNS; public: /** diff --git a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp index f8e8c2f10f..b25cf0eac0 100644 --- a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp @@ -28,7 +28,8 @@ NSModel::NSModel(int treeType, bool randomBasis) : coverTreeNS(NULL), rTreeNS(NULL), rStarTreeNS(NULL), - ballTreeNS(NULL) + ballTreeNS(NULL), + xTreeNS(NULL) { // Nothing to do. } @@ -47,6 +48,8 @@ NSModel::~NSModel() delete rStarTreeNS; if (ballTreeNS) delete ballTreeNS; + if (xTreeNS) + delete xTreeNS; } //! Serialize the kNN model. @@ -72,12 +75,16 @@ void NSModel::Serialize(Archive& ar, delete rStarTreeNS; if (ballTreeNS) delete ballTreeNS; + if (xTreeNS) + delete xTreeNS; // Set all the pointers to NULL. kdTreeNS = NULL; coverTreeNS = NULL; rTreeNS = NULL; rStarTreeNS = NULL; + ballTreeNS = NULL; + xTreeNS = NULL; } // We'll only need to serialize one of the kNN objects, based on the type. @@ -99,6 +106,9 @@ void NSModel::Serialize(Archive& ar, case BALL_TREE: ar & data::CreateNVP(ballTreeNS, name); break; + case X_TREE: + ar & data::CreateNVP(xTreeNS, name); + break; } } @@ -115,6 +125,8 @@ const arma::mat& NSModel::Dataset() const return rStarTreeNS->ReferenceSet(); else if (ballTreeNS) return ballTreeNS->ReferenceSet(); + else if (xTreeNS) + return xTreeNS->ReferenceSet(); throw std::runtime_error("no neighbor search model initialized"); } @@ -133,6 +145,8 @@ bool NSModel::SingleMode() const return rStarTreeNS->SingleMode(); else if (ballTreeNS) return ballTreeNS->SingleMode(); + else if (xTreeNS) + return xTreeNS->SingleMode(); throw std::runtime_error("no neighbor search model initialized"); } @@ -150,6 +164,8 @@ bool& NSModel::SingleMode() return rStarTreeNS->SingleMode(); else if (ballTreeNS) return ballTreeNS->SingleMode(); + else if (xTreeNS) + return xTreeNS->SingleMode(); throw std::runtime_error("no neighbor search model initialized"); } @@ -167,6 +183,8 @@ bool NSModel::Naive() const return rStarTreeNS->Naive(); else if (ballTreeNS) return ballTreeNS->Naive(); + else if (xTreeNS) + return xTreeNS->Naive(); throw std::runtime_error("no neighbor search model initialized"); } @@ -184,6 +202,8 @@ bool& NSModel::Naive() return rStarTreeNS->Naive(); else if (ballTreeNS) return ballTreeNS->Naive(); + else if (xTreeNS) + return xTreeNS->Naive(); throw std::runtime_error("no neighbor search model initialized"); } @@ -238,6 +258,8 @@ void NSModel::BuildModel(arma::mat&& referenceSet, delete rStarTreeNS; if (ballTreeNS) delete ballTreeNS; + if (xTreeNS) + delete xTreeNS; // Do we need to modify the reference set? if (randomBasis) @@ -308,6 +330,11 @@ void NSModel::BuildModel(arma::mat&& referenceSet, } break; + case X_TREE: + // If necessary, build the X tree. + xTreeNS = new NSType(std::move(referenceSet), naive, + singleMode); + break; } if (!naive) @@ -412,6 +439,10 @@ void NSModel::Search(arma::mat&& querySet, ballTreeNS->Search(querySet, k, neighbors, distances); } + break; + case X_TREE: + // No mapping necessary. + xTreeNS->Search(querySet, k, neighbors, distances); break; } } @@ -447,6 +478,9 @@ void NSModel::Search(const size_t k, case BALL_TREE: ballTreeNS->Search(k, neighbors, distances); break; + case X_TREE: + xTreeNS->Search(k, neighbors, distances); + break; } } @@ -466,6 +500,8 @@ std::string NSModel::TreeName() const return "R* tree"; case BALL_TREE: return "ball tree"; + case X_TREE: + return "X tree"; default: return "unknown tree"; } diff --git a/src/mlpack/methods/range_search/range_search_main.cpp b/src/mlpack/methods/range_search/range_search_main.cpp index f012ce864a..48fd2ede30 100644 --- a/src/mlpack/methods/range_search/range_search_main.cpp +++ b/src/mlpack/methods/range_search/range_search_main.cpp @@ -70,7 +70,7 @@ PARAM_DOUBLE("min", "Lower bound in range.", "L", 0.0); // The user may specify the type of tree to use, and a few parameters for tree // building. PARAM_STRING("tree_type", "Type of tree to use: 'kd', 'cover', 'r', 'r-star', " - "'ball'.", "t", "kd"); + "'x', 'ball'.", "t", "kd"); PARAM_INT("leaf_size", "Leaf size for tree building.", "l", 20); PARAM_FLAG("random_basis", "Before tree-building, project the data onto a " "random orthogonal basis.", "R"); @@ -171,9 +171,11 @@ int main(int argc, char *argv[]) tree = RSModel::R_STAR_TREE; else if (treeType == "ball") tree = RSModel::BALL_TREE; + else if (treeType == "x") + tree = RSModel::X_TREE; else Log::Fatal << "Unknown tree type '" << treeType << "; valid choices are " - << "'kd', 'cover', 'r', 'r-star', and 'ball'." << endl; + << "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl; rs.TreeType() = tree; rs.RandomBasis() = randomBasis; diff --git a/src/mlpack/methods/range_search/rs_model.cpp b/src/mlpack/methods/range_search/rs_model.cpp index 8b735faf1d..7ffc97d41e 100644 --- a/src/mlpack/methods/range_search/rs_model.cpp +++ b/src/mlpack/methods/range_search/rs_model.cpp @@ -21,7 +21,8 @@ RSModel::RSModel(int treeType, bool randomBasis) : coverTreeRS(NULL), rTreeRS(NULL), rStarTreeRS(NULL), - ballTreeRS(NULL) + ballTreeRS(NULL), + xTreeRS(NULL) { // Nothing to do. } @@ -116,6 +117,11 @@ void RSModel::BuildModel(arma::mat&& referenceSet, } break; + + case X_TREE: + xTreeRS = new RSType(move(referenceSet), naive, + singleMode); + break; } if (!naive) @@ -221,6 +227,10 @@ void RSModel::Search(arma::mat&& querySet, ballTreeRS->Search(querySet, range, neighbors, distances); } break; + + case X_TREE: + xTreeRS->Search(querySet, range, neighbors, distances); + break; } } @@ -259,6 +269,10 @@ void RSModel::Search(const math::Range& range, case BALL_TREE: ballTreeRS->Search(range, neighbors, distances); break; + + case X_TREE: + xTreeRS->Search(range, neighbors, distances); + break; } } @@ -277,6 +291,8 @@ std::string RSModel::TreeName() const return "R* tree"; case BALL_TREE: return "ball tree"; + case X_TREE: + return "X tree"; default: return "unknown tree"; } @@ -295,10 +311,13 @@ void RSModel::CleanMemory() delete rStarTreeRS; if (ballTreeRS) delete ballTreeRS; + if (xTreeRS) + delete xTreeRS; kdTreeRS = NULL; coverTreeRS = NULL; rTreeRS = NULL; rStarTreeRS = NULL; ballTreeRS = NULL; + xTreeRS = NULL; } diff --git a/src/mlpack/methods/range_search/rs_model.hpp b/src/mlpack/methods/range_search/rs_model.hpp index a7cae2d2d6..a3827b529a 100644 --- a/src/mlpack/methods/range_search/rs_model.hpp +++ b/src/mlpack/methods/range_search/rs_model.hpp @@ -28,7 +28,8 @@ class RSModel COVER_TREE, R_TREE, R_STAR_TREE, - BALL_TREE + BALL_TREE, + X_TREE }; private: @@ -57,6 +58,8 @@ class RSModel RSType* rStarTreeRS; //! Ball tree based range search object (NULL if not in use). RSType* ballTreeRS; + //! X tree based range search object (NULL if not in use). + RSType* xTreeRS; public: /** diff --git a/src/mlpack/methods/range_search/rs_model_impl.hpp b/src/mlpack/methods/range_search/rs_model_impl.hpp index 8cf83d3a47..5a71faab4e 100644 --- a/src/mlpack/methods/range_search/rs_model_impl.hpp +++ b/src/mlpack/methods/range_search/rs_model_impl.hpp @@ -49,6 +49,10 @@ void RSModel::Serialize(Archive& ar, const unsigned int /* version */) case BALL_TREE: ar & CreateNVP(ballTreeRS, "range_search_model"); break; + + case X_TREE: + ar & CreateNVP(xTreeRS, "range_search_model"); + break; } } @@ -64,6 +68,8 @@ inline const arma::mat& RSModel::Dataset() const return rStarTreeRS->ReferenceSet(); else if (ballTreeRS) return ballTreeRS->ReferenceSet(); + else if (xTreeRS) + return xTreeRS->ReferenceSet(); throw std::runtime_error("no range search model initialized"); } @@ -80,6 +86,8 @@ inline bool RSModel::SingleMode() const return rStarTreeRS->SingleMode(); else if (ballTreeRS) return ballTreeRS->SingleMode(); + else if (xTreeRS) + return xTreeRS->SingleMode(); throw std::runtime_error("no range search model initialized"); } @@ -96,6 +104,8 @@ inline bool& RSModel::SingleMode() return rStarTreeRS->SingleMode(); else if (ballTreeRS) return ballTreeRS->SingleMode(); + else if (xTreeRS) + return xTreeRS->SingleMode(); throw std::runtime_error("no range search model initialized"); } @@ -112,6 +122,8 @@ inline bool RSModel::Naive() const return rStarTreeRS->Naive(); else if (ballTreeRS) return ballTreeRS->Naive(); + else if (xTreeRS) + return xTreeRS->Naive(); throw std::runtime_error("no range search model initialized"); } @@ -128,6 +140,8 @@ inline bool& RSModel::Naive() return rStarTreeRS->Naive(); else if (ballTreeRS) return ballTreeRS->Naive(); + else if (xTreeRS) + return xTreeRS->Naive(); throw std::runtime_error("no range search model initialized"); } diff --git a/src/mlpack/methods/rann/allkrann_main.cpp b/src/mlpack/methods/rann/allkrann_main.cpp index 430eae3338..6efb097fd4 100644 --- a/src/mlpack/methods/rann/allkrann_main.cpp +++ b/src/mlpack/methods/rann/allkrann_main.cpp @@ -64,7 +64,7 @@ PARAM_INT("k", "Number of nearest neighbors to find.", "k", 0); // The user may specify the type of tree to use, and a few parameters for tree // building. PARAM_STRING("tree_type", "Type of tree to use: 'kd', 'cover', 'r', or " - "'r-star'.", "t", "kd"); + "'x', 'r-star'.", "t", "kd"); PARAM_INT("leaf_size", "Leaf size for tree building (used for kd-trees, R " "trees, and R* trees).", "l", 20); PARAM_FLAG("random_basis", "Before tree-building, project the data onto a " @@ -170,9 +170,11 @@ int main(int argc, char *argv[]) tree = RANNModel::R_TREE; else if (treeType == "r-star") tree = RANNModel::R_STAR_TREE; + else if (treeType == "x") + tree = RANNModel::X_TREE; else Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are " - << "'kd', 'cover', 'r', and 'r-star'." << endl; + << "'kd', 'cover', 'r', 'r-star' and 'x'." << endl; rann.TreeType() = tree; rann.RandomBasis() = randomBasis; diff --git a/src/mlpack/methods/rann/ra_model.hpp b/src/mlpack/methods/rann/ra_model.hpp index 1e9fed38de..fffdb5825b 100644 --- a/src/mlpack/methods/rann/ra_model.hpp +++ b/src/mlpack/methods/rann/ra_model.hpp @@ -39,7 +39,8 @@ class RAModel KD_TREE, COVER_TREE, R_TREE, - R_STAR_TREE + R_STAR_TREE, + X_TREE }; private: @@ -70,6 +71,8 @@ class RAModel RAType* rTreeRA; //! Non-NULL if the R* tree is used. RAType* rStarTreeRA; + //! Non-NULL if the X tree is used. + RAType* xTreeRA; public: /** diff --git a/src/mlpack/methods/rann/ra_model_impl.hpp b/src/mlpack/methods/rann/ra_model_impl.hpp index e87d8edb02..72f083bb72 100644 --- a/src/mlpack/methods/rann/ra_model_impl.hpp +++ b/src/mlpack/methods/rann/ra_model_impl.hpp @@ -21,7 +21,8 @@ RAModel::RAModel(const int treeType, const bool randomBasis) : kdTreeRA(NULL), coverTreeRA(NULL), rTreeRA(NULL), - rStarTreeRA(NULL) + rStarTreeRA(NULL), + xTreeRA(NULL) { // Nothing to do. } @@ -37,6 +38,8 @@ RAModel::~RAModel() delete rTreeRA; if (rStarTreeRA) delete rStarTreeRA; + if (xTreeRA) + delete xTreeRA; } template @@ -59,12 +62,15 @@ void RAModel::Serialize(Archive& ar, delete rTreeRA; if (rStarTreeRA) delete rStarTreeRA; + if (xTreeRA) + delete xTreeRA; // Set all the pointers to NULL. kdTreeRA = NULL; coverTreeRA = NULL; rTreeRA = NULL; rStarTreeRA = NULL; + xTreeRA = NULL; } // We only need to serialize one of the kRANN objects. @@ -82,6 +88,9 @@ void RAModel::Serialize(Archive& ar, case R_STAR_TREE: ar & data::CreateNVP(rStarTreeRA, "ra_model"); break; + case X_TREE: + ar & data::CreateNVP(xTreeRA, "ra_model"); + break; } } @@ -96,6 +105,8 @@ const arma::mat& RAModel::Dataset() const return rTreeRA->ReferenceSet(); else if (rStarTreeRA) return rStarTreeRA->ReferenceSet(); + else if (xTreeRA) + return xTreeRA->ReferenceSet(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -112,6 +123,8 @@ bool RAModel::Naive() const return rTreeRA->Naive(); else if (rStarTreeRA) return rStarTreeRA->Naive(); + else if (xTreeRA) + return xTreeRA->Naive(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -128,6 +141,8 @@ bool& RAModel::Naive() return rTreeRA->Naive(); else if (rStarTreeRA) return rStarTreeRA->Naive(); + else if (xTreeRA) + return xTreeRA->Naive(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -144,6 +159,8 @@ bool RAModel::SingleMode() const return rTreeRA->SingleMode(); else if (rStarTreeRA) return rStarTreeRA->SingleMode(); + else if (xTreeRA) + return xTreeRA->SingleMode(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -160,6 +177,8 @@ bool& RAModel::SingleMode() return rTreeRA->SingleMode(); else if (rStarTreeRA) return rStarTreeRA->SingleMode(); + else if (xTreeRA) + return xTreeRA->SingleMode(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -176,6 +195,8 @@ double RAModel::Tau() const return rTreeRA->Tau(); else if (rStarTreeRA) return rStarTreeRA->Tau(); + else if (xTreeRA) + return xTreeRA->Tau(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -192,6 +213,8 @@ double& RAModel::Tau() return rTreeRA->Tau(); else if (rStarTreeRA) return rStarTreeRA->Tau(); + else if (xTreeRA) + return xTreeRA->Tau(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -208,6 +231,8 @@ double RAModel::Alpha() const return rTreeRA->Alpha(); else if (rStarTreeRA) return rStarTreeRA->Alpha(); + else if (xTreeRA) + return xTreeRA->Alpha(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -224,6 +249,8 @@ double& RAModel::Alpha() return rTreeRA->Alpha(); else if (rStarTreeRA) return rStarTreeRA->Alpha(); + else if (xTreeRA) + return xTreeRA->Alpha(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -240,6 +267,8 @@ bool RAModel::SampleAtLeaves() const return rTreeRA->SampleAtLeaves(); else if (rStarTreeRA) return rStarTreeRA->SampleAtLeaves(); + else if (xTreeRA) + return xTreeRA->SampleAtLeaves(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -256,6 +285,8 @@ bool& RAModel::SampleAtLeaves() return rTreeRA->SampleAtLeaves(); else if (rStarTreeRA) return rStarTreeRA->SampleAtLeaves(); + else if (xTreeRA) + return xTreeRA->SampleAtLeaves(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -272,6 +303,8 @@ bool RAModel::FirstLeafExact() const return rTreeRA->FirstLeafExact(); else if (rStarTreeRA) return rStarTreeRA->FirstLeafExact(); + else if (xTreeRA) + return xTreeRA->FirstLeafExact(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -288,6 +321,8 @@ bool& RAModel::FirstLeafExact() return rTreeRA->FirstLeafExact(); else if (rStarTreeRA) return rStarTreeRA->FirstLeafExact(); + else if (xTreeRA) + return xTreeRA->FirstLeafExact(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -304,6 +339,8 @@ size_t RAModel::SingleSampleLimit() const return rTreeRA->SingleSampleLimit(); else if (rStarTreeRA) return rStarTreeRA->SingleSampleLimit(); + else if (xTreeRA) + return xTreeRA->SingleSampleLimit(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -320,6 +357,8 @@ size_t& RAModel::SingleSampleLimit() return rTreeRA->SingleSampleLimit(); else if (rStarTreeRA) return rStarTreeRA->SingleSampleLimit(); + else if (xTreeRA) + return xTreeRA->SingleSampleLimit(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -383,6 +422,8 @@ void RAModel::BuildModel(arma::mat&& referenceSet, delete rTreeRA; if (rStarTreeRA) delete rStarTreeRA; + if (xTreeRA) + delete xTreeRA; if (randomBasis) referenceSet = q * referenceSet; @@ -427,6 +468,10 @@ void RAModel::BuildModel(arma::mat&& referenceSet, rStarTreeRA = new RAType(std::move(referenceSet), naive, singleMode); break; + case X_TREE: + xTreeRA = new RAType(std::move(referenceSet), naive, + singleMode); + break; } if (!naive) @@ -500,6 +545,10 @@ void RAModel::Search(arma::mat&& querySet, // No mapping necessary. rStarTreeRA->Search(querySet, k, neighbors, distances); break; + case X_TREE: + // No mapping necessary. + xTreeRA->Search(querySet, k, neighbors, distances); + break; } } @@ -531,6 +580,9 @@ void RAModel::Search(const size_t k, case R_STAR_TREE: rStarTreeRA->Search(k, neighbors, distances); break; + case X_TREE: + xTreeRA->Search(k, neighbors, distances); + break; } } @@ -547,6 +599,8 @@ std::string RAModel::TreeName() const return "R tree"; case R_STAR_TREE: return "R* tree"; + case X_TREE: + return "X tree"; default: return "unknown tree"; } diff --git a/src/mlpack/tests/allkrann_search_test.cpp b/src/mlpack/tests/allkrann_search_test.cpp index c42c10bdb1..757811a609 100644 --- a/src/mlpack/tests/allkrann_search_test.cpp +++ b/src/mlpack/tests/allkrann_search_test.cpp @@ -625,7 +625,7 @@ BOOST_AUTO_TEST_CASE(RAModelTest) data::Load("rann_test_q_3_100.csv", queryData, true); // Build all the possible models. - KNNModel models[8]; + KNNModel models[10]; models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, false); models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, true); models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false); @@ -634,13 +634,15 @@ BOOST_AUTO_TEST_CASE(RAModelTest) models[5] = KNNModel(KNNModel::TreeTypes::R_TREE, true); models[6] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, false); models[7] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, true); + models[8] = KNNModel(KNNModel::TreeTypes::X_TREE, false); + models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, true); arma::Mat qrRanks; data::Load("rann_test_qr_ranks.csv", qrRanks, true, false); // No transpose. for (size_t j = 0; j < 3; ++j) { - for (size_t i = 0; i < 8; ++i) + for (size_t i = 0; i < 10; ++i) { // We only have std::move() constructors so make a copy of our data. arma::mat referenceCopy(referenceData); diff --git a/src/mlpack/tests/knn_test.cpp b/src/mlpack/tests/knn_test.cpp index 32dcec3985..51a854aafb 100644 --- a/src/mlpack/tests/knn_test.cpp +++ b/src/mlpack/tests/knn_test.cpp @@ -975,7 +975,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - KNNModel models[10]; + KNNModel models[12]; models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true); models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false); models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true); @@ -984,8 +984,10 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) models[5] = KNNModel(KNNModel::TreeTypes::R_TREE, false); models[6] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, true); models[7] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, false); - models[8] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); - models[9] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); + models[8] = KNNModel(KNNModel::TreeTypes::X_TREE, true); + models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false); + models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); + models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -995,7 +997,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) arma::mat baselineDistances; knn.Search(queryData, 3, baselineNeighbors, baselineDistances); - for (size_t i = 0; i < 10; ++i) + for (size_t i = 0; i < 12; ++i) { // We only have std::move() constructors so make a copy of our data. arma::mat referenceCopy(referenceData); @@ -1039,7 +1041,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - KNNModel models[10]; + KNNModel models[12]; models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true); models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false); models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true); @@ -1048,8 +1050,10 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) models[5] = KNNModel(KNNModel::TreeTypes::R_TREE, false); models[6] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, true); models[7] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, false); - models[8] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); - models[0] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); + models[8] = KNNModel(KNNModel::TreeTypes::X_TREE, true); + models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false); + models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); + models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -1059,7 +1063,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) arma::mat baselineDistances; knn.Search(3, baselineNeighbors, baselineDistances); - for (size_t i = 0; i < 10; ++i) + for (size_t i = 0; i < 12; ++i) { // We only have a std::move() constructor... so copy the data. arma::mat referenceCopy(referenceData); diff --git a/src/mlpack/tests/range_search_test.cpp b/src/mlpack/tests/range_search_test.cpp index f7ed67a4b6..84cfed8dfc 100644 --- a/src/mlpack/tests/range_search_test.cpp +++ b/src/mlpack/tests/range_search_test.cpp @@ -1251,7 +1251,7 @@ BOOST_AUTO_TEST_CASE(RSModelTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - RSModel models[10]; + RSModel models[12]; models[0] = RSModel(RSModel::TreeTypes::KD_TREE, true); models[1] = RSModel(RSModel::TreeTypes::KD_TREE, false); models[2] = RSModel(RSModel::TreeTypes::COVER_TREE, true); @@ -1260,8 +1260,10 @@ BOOST_AUTO_TEST_CASE(RSModelTest) models[5] = RSModel(RSModel::TreeTypes::R_TREE, false); models[6] = RSModel(RSModel::TreeTypes::R_STAR_TREE, true); models[7] = RSModel(RSModel::TreeTypes::R_STAR_TREE, false); - models[8] = RSModel(RSModel::TreeTypes::BALL_TREE, true); - models[9] = RSModel(RSModel::TreeTypes::BALL_TREE, false); + models[8] = RSModel(RSModel::TreeTypes::X_TREE, true); + models[9] = RSModel(RSModel::TreeTypes::X_TREE, false); + models[10] = RSModel(RSModel::TreeTypes::BALL_TREE, true); + models[11] = RSModel(RSModel::TreeTypes::BALL_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -1275,7 +1277,7 @@ BOOST_AUTO_TEST_CASE(RSModelTest) vector>> baselineSorted; SortResults(baselineNeighbors, baselineDistances, baselineSorted); - for (size_t i = 0; i < 10; ++i) + for (size_t i = 0; i < 12; ++i) { // We only have std::move() constructors, so make a copy of our data. arma::mat referenceCopy(referenceData); @@ -1319,7 +1321,7 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - RSModel models[10]; + RSModel models[12]; models[0] = RSModel(RSModel::TreeTypes::KD_TREE, true); models[1] = RSModel(RSModel::TreeTypes::KD_TREE, false); models[2] = RSModel(RSModel::TreeTypes::COVER_TREE, true); @@ -1328,8 +1330,10 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest) models[5] = RSModel(RSModel::TreeTypes::R_TREE, false); models[6] = RSModel(RSModel::TreeTypes::R_STAR_TREE, true); models[7] = RSModel(RSModel::TreeTypes::R_STAR_TREE, false); - models[8] = RSModel(RSModel::TreeTypes::BALL_TREE, true); - models[9] = RSModel(RSModel::TreeTypes::BALL_TREE, false); + models[8] = RSModel(RSModel::TreeTypes::X_TREE, true); + models[9] = RSModel(RSModel::TreeTypes::X_TREE, false); + models[10] = RSModel(RSModel::TreeTypes::BALL_TREE, true); + models[11] = RSModel(RSModel::TreeTypes::BALL_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -1342,7 +1346,7 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest) vector>> baselineSorted; SortResults(baselineNeighbors, baselineDistances, baselineSorted); - for (size_t i = 0; i < 10; ++i) + for (size_t i = 0; i < 12; ++i) { // We only have std::move() cosntructors, so make a copy of our data. arma::mat referenceCopy(referenceData); diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index 56a3915dc2..9e5b2036bc 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -571,7 +571,7 @@ BOOST_AUTO_TEST_CASE(SingleTreeTraverserTest) // A test to ensure that the SingleTreeTraverser is working correctly by // comparing its results to the results of a naive search. -/** This is known to not work: see #368. +//* This is known to not work: see #368. BOOST_AUTO_TEST_CASE(XTreeTraverserTest) { arma::mat dataset; @@ -584,23 +584,19 @@ BOOST_AUTO_TEST_CASE(XTreeTraverserTest) arma::Mat neighbors2; arma::mat distances2; - typedef RectangleTree< - XTreeSplit, - arma::mat>, - RStarTreeDescentHeuristic, - NeighborSearchStat, + typedef XTree, arma::mat> TreeType; TreeType xTree(dataset, 20, 6, 5, 2, 0); // Nearest neighbor search with the X tree. - NeighborSearch, TreeType> - knn1(&xTree, dataset, true); + + NeighborSearch, arma::mat, XTree > + knn1(&xTree, true); BOOST_REQUIRE_EQUAL(xTree.NumDescendants(), numP); CheckSync(xTree); - //CheckContainment(xTree); + CheckContainment(xTree); CheckExactContainment(xTree); CheckHierarchy(xTree); @@ -617,7 +613,7 @@ BOOST_AUTO_TEST_CASE(XTreeTraverserTest) BOOST_REQUIRE_EQUAL(distances1[i], distances2[i]); } } -*/ + // Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low // to allow us to test by hand without adding hundreds of points.