From beeea6da618f195745a90e78ce5e3d7378883968 Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Tue, 24 May 2016 18:14:49 +0300 Subject: [PATCH 01/38] Added initial support for Hilbert R trees (split and descent heuristic design). --- .../hilbert_r_tree_descent_heuristic.hpp | 32 ++ .../hilbert_r_tree_descent_heuristic_impl.hpp | 45 +++ .../rectangle_tree/hilbert_r_tree_split.hpp | 70 +++++ .../hilbert_r_tree_split_impl.hpp | 287 ++++++++++++++++++ 4 files changed, 434 insertions(+) create mode 100644 src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp create mode 100644 src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp new file mode 100644 index 0000000000..a647475570 --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp @@ -0,0 +1,32 @@ +/** + * @file hilbert_r_tree_descent_heuristic.hpp + * @author Mikhail Lozhnikov + * + * Definition of HilbertRTreeDescentHeuristic, 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_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP + +#include + +namespace mlpack { +namespace tree { + +class HilbertRTreeDescentHeuristic +{ + public: + template + static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point); + + template + static size_t ChooseDescentNode(const TreeType* node, + const TreeType* insertedNode); + +}; +} // namespace tree +} // namespace mlpack + +#include "hilbert_r_tree_descent_heuristic_impl.hpp" + +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp new file mode 100644 index 0000000000..6964f94a2a --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp @@ -0,0 +1,45 @@ +/** + * @file hilbert_r_tree_descent_heuristic_impl.hpp + * @author Mikhail Lozhnikov + * + * Implementation of HilbertRTreeDescentHeuristic, 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_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP + +#include "hilbert_r_tree_descent_heuristic.hpp" + +namespace mlpack { +namespace tree { + +template +size_t HilbertRTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, const arma::vec& point) +{ + size_t bestIndex = 0; + + for(bestIndex = node->NumChildren() - 1; bestIndex > 0; bestIndex--) + if(node->Children()[bestIndex]->Split().LargestHilbertValue().CompareWithPoint(point) < 0) + break; + + return bestIndex; +} + +template +size_t HilbertRTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, + const TreeType* insertedNode) +{ + size_t bestIndex = 0; + + for(bestIndex = node->NumChildren() - 1; bestIndex > 0; bestIndex--) + if(node->Children()[bestIndex]->Split().LargestHilbertValue() < node->Split().LargestHilbertValue()) + break; + + return bestIndex; +} + + +} // namespace tree +} // namespace mlpack + +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp index 8b13789179..d5c54ef122 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp @@ -1 +1,71 @@ +/** + * @file hilbert_r_tree_split.hpp + * @author Mikhail Lozhnikov + * + * Defintion of the HilbertRTreeSplit 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_HILBERT_R_TREE_SPLIT_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_HPP + +#include + +namespace mlpack { +namespace tree /** Trees and tree-building procedures. */ { + +template +class HilbertRTreeSplit +{ + public: + //! Default constructor + HilbertRTreeSplit(); + + //! Construct this with the specified node. + HilbertRTreeSplit(const TreeType *node); + + //! Create a copy of the other.split. + HilbertRTreeSplit(const TreeType &other); + + /** + * Split a leaf node using the "default" algorithm. If necessary, this split + * will propagate upwards through the tree. + */ + 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. + */ + bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + private: + HilbertValue largestHilbertValue; + const int splitOrder = 2; + + public: + HilbertValue &LargestHilbertValue() { return largestHilbertValue }; + + HilbertValue LargestHilbertValue() { return largestHilbertValue } const; + + bool FindCooperatingSiblings(TreeType *parent,size_t iTree,size_t &firstSubling,size_t &lastSibling); + + void RedistributeNodesEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling); + + void RedistributePointsEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling); + + + public: + /** + * Serialize the split. + */ + template + void Serialize(Archive &, const unsigned int /* version */); + +}; +} // namespace tree +} // namespace mlpack + +// Include implementation +#include "hilbert_r_tree_split_impl.hpp" + +#endif diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index 8b13789179..e360206339 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -1 +1,288 @@ +/** + * @file hilbert_r_tree_split_impl.hpp + * @author Mikhail Lozhnikov + * + * Implementation of class (HilbertRTreeSplit) to split a RectangleTree. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_IMPL_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_IMPL_HPP +#include "hilbert_r_tree_split.hpp" +#include "rectangle_tree.hpp" +#include + +namespace mlpack { +namespace tree { + +template +HilbertRTreeSplit::HilbertRTreeSplit() +{ +} + +template +HilbertRTreeSplit(const TreeType *node) +{ +} + +template +HilbertRTreeSplit(const TreeType &other) +{ +} + +template +void HilbertRTreeSplit::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 + // an address of another node. + if (tree->Parent() == NULL) + { + // We actually want to copy this way. Pointers and everything. + TreeType* copy = new TreeType(*tree, false); + copy->Parent() = tree; + tree->Count() = 0; + tree->NullifyData(); + // Because this was a leaf node, numChildren must be 0. + tree->Children()[(tree->NumChildren())++] = copy; + copy->Split().SplitLeafNode(copy,relevels); + return; + } + + TreeType *parent = tree->Parent(); + + size_t iTree = 0; + for(iTree = 0;parent->Children()[iTree] != tree; iTree++); + + size_t firstSibling,lastSibling; + if(FindCooperatingSiblings(parent,iTree,firstSibling,lastSibling)) + { + RedistributePointsEvenly(parent,firstSibling,lastSibling); + return; + } + + + size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); + + for(size_t i = parent->NumChildren(); i > iNewSibling ; i--) + parent->Children()[i] = parent->Children[i-1]; + + parent->NumChildren()++; + + parent->Children()[iNewSibling] = new TreeType(parent); + + lastSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder ? lastSibling - splitOrder : 0); + + RedistributePointsEvenly(parent,firstSibling,lastSibling); + + if(parent->NumChildren() == parent->MaxNumChildren() + 1) + parent->Split().SplitNonLeafNode(parent,relevels); + +} + +template +void HilbertRTreeSplit::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 + // an address of another node. + if (tree->Parent() == NULL) + { + // We actually want to copy this way. Pointers and everything. + TreeType* copy = new TreeType(*tree, false); + copy->Parent() = tree; + tree->Count() = 0; + tree->NullifyData(); + // Because this was a leaf node, numChildren must be 0. + tree->Children()[(tree->NumChildren())++] = copy; + copy->Split().SplitLeafNode(copy,relevels); + return; + } + + TreeType *parent = tree->Parent(); + + size_t iTree = 0; + for(iTree = 0;parent->Children()[iTree] != tree; iTree++); + + size_t firstSibling,lastSibling; + if(FindCooperatingSiblings(parent,iTree,firstSibling,lastSibling)) + { + RedistributeNodesEvenly(parent,firstSibling,lastSibling); + return; + } + + size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); + + for(size_t i = parent->NumChildren(); i > iNewSibling ; i--) + parent->Children()[i] = parent->Children[i-1]; + + parent->NumChildren()++; + + parent->Children()[iNewSibling] = new TreeType(parent); + + lastSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder ? lastSibling - splitOrder : 0); + + RedistributeNodesEvenly(parent,firstSibling,lastSibling); + + if(parent->NumChildren() == parent->MaxNumChildren() + 1) + parent->Split().SplitNonLeafNode(parent,relevels); +} + +template +bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent,size_t iTree,size_t &firstSubling,size_t &lastSibling) +{ + size_t start = (iTree > splitOrder-1 ? iTree - splitOrder + 1 : 0); + size_t end = (iTree + splitOrder <= parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); + + size_t iUnderfullSibling; + if(parent->Children()[iTree]->NumChildren() != 0) + { + for(iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) + if(parent->Children()][iUnderfullSibling]->NumChildren() < parent->Children()][iUnderfullSibling]->MaxNumChildren() - 1) + break; + } + else + { + for(iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) + if(parent->Children()][iUnderfullSibling]->NumPoints() < parent->Children()][iUnderfullSibling]->MaxLeafSize() - 1) + break; + } + + if(iUnderfullSibling == end) + return false; + + if(iUnderfullSibling > iTree) + { + lastSibling = (iTree + splitOrder-1 < parent->NumChildren() ? iTree + splitOrder-1 : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder-1 ? lastSibling - splitOrder + 1 : 0); + } + else + { + lastSibling = (iUnderfullSibling + splitOrder-1 < parent->NumChildren() ? iUnderfullSibling + splitOrder-1 : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder-1 ? lastSibling - splitOrder + 1 : 0); + } + + return true; +} + +template +void HilbertRTreeSplit::RedistributeNodesEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling) +{ + size_t numChildren = 0; + size_t numChildrenPerNode,numRestChildren; + + for(size_t i = firstSibling; i <= lastSibling; i++) + numChildren += parent->Children()[i]->NumChildren(); + + numChildrenPerNode = numChildren / (lastSibling - firstSibling + 1); + numRestChildren = numChildren % (lastSibling - firstSibling + 1); + + std::vector children(numChildren); + + size_t iChild = 0; + for(size_t i = firstSibling; i <= lastSibling; i++) + { + for(size_t j = 0; j < parent->Children()[i]->NumChildren(); j++) + { + children[iChild] = parent->Children()[i]->Children()[j]; + iChild++; + } + } + + iChild = 0; + for(size_t i = firstSibling; i <= lastSibling; i++) + { + for(size_t j = 0; j < numChildrenPerNode; j++) + { + parent->Children()[i]->Children()[j] = children[iChild]; + children[iChild]->Parent() = parent->Children()[i]; + iChild++; + } + if(numRestChildren > 0) + { + parent->Children()[i]->Children()[numChildrenPerNode] = children[iChild]; + children[iChild]->Parent() = parent->Children()[i]; + parent->Children()[i]->NumChildren() = numChildrenPerNode + 1; + numRestChildren--; + iChild++; + } + else + { + parent->Children()[i]->NumChildren() = numChildrenPerNode; + } + parent->Children()[i]->Split().largestHilbertValue = children[iChild-1]->Split().largestHilbertValue; + } +} + +template +void HilbertRTreeSplit::RedistributePointsEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling) +{ + size_t numPoints = 0; + size_t numPointsPerNode,numRestPoints; + + for(size_t i = firstSibling; i <= lastSibling; i++) + numPoints += parent->Children()[i]->NumPoints(); + + numPointsPerNode = numPoints / (lastSibling - firstSibling + 1); + numRestPoints = numPoints % (lastSibling - firstSibling + 1); + + std::vector points(numPoints); + + size_t iPoint = 0; + for(size_t i = firstSibling; i <= lastSibling; i++) + { + for(size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) + { + points[iPoint] = parent->Children()[i]->Points()[j]; + iPoint++; + } + } + + iPoint = 0; + for(size_t i = firstSibling; i <= lastSibling; i++) + { + parent->Children()[i]->Bound().Clear(); + + for(size_t j = 0; j < numPointsPerNode; j++) + { + parent->Children()[i]->Bound() |= parent->Children()[i]->Dataset()->col(points[iPoint]); + parent->Children()[i]->Points()[j] = points[iPoint]; + parent->Children()[i]->LocalDataset()->col(j) = parent->Children()[i]->Dataset()->col(points[iPoint]); + iPoint++; + } + if(numRestPoints > 0) + { + parent->Children()[i]->Bound() |= parent->Children()[i]->Dataset()->col(points[iPoint]); + parent->Children()[i]->Points()[j] = points[iPoint]; + parent->Children()[i]->LocalDataset()->col(j) = parent->Children()[i]->Dataset()->col(points[iPoint]); + parent->Children()[i]->NumPoints() = numPointsPerNode + 1; + numRestPoints--; + iPoint++; + } + else + { + parent->Children()[i]->NumPoints() = numPointsPerNode; + } +// TODO: +// Adjust the largestHilbertValue +// parent->Children()[i]->Split().largestHilbertValue.AdjustValue(); + } +} + +/** + * Serialize the split. + */ +template +template +void XTreeSplit::Serialize(Archive& ar,const unsigned int /* version */) +{ + using data::CreateNVP; + + ar & CreateNVP(largestHilbertValue, "largestHilbertValue"); +} + +} // namespace tree +} // namespace mlpack + +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_IMPL_HPP From a58fed53fbd2650b8ca47e288671cfd91a2bb5cc Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Wed, 25 May 2016 19:22:40 +0300 Subject: [PATCH 02/38] Move tree-specific information to a new class (AuxiliaryInformationType). --- src/mlpack/core/tree/CMakeLists.txt | 2 + src/mlpack/core/tree/rectangle_tree.hpp | 2 + .../rectangle_tree/dual_tree_traverser.hpp | 7 +- .../dual_tree_traverser_impl.hpp | 16 +- .../no_auxiliary_information.hpp | 32 +++ .../tree/rectangle_tree/r_star_tree_split.hpp | 24 +- .../rectangle_tree/r_star_tree_split_impl.hpp | 35 +-- .../core/tree/rectangle_tree/r_tree_split.hpp | 29 +- .../tree/rectangle_tree/r_tree_split_impl.hpp | 44 +--- .../tree/rectangle_tree/rectangle_tree.hpp | 18 +- .../rectangle_tree/rectangle_tree_impl.hpp | 247 +++++++++++------- .../rectangle_tree/single_tree_traverser.hpp | 7 +- .../single_tree_traverser_impl.hpp | 16 +- .../core/tree/rectangle_tree/traits.hpp | 7 +- .../core/tree/rectangle_tree/typedef.hpp | 9 +- .../x_tree_auxiliary_information.hpp | 91 +++++++ .../core/tree/rectangle_tree/x_tree_split.hpp | 62 +---- .../tree/rectangle_tree/x_tree_split_impl.hpp | 94 +++---- 18 files changed, 395 insertions(+), 347 deletions(-) create mode 100644 src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp create mode 100644 src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp diff --git a/src/mlpack/core/tree/CMakeLists.txt b/src/mlpack/core/tree/CMakeLists.txt index 36f9e836f6..5bf5dc23b8 100644 --- a/src/mlpack/core/tree/CMakeLists.txt +++ b/src/mlpack/core/tree/CMakeLists.txt @@ -43,6 +43,7 @@ set(SOURCES rectangle_tree/dual_tree_traverser_impl.hpp rectangle_tree/r_tree_split.hpp rectangle_tree/r_tree_split_impl.hpp + rectangle_tree/no_auxiliary_information.hpp rectangle_tree/r_tree_descent_heuristic.hpp rectangle_tree/r_tree_descent_heuristic_impl.hpp rectangle_tree/r_star_tree_descent_heuristic.hpp @@ -51,6 +52,7 @@ set(SOURCES rectangle_tree/r_star_tree_split_impl.hpp rectangle_tree/x_tree_split.hpp rectangle_tree/x_tree_split_impl.hpp + rectangle_tree/x_tree_auxiliary_information.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 d3cdd5be23..725bf3c194 100644 --- a/src/mlpack/core/tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree.hpp @@ -19,10 +19,12 @@ #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/no_auxiliary_information.hpp" #include "rectangle_tree/r_tree_descent_heuristic.hpp" #include "rectangle_tree/r_star_tree_descent_heuristic.hpp" #include "rectangle_tree/traits.hpp" #include "rectangle_tree/x_tree_split.hpp" +#include "rectangle_tree/x_tree_auxiliary_information.hpp" #include "rectangle_tree/typedef.hpp" #endif 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 31067c28d7..fd44522433 100644 --- a/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser.hpp +++ b/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser.hpp @@ -19,11 +19,12 @@ namespace tree { template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> template class RectangleTree::DualTreeTraverser + DescentType, AuxiliaryInformationType>::DualTreeTraverser { public: /** diff --git a/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser_impl.hpp b/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser_impl.hpp index eba26d0dd9..c51cbfe99a 100644 --- a/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser_impl.hpp @@ -20,10 +20,12 @@ namespace tree { template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> template -RectangleTree:: +RectangleTree:: DualTreeTraverser::DualTreeTraverser(RuleType& rule) : rule(rule), numPrunes(0), @@ -35,10 +37,12 @@ DualTreeTraverser::DualTreeTraverser(RuleType& rule) : template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> template -void RectangleTree:: +void RectangleTree:: DualTreeTraverser::Traverse(RectangleTree& queryNode, RectangleTree& referenceNode) { diff --git a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp new file mode 100644 index 0000000000..6484bb31ce --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp @@ -0,0 +1,32 @@ +/** + * @file no_auxiliary_information.hpp + * @author Mikhail Lozhnikov + * + * Definition of the NoAuxiliaryInformation class, a class that provides + * no additional information about the nodes. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_NO_AUXILIARY_INFORMATION_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_NO_AUXILIARY_INFORMATION_HPP + +namespace mlpack { +namespace tree { + +template +class NoAuxiliaryInformation +{ + public: + NoAuxiliaryInformation() { }; + NoAuxiliaryInformation(const TreeType *) { }; + NoAuxiliaryInformation(const TreeType &) { }; + + /** + * Serialize the information. + */ + template + void Serialize(Archive &, const unsigned int /* version */) { }; +}; + +} // namespace tree +} // namespace mlpack + +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_NO_AUXILIARY_INFORMATION_HPP 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 d17abf64f4..b87389f43f 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,31 +18,23 @@ 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. */ - void SplitLeafNode(TreeType *tree,std::vector& relevels); + template + static 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. */ - bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + template + static bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); private: /** @@ -68,14 +60,8 @@ 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 49adbe8377..e1f2a73235 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,25 +15,6 @@ 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 @@ -41,7 +22,7 @@ RStarTreeSplit::RStarTreeSplit(const TreeType &) * 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; @@ -60,7 +41,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& r tree->Children()[(tree->NumChildren())++] = copy; assert(tree->NumChildren() == 1); - copy->Split().SplitLeafNode(copy,relevels); + RStarTreeSplit::SplitLeafNode(copy,relevels); return; } @@ -77,7 +58,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& r size_t p = tree->MaxLeafSize() * 0.3; // The paper says this works the best. if (p == 0) { - tree->Split().SplitLeafNode(tree,relevels); + RStarTreeSplit::SplitLeafNode(tree,relevels); return; } @@ -270,7 +251,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& r // just in case, we use an assert. assert(par->NumChildren() <= par->MaxNumChildren() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) - par->Split().SplitNonLeafNode(par,relevels); + RStarTreeSplit::SplitNonLeafNode(par,relevels); assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren()); assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren()); @@ -288,7 +269,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& r * 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; @@ -306,7 +287,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector tree->NullifyData(); tree->Children()[(tree->NumChildren())++] = copy; - copy->Split().SplitNonLeafNode(copy,relevels); + RStarTreeSplit::SplitNonLeafNode(copy,relevels); return true; } @@ -662,7 +643,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector assert(par->NumChildren() <= par->MaxNumChildren() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) { - par->Split().SplitNonLeafNode(par,relevels); + RStarTreeSplit::SplitNonLeafNode(par,relevels); } // We have to update the children of each of these new nodes so that they @@ -691,7 +672,7 @@ bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector * 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_split.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp index a77308a9a7..5dd3faf394 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_tree_split.hpp @@ -18,45 +18,40 @@ 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. */ - void SplitLeafNode(TreeType *tree,std::vector& relevels); + template + static 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. */ - bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + template + static 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); /** * Get the seeds for splitting a non-leaf node. */ + template 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, @@ -66,6 +61,7 @@ class RTreeSplit /** * Assign nodes to the two new nodes. */ + template static void AssignNodeDestNode(TreeType* oldTree, TreeType* treeOne, TreeType* treeTwo, @@ -75,15 +71,8 @@ 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 442e49f7ed..7673d49cdb 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,24 +14,6 @@ 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 @@ -39,7 +21,7 @@ RTreeSplit::RTreeSplit(const TreeType &) * 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 @@ -53,7 +35,7 @@ void RTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev tree->NullifyData(); // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; - copy->Split().SplitLeafNode(copy,relevels); + RTreeSplit::SplitLeafNode(copy,relevels); return; } @@ -64,7 +46,7 @@ void RTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev // rectangles, only points. We assume that the tree uses Euclidean Distance. int i = 0; int j = 0; - RTreeSplit::GetPointSeeds(tree,i, j); + RTreeSplit::GetPointSeeds(tree,i, j); TreeType* treeOne = new TreeType(tree->Parent()); TreeType* treeTwo = new TreeType(tree->Parent()); @@ -84,7 +66,7 @@ void RTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev // just in case, we use an assert. assert(par->NumChildren() <= par->MaxNumChildren() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) - par->Split().SplitNonLeafNode(par,relevels); + RTreeSplit::SplitNonLeafNode(par,relevels); assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren()); assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren()); @@ -103,7 +85,7 @@ void RTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev * 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 @@ -116,13 +98,13 @@ bool RTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re tree->NumChildren() = 0; tree->NullifyData(); tree->Children()[(tree->NumChildren())++] = copy; - copy->Split().SplitNonLeafNode(copy,relevels); + RTreeSplit::SplitNonLeafNode(copy,relevels); return true; } int i = 0; int j = 0; - RTreeSplit::GetBoundSeeds(tree,i, j); + RTreeSplit::GetBoundSeeds(tree,i, j); assert(i != j); @@ -149,7 +131,7 @@ bool RTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re assert(par->NumChildren() <= par->MaxNumChildren() + 1); if (par->NumChildren() == par->MaxNumChildren() + 1) - par->Split().SplitNonLeafNode(par,relevels); + RTreeSplit::SplitNonLeafNode(par,relevels); // We have to update the children of each of these new nodes so that they // record the correct parent. @@ -175,7 +157,7 @@ bool RTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re * 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 @@ -203,7 +185,7 @@ void RTreeSplit::GetPointSeeds(const TreeType *tree,int& iRet, int& jR * 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; @@ -234,7 +216,7 @@ void RTreeSplit::GetBoundSeeds(const TreeType *tree,int& iRet, int& jR } template -void RTreeSplit::AssignPointDestNode(TreeType* oldTree, +void RTreeSplit::AssignPointDestNode(TreeType* oldTree, TreeType* treeOne, TreeType* treeTwo, const int intI, @@ -375,7 +357,7 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, } template -void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, +void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, TreeType* treeOne, TreeType* treeTwo, const int intI, @@ -540,7 +522,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 8432f44233..b2fa544f17 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -14,6 +14,7 @@ #include "../statistic.hpp" #include "r_tree_split.hpp" #include "r_tree_descent_heuristic.hpp" +#include "no_auxiliary_information.hpp" namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { @@ -39,8 +40,9 @@ namespace tree /** Trees and tree-building procedures. */ { template class SplitType = RTreeSplit, - typename DescentType = RTreeDescentHeuristic> + typename SplitType = RTreeSplit, + typename DescentType = RTreeDescentHeuristic, + template class AuxiliaryInformationType = NoAuxiliaryInformation> class RectangleTree { // The metric *must* be the euclidean distance. @@ -91,8 +93,8 @@ class RectangleTree std::vector points; //! The local dataset MatType* localDataset; - //! The class that performs the split of the node. - SplitType split; + //! A tree-specific information + AuxiliaryInformationType auxiliaryInfo; public: //! A single traverser for rectangle type trees. See @@ -291,10 +293,12 @@ class RectangleTree //! Modify the statistic object for this node. StatisticType& Stat() { return stat; } - //! Return the split object of this node. - const SplitType& Split() const { return split; } + //! Return the auxiliary information object of this node. + const AuxiliaryInformationType& AuxiliaryInfo() const + { return auxiliaryInfo; } //! Modify the split object of this node. - SplitType& Split() { return split; } + AuxiliaryInformationType& AuxiliaryInfo() + { return auxiliaryInfo; } //! 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 8184f89c06..0c18c808ae 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -20,9 +20,11 @@ namespace tree { template class SplitType, - typename DescentType> -RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +RectangleTree:: RectangleTree(const MatType& data, const size_t maxLeafSize, const size_t minLeafSize, @@ -48,7 +50,7 @@ RectangleTree(const MatType& data, { stat = StatisticType(*this); - split = SplitType(this); + auxiliaryInfo = AuxiliaryInformationType(this); // For now, just insert the points in order. RectangleTree* root = this; @@ -60,9 +62,11 @@ RectangleTree(const MatType& data, template class SplitType, - typename DescentType> -RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +RectangleTree:: RectangleTree(MatType&& data, const size_t maxLeafSize, const size_t minLeafSize, @@ -88,7 +92,7 @@ RectangleTree(MatType&& data, { stat = StatisticType(*this); - split = SplitType(this); + auxiliaryInfo = AuxiliaryInformationType(this); // For now, just insert the points in order. RectangleTree* root = this; @@ -100,13 +104,17 @@ RectangleTree(MatType&& data, template class SplitType, - typename DescentType> -RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +RectangleTree:: RectangleTree( - RectangleTree* + RectangleTree* parentNode,const size_t numMaxChildren) : - maxNumChildren(numMaxChildren > 0 ? numMaxChildren : parentNode->MaxNumChildren()), + maxNumChildren(numMaxChildren > 0 ? numMaxChildren : + parentNode->MaxNumChildren()), minNumChildren(parentNode->MinNumChildren()), numChildren(0), children(maxNumChildren + 1), @@ -124,7 +132,7 @@ RectangleTree( maxLeafSize + 1))) { stat = StatisticType(*this); - split = SplitType(this); + auxiliaryInfo = AuxiliaryInformationType(this); } /** @@ -134,9 +142,11 @@ RectangleTree( template class SplitType, - typename DescentType> -RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +RectangleTree:: RectangleTree( const RectangleTree& other, const bool deepCopy) : @@ -156,7 +166,7 @@ RectangleTree( points(other.Points()), localDataset(NULL) { - split = SplitType(other); + auxiliaryInfo = AuxiliaryInformationType(other); if (deepCopy) { if (numChildren > 0) @@ -185,10 +195,12 @@ RectangleTree( template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> template -RectangleTree:: +RectangleTree:: RectangleTree( Archive& ar, const typename boost::enable_if::type*) : @@ -206,9 +218,11 @@ RectangleTree( template class SplitType, - typename DescentType> -RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +RectangleTree:: ~RectangleTree() { for (size_t i = 0; i < numChildren; i++) @@ -227,9 +241,11 @@ RectangleTree:: template class SplitType, - typename DescentType> -void RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +void RectangleTree:: SoftDelete() { parent = NULL; @@ -247,9 +263,11 @@ void RectangleTree:: template class SplitType, - typename DescentType> -void RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +void RectangleTree:: NullifyData() { localDataset = NULL; @@ -262,9 +280,11 @@ void RectangleTree:: template class SplitType, - typename DescentType> -void RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +void RectangleTree:: InsertPoint(const size_t point) { // Expand the bound regardless of whether it is a leaf node. @@ -299,9 +319,11 @@ void RectangleTree:: template class SplitType, - typename DescentType> -void RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +void RectangleTree:: InsertPoint(const size_t point, std::vector& relevels) { // Expand the bound regardless of whether it is a leaf node. @@ -334,9 +356,11 @@ void RectangleTree:: template class SplitType, - typename DescentType> -void RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +void RectangleTree:: InsertNode(RectangleTree* node, const size_t level, std::vector& relevels) @@ -363,9 +387,11 @@ void RectangleTree:: template class SplitType, - typename DescentType> -bool RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +bool RectangleTree:: DeletePoint(const size_t point) { // It is possible that this will cause a reinsertion, so we need to handle the @@ -408,9 +434,11 @@ bool RectangleTree:: template class SplitType, - typename DescentType> -bool RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +bool RectangleTree:: DeletePoint(const size_t point, std::vector& relevels) { if (numChildren == 0) @@ -443,9 +471,11 @@ bool RectangleTree:: template class SplitType, - typename DescentType> -bool RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +bool RectangleTree:: RemoveNode(const RectangleTree* node, std::vector& relevels) { for (size_t i = 0; i < numChildren; i++) @@ -472,10 +502,11 @@ bool RectangleTree:: template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> size_t RectangleTree::TreeSize() const + DescentType, AuxiliaryInformationType>::TreeSize() const { int n = 0; for (int i = 0; i < numChildren; i++) @@ -487,10 +518,11 @@ size_t RectangleTree class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> size_t RectangleTree::TreeDepth() const + DescentType, AuxiliaryInformationType>::TreeDepth() const { int n = 1; RectangleTree* currentNode = const_cast (this); @@ -507,10 +539,11 @@ size_t RectangleTree class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> inline bool RectangleTree::IsLeaf() const + DescentType, AuxiliaryInformationType>::IsLeaf() const { return (numChildren == 0); } @@ -522,13 +555,14 @@ inline bool RectangleTree class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> inline typename RectangleTree::ElemType + DescentType, AuxiliaryInformationType>::ElemType RectangleTree::FurthestPointDistance() const + DescentType, AuxiliaryInformationType>::FurthestPointDistance() const { if (!IsLeaf()) return 0.0; @@ -547,13 +581,14 @@ RectangleTree class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> inline typename RectangleTree::ElemType + DescentType, AuxiliaryInformationType>::ElemType RectangleTree::FurthestDescendantDistance() const + DescentType, AuxiliaryInformationType>::FurthestDescendantDistance() const { // Return the distance from the centroid to a corner of the bound. return 0.5 * bound.Diameter(); @@ -566,10 +601,11 @@ RectangleTree class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> inline size_t RectangleTree::NumPoints() const + DescentType, AuxiliaryInformationType>::NumPoints() const { if (numChildren != 0) // This is not a leaf node. return 0; @@ -583,10 +619,11 @@ inline size_t RectangleTree class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> inline size_t RectangleTree::NumDescendants() const + DescentType, AuxiliaryInformationType>::NumDescendants() const { if (numChildren == 0) { @@ -607,10 +644,11 @@ inline size_t RectangleTree class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> inline size_t RectangleTree::Descendant(const size_t index) const + DescentType, AuxiliaryInformationType>::Descendant(const size_t index) const { // I think this may be inefficient... if (numChildren == 0) @@ -639,10 +677,11 @@ inline size_t RectangleTree class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> inline size_t RectangleTree::Point(const size_t index) const + DescentType, AuxiliaryInformationType>::Point(const size_t index) const { return points[index]; } @@ -654,9 +693,11 @@ inline size_t RectangleTree class SplitType, - typename DescentType> -void RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +void RectangleTree:: SplitNode(std::vector& relevels) { if (numChildren == 0) @@ -667,7 +708,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. - split.SplitLeafNode(this,relevels); + SplitType::SplitLeafNode(this,relevels); } else { @@ -677,7 +718,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. - split.SplitNonLeafNode(this,relevels); + SplitType::SplitNonLeafNode(this,relevels); } } @@ -685,9 +726,11 @@ void RectangleTree:: template class SplitType, - typename DescentType> -RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +RectangleTree:: RectangleTree() : maxNumChildren(0), // Try to give sensible defaults, but it shouldn't matter minNumChildren(0), // because this tree isn't valid anyway and is only used @@ -712,9 +755,11 @@ RectangleTree() : template class SplitType, - typename DescentType> -void RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +void RectangleTree:: CondenseTree(const arma::vec& point, std::vector& relevels, const bool usePoint) @@ -841,9 +886,11 @@ void RectangleTree:: template class SplitType, - typename DescentType> -bool RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +bool RectangleTree:: ShrinkBoundForPoint(const arma::vec& point) { bool shrunk = false; @@ -937,9 +984,11 @@ bool RectangleTree:: template class SplitType, - typename DescentType> -bool RectangleTree:: + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> +bool RectangleTree:: ShrinkBoundForBound(const bound::HRectBound& /* b */) { // Using the sum is safe since none of the dimensions can increase. @@ -971,10 +1020,12 @@ bool RectangleTree:: template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> template -void RectangleTree:: +void RectangleTree:: Serialize(Archive& ar, const unsigned int /* version */) { @@ -1027,7 +1078,7 @@ void RectangleTree:: ar & CreateNVP(points, "points"); ar & CreateNVP(localDataset, "localDataset"); - ar & CreateNVP(split, "split"); + ar & CreateNVP(auxiliaryInfo, "auxiliaryInfo"); // 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 702d261d0c..2b10e73f64 100644 --- a/src/mlpack/core/tree/rectangle_tree/single_tree_traverser.hpp +++ b/src/mlpack/core/tree/rectangle_tree/single_tree_traverser.hpp @@ -19,11 +19,12 @@ namespace tree { template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> template class RectangleTree::SingleTreeTraverser + DescentType, AuxiliaryInformationType>::SingleTreeTraverser { public: /** diff --git a/src/mlpack/core/tree/rectangle_tree/single_tree_traverser_impl.hpp b/src/mlpack/core/tree/rectangle_tree/single_tree_traverser_impl.hpp index 17102a92a2..6ada71c335 100644 --- a/src/mlpack/core/tree/rectangle_tree/single_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/single_tree_traverser_impl.hpp @@ -20,10 +20,12 @@ namespace tree { template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> template -RectangleTree:: +RectangleTree:: SingleTreeTraverser::SingleTreeTraverser(RuleType& rule) : rule(rule), numPrunes(0) @@ -32,10 +34,12 @@ SingleTreeTraverser::SingleTreeTraverser(RuleType& rule) : template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> template -void RectangleTree:: +void RectangleTree:: SingleTreeTraverser::Traverse( const size_t queryIndex, const RectangleTree& referenceNode) diff --git a/src/mlpack/core/tree/rectangle_tree/traits.hpp b/src/mlpack/core/tree/rectangle_tree/traits.hpp index 91923cd77d..76452ecdf9 100644 --- a/src/mlpack/core/tree/rectangle_tree/traits.hpp +++ b/src/mlpack/core/tree/rectangle_tree/traits.hpp @@ -21,10 +21,11 @@ namespace tree { template class SplitType, - typename DescentType> + typename SplitType, + typename DescentType, + template class AuxiliaryInformationType> class TreeTraits> + DescentType, AuxiliaryInformationType>> { public: /** diff --git a/src/mlpack/core/tree/rectangle_tree/typedef.hpp b/src/mlpack/core/tree/rectangle_tree/typedef.hpp index b5b0fa6da8..59f74d3933 100644 --- a/src/mlpack/core/tree/rectangle_tree/typedef.hpp +++ b/src/mlpack/core/tree/rectangle_tree/typedef.hpp @@ -38,7 +38,8 @@ using RTree = RectangleTree; + RTreeDescentHeuristic, + NoAuxiliaryInformation>; /** * The R*-tree, a more recent variant of the R tree. This template typedef @@ -65,7 +66,8 @@ using RStarTree = RectangleTree; + RStarTreeDescentHeuristic, + NoAuxiliaryInformation>; /** * The X-tree, a variant of the R tree with supernodes. This template typedef @@ -90,7 +92,8 @@ using XTree = RectangleTree; + RTreeDescentHeuristic, + XTreeAuxiliaryInformation>; } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp new file mode 100644 index 0000000000..2a5af572af --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp @@ -0,0 +1,91 @@ +/** + * @file no_auxiliary_information.hpp + * @author Mikhail Lozhnikov + * + * Definition of the XTreeAuxiliaryInformation class, a class that provides + * some x-tree specific information about the nodes. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_X_TREE_AUXILIARY_INFORMATION_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_X_TREE_AUXILIARY_INFORMATION_HPP + +namespace mlpack { +namespace tree { + +template +class XTreeAuxiliaryInformation +{ + public: + XTreeAuxiliaryInformation() : + normalNodeMaxNumChildren(0), + splitHistory(0) + { }; + + XTreeAuxiliaryInformation(const TreeType *node) : + normalNodeMaxNumChildren(node->Parent() ? + node->Parent()->AuxiliaryInfo().NormalNodeMaxNumChildren() : + node->MaxNumChildren()), + splitHistory(node->Bound().Dim()) + { }; + + XTreeAuxiliaryInformation(const TreeType &other) : + normalNodeMaxNumChildren(other.AuxiliaryInfo().NormalNodeMaxNumChildren()), + splitHistory(other.AuxiliaryInfo().SplitHistory()) + { }; + + /** + * 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; + + 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 information. + */ + template + void Serialize(Archive& ar, const unsigned int /* version */) + { + using data::CreateNVP; + + ar & CreateNVP(normalNodeMaxNumChildren, "normalNodeMaxNumChildren"); + ar & CreateNVP(splitHistory, "splitHistory"); + } + +}; + +} // namespace tree +} // namespace mlpack + +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_X_TREE_AUXILIARY_INFORMATION_HPP 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 7b120a9086..edf12e385c 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_split.hpp @@ -28,61 +28,25 @@ 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. */ - void SplitLeafNode(TreeType *tree,std::vector& relevels); + template + static 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. */ - 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; + template + static bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); 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. */ @@ -107,24 +71,8 @@ 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 a619b725a5..1e02901e90 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,33 +14,6 @@ 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 @@ -48,7 +21,7 @@ XTreeSplit::XTreeSplit(const TreeType &other) : * 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; @@ -66,7 +39,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; assert(tree->NumChildren() == 1); - copy->Split().SplitLeafNode(copy,relevels); + XTreeSplit::SplitLeafNode(copy,relevels); return; } @@ -84,7 +57,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev size_t p = tree->MaxLeafSize() * 0.3; if (p == 0) { - tree->Split().SplitLeafNode(tree,relevels); + XTreeSplit::SplitLeafNode(tree,relevels); return; } @@ -245,8 +218,10 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev std::sort(sorted.begin(), sorted.end(), structComp); - TreeType* treeOne = new TreeType(tree->Parent(),NormalNodeMaxNumChildren()); - TreeType* treeTwo = new TreeType(tree->Parent(),NormalNodeMaxNumChildren()); + TreeType* treeOne = new TreeType(tree->Parent(), + tree->AuxiliaryInfo().NormalNodeMaxNumChildren()); + TreeType* treeTwo = new TreeType(tree->Parent(), + tree->AuxiliaryInfo().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 @@ -288,16 +263,16 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev par->Children()[par->NumChildren()++] = treeTwo; // We now update the split history of each new node. - treeOne->Split().SplitHistory().history[bestAxis] = true; - treeOne->Split().SplitHistory().lastDimension = bestAxis; - treeTwo->Split().SplitHistory().history[bestAxis] = true; - treeTwo->Split().SplitHistory().lastDimension = bestAxis; + treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; + treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis; + treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; + treeTwo->AuxiliaryInfo().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) - par->Split().SplitNonLeafNode(par,relevels); + XTreeSplit::SplitNonLeafNode(par,relevels); assert(treeOne->Parent()->NumChildren() <= treeOne->Parent()->MaxNumChildren()); @@ -319,7 +294,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relev * 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; @@ -336,7 +311,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re tree->NumChildren() = 0; tree->NullifyData(); tree->Children()[(tree->NumChildren())++] = copy; - copy->Split().SplitNonLeafNode(copy,relevels); + XTreeSplit::SplitNonLeafNode(copy,relevels); return true; } @@ -351,7 +326,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re 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).Split().SplitHistory().lastDimension; + dimensionsLastUsed[i] = + tree->Child(i).AuxiliaryInfo().SplitHistory().lastDimension; std::sort(dimensionsLastUsed.begin(), dimensionsLastUsed.end()); size_t lastDim = dimensionsLastUsed[dimensionsLastUsed.size()/2]; @@ -362,7 +338,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re { axes[i] = true; for (size_t j = 0; j < tree->NumChildren(); j++) - axes[i] = axes[i] & tree->Child(j).Split().SplitHistory().history[i]; + axes[i] = axes[i] & + tree->Child(j).AuxiliaryInfo().SplitHistory().history[i]; if (axes[i] == true) { minOverlapSplitDimension = i; @@ -375,7 +352,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re { axes[i] = true; for (size_t j = 0; j < tree->NumChildren(); j++) - axes[i] = axes[i] & tree->Child(j).Split().SplitHistory().history[i]; + axes[i] = axes[i] & + tree->Child(j).AuxiliaryInfo().SplitHistory().history[i]; if (axes[i] == true) { minOverlapSplitDimension = i; @@ -770,7 +748,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re (tree->Parent()->NumChildren() == 1)) { // We make the root a supernode instead. - tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() + NormalNodeMaxNumChildren(); + tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() + + tree->AuxiliaryInfo().NormalNodeMaxNumChildren(); tree->Parent()->Children().resize(tree->Parent()->MaxNumChildren() + 1); tree->Parent()->NumChildren() = tree->NumChildren(); for (size_t i = 0; i < tree->NumChildren(); i++) @@ -787,7 +766,8 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re } // If we don't have to worry about the root, we just enlarge this node. - tree->MaxNumChildren() += NormalNodeMaxNumChildren(); + tree->MaxNumChildren() += + tree->AuxiliaryInfo().NormalNodeMaxNumChildren(); tree->Children().resize(tree->MaxNumChildren() + 1); for (size_t i = 0; i < tree->NumChildren(); i++) tree->Child(i).Parent() = tree; @@ -800,10 +780,10 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re } // Update the split history of each child. - treeOne->Split().SplitHistory().history[bestAxis] = true; - treeOne->Split().SplitHistory().lastDimension = bestAxis; - treeTwo->Split().SplitHistory().history[bestAxis] = true; - treeTwo->Split().SplitHistory().lastDimension = bestAxis; + treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; + treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis; + treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true; + treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis; // Remove this node and insert treeOne and treeTwo TreeType* par = tree->Parent(); @@ -830,7 +810,7 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re if (par->NumChildren() == par->MaxNumChildren() + 1) { - par->Split().SplitNonLeafNode(par,relevels); + XTreeSplit::SplitNonLeafNode(par,relevels); } // We have to update the children of each of these new nodes so that they @@ -859,27 +839,13 @@ bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& re * numberOfChildren. */ template -void XTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) +void XTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode) { destTree->Bound() |= srcNode->Bound(); 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 From e978498cdb4f39f2b2f900fd37c11f40dfdd6dd9 Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Mon, 30 May 2016 00:13:47 +0300 Subject: [PATCH 03/38] A lot of changes. Implemented two approaches in order to compare points. Implemented DiscreteHilbertValue, RecursiveHilbertValue classes. Implemented HilbertRTreeAuxiliaryInformation with a layer of abstraction (template). --- src/mlpack/core/tree/CMakeLists.txt | 10 + src/mlpack/core/tree/rectangle_tree.hpp | 5 + .../rectangle_tree/discrete_hilbert_value.hpp | 81 +++++ .../discrete_hilbert_value_impl.hpp | 294 ++++++++++++++++++ .../hilbert_r_tree_auxiliary_information.hpp | 60 ++++ ...bert_r_tree_auxiliary_information_impl.hpp | 157 ++++++++++ .../hilbert_r_tree_descent_heuristic_impl.hpp | 4 +- .../rectangle_tree/hilbert_r_tree_split.hpp | 36 +-- .../hilbert_r_tree_split_impl.hpp | 72 ++--- .../no_auxiliary_information.hpp | 29 ++ .../rectangle_tree/rectangle_tree_impl.hpp | 82 ++++- .../recursive_hilbert_value.hpp | 113 +++++++ .../recursive_hilbert_value_impl.hpp | 209 +++++++++++++ .../core/tree/rectangle_tree/typedef.hpp | 31 ++ .../x_tree_auxiliary_information.hpp | 28 ++ 15 files changed, 1119 insertions(+), 92 deletions(-) create mode 100644 src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp create mode 100644 src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp create mode 100644 src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp create mode 100644 src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp create mode 100644 src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp create mode 100644 src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp diff --git a/src/mlpack/core/tree/CMakeLists.txt b/src/mlpack/core/tree/CMakeLists.txt index 5bf5dc23b8..28415d528d 100644 --- a/src/mlpack/core/tree/CMakeLists.txt +++ b/src/mlpack/core/tree/CMakeLists.txt @@ -53,6 +53,16 @@ set(SOURCES rectangle_tree/x_tree_split.hpp rectangle_tree/x_tree_split_impl.hpp rectangle_tree/x_tree_auxiliary_information.hpp + rectangle_tree/hilbert_r_tree_descent_heuristic.hpp + rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp + rectangle_tree/hilbert_r_tree_split.hpp + rectangle_tree/hilbert_r_tree_split_impl.hpp + rectangle_tree/hilbert_r_tree_auxiliary_information.hpp + rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp + rectangle_tree/recursive_hilbert_value.hpp + rectangle_tree/recursive_hilbert_value_impl.hpp + rectangle_tree/discrete_hilbert_value.hpp + rectangle_tree/discrete_hilbert_value_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 725bf3c194..de236ad40e 100644 --- a/src/mlpack/core/tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree.hpp @@ -25,6 +25,11 @@ #include "rectangle_tree/traits.hpp" #include "rectangle_tree/x_tree_split.hpp" #include "rectangle_tree/x_tree_auxiliary_information.hpp" +#include "rectangle_tree/hilbert_r_tree_descent_heuristic.hpp" +#include "rectangle_tree/hilbert_r_tree_split.hpp" +#include "rectangle_tree/hilbert_r_tree_auxiliary_information.hpp" +#include "rectangle_tree/recursive_hilbert_value.hpp" +#include "rectangle_tree/discrete_hilbert_value.hpp" #include "rectangle_tree/typedef.hpp" #endif diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp new file mode 100644 index 0000000000..4890a967c2 --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -0,0 +1,81 @@ +/** + * @file discrete_hilbert_value.hpp + * @author Mikhail Lozhnikov + * + * Defintion of the DiscreteHilbertValue class, a class that calculates + * the ordering of points using the Hilbert curve. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_HPP + +#include + +namespace mlpack { +namespace tree /** Trees and tree-building procedures. */ { + +class DiscreteHilbertValue +{ + public: + + DiscreteHilbertValue(); + + template + DiscreteHilbertValue(const TreeType *tree); + + template + DiscreteHilbertValue(const TreeType &other); + + ~DiscreteHilbertValue(); + + template + static int ComparePoints(const arma::Col &pt1, + const arma::Col &pt2); + + template + static int CompareValues(TreeType *tree, DiscreteHilbertValue &val1, + DiscreteHilbertValue &val2); + + template + int CompareWith(TreeType *tree, DiscreteHilbertValue &val); + + template + int CompareWith(TreeType *tree, const arma::Col &pt); + + template + size_t InsertPoint(TreeType *node, const size_t point); + + template + void InsertNode(TreeType *node); + + template + void DeletePoint(TreeType *node, const size_t localIndex); + + template + void RemoveNode(TreeType *node, const size_t nodeIndex); + + template + void Copy(TreeType *dst, TreeType *src); + + DiscreteHilbertValue operator = (DiscreteHilbertValue &val); + + std::list>::iterator LargestValue() const + { return largestValue; } + + std::list> *LocalDataset() { return localDataset; } + arma::Mat *Dataset() { return dataset; } + private: + arma::Mat *dataset; + bool ownsDataset; + std::list> *localDataset; + std::list>::iterator largestValue; + + template + static arma::Col CalculateValue(const arma::Col &pt); +}; +} // namespace tree +} // namespace mlpack + +// Include implementation +#include "discrete_hilbert_value_impl.hpp" + +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp new file mode 100644 index 0000000000..881fe51b59 --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -0,0 +1,294 @@ +/** + * @file discrete_hilbert_value.hpp + * @author Mikhail Lozhnikov + * + * Defintion of the DiscreteHilbertValue class, a class that calculates + * the ordering of points using the Hilbert curve. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_IMPL_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_IMPL_HPP + +#include "discrete_hilbert_value.hpp" + +namespace mlpack { +namespace tree /** Trees and tree-building procedures. */ { + +inline DiscreteHilbertValue::DiscreteHilbertValue() : + dataset(new arma::Mat()), + ownsDataset(true), + localDataset(new std::list>()), + largestValue(localDataset->end()) +{ + +}; + +inline DiscreteHilbertValue::~DiscreteHilbertValue() +{ + delete localDataset; + if(ownsDataset) + delete dataset; +}; + +template +DiscreteHilbertValue::DiscreteHilbertValue(const TreeType *tree) : + dataset(tree->Parent() ? + tree->Parent()->AuxiliaryInfo().LargestHilbertValue().Dataset() : + new arma::Mat(tree->Dataset()->n_rows, + tree->MaxLeafSize()+1)), + ownsDataset(!tree->Parent()), + localDataset(new std::list>()), + largestValue(localDataset->end()) +{ + if(!tree->Parent()) + { + for(size_t i = 0; i < tree->Dataset()->n_rows; i++) + dataset->col(i) = CalculateValue(tree->Dataset()->col(i)); + } +}; + +template +DiscreteHilbertValue::DiscreteHilbertValue(const TreeType &other) : + dataset(other.AuxiliaryInfo().LargestHilbertValue().Dataset()), + ownsDataset(!other.Parent()), + localDataset(other.AuxiliaryInfo().LargestHilbertValue().LocalDataset()), + largestValue(other.AuxiliaryInfo().LargestHilbertValue().LargestValue()) +{ +}; + +template +arma::Col CalculateValue(const arma::Col &pt) +{ + arma::Col res(pt.n_rows); + constexpr int order = 64; + constexpr double numPowers = + std::log2(std::numeric_limits::max_exponent - + std::numeric_limits::min_exponent + 1.0); + + constexpr int numExpBits = std::ceil(numPowers); + constexpr int numMantBits = order - numExpBits - 1; + + for(size_t i = 0; i < pt.n_rows; i++) + { + int e; + ElemType normalizedVal = std::frexp(pt(i),&e); + bool sgn = std::signbit(normalizedVal); + + if(sgn) + normalizedVal = -normalizedVal; + + if(e < std::numeric_limits::min_exponent) + { + uint64_t tmp = 1 << (std::numeric_limits::min_exponent - e); + e = std::numeric_limits::min_exponent; + normalizedVal /= tmp; + } + + uint64_t tmp = 1 << numMantBits; + res(i) = std::floor(normalizedVal / numMantBits); + res(i) |= (e - std::numeric_limits::min_exponent) << numMantBits; + + if(sgn) + res(i) = 1 << (order - 1) - 1 - res(i); + else + res(i) |= 1 << (order - 1); + } + + uint64_t M = 1 << (order - 1); + + for(uint64_t Q = M; Q > 1; Q >>= 1) + { + uint64_t P = Q - 1; + + for(size_t i = 0; i < pt.n_rows; i++) + { + if(res(i) & Q) + res(0) ^= P; + else + { + uint64_t t = (res(0) ^ res(i)) & P; + res(0) ^= t; + res(i) ^= t; + } + } + } + + for(size_t i = 1; i < pt.n_rows; i++) + res(i) ^= res(i-1); + + uint64_t t = 0; + + for(uint64_t Q = M; Q > 1; Q >>= 1) + if( res(pt.n_rows - 1) & Q) + t ^= Q - 1; + + for(size_t i = 0; i < pt.n_rows; i++) + res(i) ^= t; + + return res; +} + + +template +int DiscreteHilbertValue::ComparePoints(const arma::Col &pt1, + const arma::Col &pt2) +{ + arma::Col val1 = CalculateValue(pt1); + arma::Col val2 = CalculateValue(pt2); + + if(val1 > val2) + return 1; + else if(val2 > val1) + return -1; + return 0; +} + +template +int DiscreteHilbertValue::CompareValues(TreeType *tree, + DiscreteHilbertValue &val1, DiscreteHilbertValue &val2) +{ + if(*val1.LargestValue() > *val1.LargestValue()) + return 1; + else if(*val1.LargestValue() < *val1.LargestValue()) + return -1; + + return 0; +} + +template +int DiscreteHilbertValue::CompareWith(TreeType *tree, DiscreteHilbertValue &val) +{ + if(*largestValue > *val.LargestValue()) + return 1; + else if(*largestValue < *val.LargestValue()) + return -1; + + return 0; +} + +template +int DiscreteHilbertValue::CompareWith(TreeType *tree, + const arma::Col &pt) +{ + arma::Col val = CalculateValue(pt); + + if(*largestValue > val) + return 1; + else if(*largestValue < val) + return -1; + + return 0; +} + +template +size_t DiscreteHilbertValue::InsertPoint(TreeType *node, const size_t point) +{ + size_t i = 0; + std::list>::iterator it; + + if(node->IsLeaf()) + { + for(it = localDataset->begin(); it != localDataset->end(); it++) + { + if(*it > dataset->col(point)) + break; + i++; + } + std::list>::iterator insertedIterator = + localDataset->insert(it,dataset->col(point)); + if(it == localDataset->end()) + largestValue = insertedIterator; + + TreeType *root = node->Parent(); + + while(root != NULL) + { + if(root->AuxiliaryInfo().LargestHilbertValue().LargestValue() == + root->AuxiliaryInfo().LargestHilbertValue().LocalDataset()->end()) + root->AuxiliaryInfo().LargestHilbertValue().LargestValue() = insertedIterator; + + root = root->Parent(); + } + } + else if(largestValue != localDataset->end()) + { + if(*largestValue < dataset->col(point)) + largestValue = localDataset->end(); + } + + return i; +} + +template +void DiscreteHilbertValue::InsertNode(TreeType *node) +{ + std::list>::iterator it = + node->AuxiliaryInfo().LargestHilbertValue().LargestValue(); + + if(largestValue != localDataset->end() && + it != node->AuxiliaryInfo().LargestHilbertValue().LocalDataset()->end()) + if(*it > *largestValue) + largestValue = it; +} + +template +void DiscreteHilbertValue::DeletePoint(TreeType *node, const size_t localIndex) +{ + std::list>::iterator it = localDataset->begin(); + + for(size_t i=0; i < localIndex; i++) + it++; + localDataset->erase(it); + if(localDataset->size() == 0) + largestValue = localDataset->end(); + else + { + largestValue = localDataset->end(); + largestValue--; + } +} + +template +void DiscreteHilbertValue::RemoveNode(TreeType *node, const size_t nodeIndex) +{ + if(node->NumChildren() <= 1) + { + largestValue = localDataset->end(); + return; + } + if(nodeIndex + 1 == node->NumChildren()) + { + TreeType *child = node->Children()[nodeIndex-1]; + if(child->AuxiliaryInfo.LargestHilbertValue().LargestValue() != + child->AuxiliaryInfo.LargestHilbertValue().LocalDataset()->end()) + largestValue = child->AuxiliaryInfo.LargestHilbertValue().LargestValue(); + else + largestValue = localDataset->end(); + } +} + +template +void DiscreteHilbertValue::Copy(TreeType *dst, TreeType *src) +{ + DiscreteHilbertValue &dstVal = dst->AuxiliaryInfo().LargestHilbertValue(); + DiscreteHilbertValue &srcVal = src->AuxiliaryInfo().LargestHilbertValue(); + + dst.LargestValue() = src.LargestValue(); + + dst.LocalDataset()->clear(); + std::list>::iterator it = src.LocalDataset()->begin(); + for( ; it != src.LocalDataset()->end(); it++) + dst.LocalDataset()->push_back(*it); + +} + +inline DiscreteHilbertValue DiscreteHilbertValue::operator = (DiscreteHilbertValue &val) +{ + largestValue = val.LargestValue(); + + return *this; +} + +} // namespace tree +} // namespace mlpack + +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_IMPL_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp new file mode 100644 index 0000000000..c46846299e --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp @@ -0,0 +1,60 @@ +/** + * @file hilbert_r_tree_auxiliary_information.hpp + * @author Mikhail Lozhnikov + * + * Definition of the HilbertRTreeAuxiliaryInformation class, + * a class that provides some Hilbert r-tree specific information + * about the nodes. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_HPP + +namespace mlpack { +namespace tree { + +template +class HilbertRTreeAuxiliaryInformation +{ + public: + HilbertRTreeAuxiliaryInformation(); + + HilbertRTreeAuxiliaryInformation(const TreeType *node); + + HilbertRTreeAuxiliaryInformation(const TreeType &other); + + bool HandlePointInsertion(TreeType *node, const size_t point); + + bool HandleNodeInsertion(TreeType *node, + TreeType *nodeToInsert,bool insertionLevel); + + bool HandlePointDeletion(TreeType *node,const size_t localIndex); + + bool HandleNodeRemoval(TreeType *node,const size_t nodeIndex); + + bool ShrinkAuxiliaryInfo(TreeType *node); + + void Copy(TreeType *dst,TreeType *src); + + private: + HilbertValue largestHilbertValue; + + public: + //! Return the largest Hilbert value of a point covered by the node. + HilbertValue LargestHilbertValue() const { return largestHilbertValue; } + //! Modify the largest Hilbert value of a point covered by the node. + HilbertValue& LargestHilbertValue() { return largestHilbertValue; } + + /** + * Serialize the information. + */ + template + void Serialize(Archive& ar, const unsigned int /* version */); + +}; + +} // namespace tree +} // namespace mlpack + +#include "hilbert_r_tree_auxiliary_information_impl.hpp" + +#endif//MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp new file mode 100644 index 0000000000..836b1af4a9 --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -0,0 +1,157 @@ +/** + * @file hilbert_r_tree_auxiliary_information.hpp + * @author Mikhail Lozhnikov + * + * Implementation of the HilbertRTreeAuxiliaryInformation class, + * a class that provides some Hilbert r-tree specific information + * about the nodes. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_IMPL_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_IMPL_HPP + +#include "hilbert_r_tree_auxiliary_information.hpp" + +namespace mlpack { +namespace tree { + + +template +HilbertRTreeAuxiliaryInformation:: +HilbertRTreeAuxiliaryInformation() +{ + +}; + +template +HilbertRTreeAuxiliaryInformation:: +HilbertRTreeAuxiliaryInformation(const TreeType *node) : + largestHilbertValue(node) +{ + +}; + +template +HilbertRTreeAuxiliaryInformation:: +HilbertRTreeAuxiliaryInformation(const TreeType &other) : + largestHilbertValue(other) +{ + +}; + +template +bool HilbertRTreeAuxiliaryInformation:: +HandlePointInsertion(TreeType *node,const size_t point) +{ + if(node->IsLeaf()) + { + size_t pos = largestHilbertValue.InsertPoint(node,point); + + for(size_t i = node->NumPoints(); i > pos; i--) + { + node->Points()[i] = node->Points()[i-1]; + node->LocalDataset()->col(i) = node->LocalDataset()->col(i-1); + } + node->Points()[pos] = point; + node->LocalDataset()->col(pos) = node->Dataset()->col(point); + node->NumPoints()++; + } + else + largestHilbertValue.InsertPoint(node,point); + + return true; +} + +template +bool HilbertRTreeAuxiliaryInformation:: +HandleNodeInsertion(TreeType *node,TreeType *nodeToInsert,bool insertionLevel) +{ + if(insertionLevel) + { + size_t pos; + + for(pos = 0; pos < node->NumChildren(); pos++) + if(HilbertValue::CompareValues( + node->Children()[pos]->AuxiliaryInfo().LargestHilbertValue(), + nodeToInsert->AuxiliaryInfo().LargestHilbertValue()) < 0) + break; + + for(size_t i = node->NumChildren(); i > pos; i--) + node->Children()[i] = node->Children()[i-1]; + + node->Children()[pos] = nodeToInsert; + nodeToInsert->Parent() = node; + largestHilbertValue.InsertNode(nodeToInsert); + } + else + largestHilbertValue.InsertNode(nodeToInsert); + + return true; +} + +template +bool HilbertRTreeAuxiliaryInformation:: +HandlePointDeletion(TreeType *node,const size_t localIndex) +{ + largestHilbertValue.DeletePoint(node,localIndex); + + for(size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) + { + node->Points()[i-1] = node->Points()[i]; + node->LocalDataset()->col(i-1) = node->LocalDataset()->col(i); + } + node->NumPoints()--; + return true; +} + +template +bool HilbertRTreeAuxiliaryInformation:: +HandleNodeRemoval(TreeType *node,const size_t nodeIndex) +{ + largestHilbertValue.RemoveNode(node,nodeIndex); + + for(size_t i = nodeIndex + 1; nodeIndex < node->NumChildren(); i++) + node->Children()[i-1] = node->Children()[i]; + + node->NumChildren()--; + return true; +} + +template +bool HilbertRTreeAuxiliaryInformation:: +ShrinkAuxiliaryInfo(TreeType *node) +{ + if(node->IsLeaf()) + return true; + + TreeType *child = node->Children()[node->NumChildren()-1]; + if(HilbertValue::CompareValues(largestHilbertValue, + child->AuxiliaryInfo().LargestHilbertValue()) > 0) + { + largestHilbertValue = child->AuxiliaryInfo().LargestHilbertValue(); + return true; + } + return false; +} + +template +void HilbertRTreeAuxiliaryInformation:: +Copy(TreeType *dst,TreeType *src) +{ + largestHilbertValue.Copy(dst,src); +} + +template +template +void HilbertRTreeAuxiliaryInformation:: +Serialize(Archive& ar, const unsigned int /* version */) +{ + using data::CreateNVP; + + ar & CreateNVP(largestHilbertValue, "largestHilbertValue"); +} + + +} // namespace tree +} // namespace mlpack + +#endif//MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_IMPL_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp index 6964f94a2a..a61cf0e450 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp @@ -19,7 +19,7 @@ size_t HilbertRTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, con size_t bestIndex = 0; for(bestIndex = node->NumChildren() - 1; bestIndex > 0; bestIndex--) - if(node->Children()[bestIndex]->Split().LargestHilbertValue().CompareWithPoint(point) < 0) + if(node->Children()[bestIndex]->Split().LargestHilbertValue().CompareWith(node,point) < 0) break; return bestIndex; @@ -32,7 +32,7 @@ size_t HilbertRTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, size_t bestIndex = 0; for(bestIndex = node->NumChildren() - 1; bestIndex > 0; bestIndex--) - if(node->Children()[bestIndex]->Split().LargestHilbertValue() < node->Split().LargestHilbertValue()) + if(node->Children()[bestIndex]->Split().LargestHilbertValue().CompareWith(node,node->Split().LargestHilbertValue()) < 0) break; return bestIndex; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp index d5c54ef122..038aa491a2 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp @@ -13,53 +13,35 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { -template +const int splitOrder = 2; + class HilbertRTreeSplit { public: - //! Default constructor - HilbertRTreeSplit(); - - //! Construct this with the specified node. - HilbertRTreeSplit(const TreeType *node); - - //! Create a copy of the other.split. - HilbertRTreeSplit(const TreeType &other); - /** * Split a leaf node using the "default" algorithm. If necessary, this split * will propagate upwards through the tree. */ + template 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 bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + private: - HilbertValue largestHilbertValue; - const int splitOrder = 2; - - public: - HilbertValue &LargestHilbertValue() { return largestHilbertValue }; - - HilbertValue LargestHilbertValue() { return largestHilbertValue } const; - - bool FindCooperatingSiblings(TreeType *parent,size_t iTree,size_t &firstSubling,size_t &lastSibling); + template + bool FindCooperatingSiblings(TreeType *parent,size_t iTree,size_t &firstSibling,size_t &lastSibling); + template void RedistributeNodesEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling); + template void RedistributePointsEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling); - - public: - /** - * Serialize the split. - */ - template - void Serialize(Archive &, const unsigned int /* version */); - }; } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index e360206339..0f1a7bdbfa 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -14,23 +14,8 @@ namespace mlpack { namespace tree { -template -HilbertRTreeSplit::HilbertRTreeSplit() -{ -} - -template -HilbertRTreeSplit(const TreeType *node) -{ -} - -template -HilbertRTreeSplit(const TreeType &other) -{ -} - -template -void HilbertRTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) +template +void HilbertRTreeSplit::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 @@ -44,7 +29,7 @@ void HilbertRTreeSplit::SplitLeafNode(TreeType *tree,std: tree->NullifyData(); // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; - copy->Split().SplitLeafNode(copy,relevels); + copy->AuxiliarityInfo().SplitLeafNode(copy,relevels); return; } @@ -76,12 +61,12 @@ void HilbertRTreeSplit::SplitLeafNode(TreeType *tree,std: RedistributePointsEvenly(parent,firstSibling,lastSibling); if(parent->NumChildren() == parent->MaxNumChildren() + 1) - parent->Split().SplitNonLeafNode(parent,relevels); + parent->AuxiliarityInfo().SplitNonLeafNode(parent,relevels); } -template -void HilbertRTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) +template +bool HilbertRTreeSplit::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 @@ -90,13 +75,14 @@ void HilbertRTreeSplit::SplitNonLeafNode(TreeType *tree,s { // We actually want to copy this way. Pointers and everything. TreeType* copy = new TreeType(*tree, false); + copy->Parent() = tree; - tree->Count() = 0; + tree->NumChildren() = 0; tree->NullifyData(); - // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; - copy->Split().SplitLeafNode(copy,relevels); - return; + + HilbertRTreeSplit::SplitNonLeafNode(copy,relevels); + return true; } TreeType *parent = tree->Parent(); @@ -126,11 +112,11 @@ void HilbertRTreeSplit::SplitNonLeafNode(TreeType *tree,s RedistributeNodesEvenly(parent,firstSibling,lastSibling); if(parent->NumChildren() == parent->MaxNumChildren() + 1) - parent->Split().SplitNonLeafNode(parent,relevels); + parent->AuxiliarityInfo().SplitNonLeafNode(parent,relevels); } -template -bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent,size_t iTree,size_t &firstSubling,size_t &lastSibling) +template +bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent,size_t iTree,size_t &firstSibling,size_t &lastSibling) { size_t start = (iTree > splitOrder-1 ? iTree - splitOrder + 1 : 0); size_t end = (iTree + splitOrder <= parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); @@ -139,13 +125,13 @@ bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType if(parent->Children()[iTree]->NumChildren() != 0) { for(iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) - if(parent->Children()][iUnderfullSibling]->NumChildren() < parent->Children()][iUnderfullSibling]->MaxNumChildren() - 1) + if(parent->Children()[iUnderfullSibling]->NumChildren() < parent->Children()[iUnderfullSibling]->MaxNumChildren() - 1) break; } else { for(iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) - if(parent->Children()][iUnderfullSibling]->NumPoints() < parent->Children()][iUnderfullSibling]->MaxLeafSize() - 1) + if(parent->Children()[iUnderfullSibling]->NumPoints() < parent->Children()[iUnderfullSibling]->MaxLeafSize() - 1) break; } @@ -166,8 +152,8 @@ bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType return true; } -template -void HilbertRTreeSplit::RedistributeNodesEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling) +template +void HilbertRTreeSplit::RedistributeNodesEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling) { size_t numChildren = 0; size_t numChildrenPerNode,numRestChildren; @@ -211,12 +197,13 @@ void HilbertRTreeSplit::RedistributeNodesEvenly(const Tre { parent->Children()[i]->NumChildren() = numChildrenPerNode; } - parent->Children()[i]->Split().largestHilbertValue = children[iChild-1]->Split().largestHilbertValue; + parent->Children()[i]->AuxiliarityInfo().largestHilbertValue = + children[iChild-1]->AuxiliarityInfo().largestHilbertValue; } } -template -void HilbertRTreeSplit::RedistributePointsEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling) +template +void HilbertRTreeSplit::RedistributePointsEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling) { size_t numPoints = 0; size_t numPointsPerNode,numRestPoints; @@ -244,7 +231,8 @@ void HilbertRTreeSplit::RedistributePointsEvenly(const Tr { parent->Children()[i]->Bound().Clear(); - for(size_t j = 0; j < numPointsPerNode; j++) + size_t j; + for(j = 0; j < numPointsPerNode; j++) { parent->Children()[i]->Bound() |= parent->Children()[i]->Dataset()->col(points[iPoint]); parent->Children()[i]->Points()[j] = points[iPoint]; @@ -270,18 +258,6 @@ void HilbertRTreeSplit::RedistributePointsEvenly(const Tr } } -/** - * Serialize the split. - */ -template -template -void XTreeSplit::Serialize(Archive& ar,const unsigned int /* version */) -{ - using data::CreateNVP; - - ar & CreateNVP(largestHilbertValue, "largestHilbertValue"); -} - } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp index 6484bb31ce..2dcde7304b 100644 --- a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp @@ -19,6 +19,35 @@ class NoAuxiliaryInformation NoAuxiliaryInformation(const TreeType *) { }; NoAuxiliaryInformation(const TreeType &) { }; + bool HandlePointInsertion(TreeType *, const size_t) + { + return false; + } + + bool HandleNodeInsertion(TreeType *,TreeType *,bool) + { + return false; + } + + bool HandlePointDeletion(TreeType *,const size_t) + { + return false; + } + + bool HandleNodeRemoval(TreeType *,const size_t) + { + return false; + } + + bool ShrinkAuxiliaryInfo(TreeType *) + { + return false; + } + + void Copy(TreeType *,TreeType *) + { } + + /** * Serialize the information. */ 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 0c18c808ae..24983cfa65 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -297,14 +297,18 @@ void RectangleTreecol(count) = dataset->col(point); - points[count++] = point; + if(!auxiliaryInfo.HandlePointInsertion(this,point)) + { + localDataset->col(count) = dataset->col(point); + points[count++] = point; + } SplitNode(lvls); return; } // If it is not a leaf node, we use the DescentHeuristic to choose a child // to which we recurse. + auxiliaryInfo.HandlePointInsertion(this,point); const size_t descentNode = DescentType::ChooseDescentNode(this, dataset->col(point)); children[descentNode]->InsertPoint(point, lvls); @@ -332,14 +336,18 @@ void RectangleTreecol(count) = dataset->col(point); - points[count++] = point; + if(!auxiliaryInfo.HandlePointInsertion(this,point)) + { + localDataset->col(count) = dataset->col(point); + points[count++] = point; + } SplitNode(relevels); return; } // If it is not a leaf node, we use the DescentHeuristic to choose a child // to which we recurse. + auxiliaryInfo.HandlePointInsertion(this,point); const size_t descentNode = DescentType::ChooseDescentNode(this, dataset->col(point)); children[descentNode]->InsertPoint(point, relevels); @@ -369,12 +377,16 @@ void RectangleTreeBound(); if (level == TreeDepth()) { - children[numChildren++] = node; - node->Parent() = this; + if(!auxiliaryInfo.HandleNodeInsertion(this,node,true)) + { + children[numChildren++] = node; + node->Parent() = this; + } SplitNode(relevels); } else { + auxiliaryInfo.HandleNodeInsertion(this,node,false); const size_t descentNode = DescentType::ChooseDescentNode(this, node); children[descentNode]->InsertNode(node, level, relevels); } @@ -410,8 +422,11 @@ bool RectangleTreecol(i) = localDataset->col(--count); // Decrement count. - points[i] = points[count]; + if(!auxiliaryInfo.HandlePointDeletion(this,i)) + { + localDataset->col(i) = localDataset->col(--count); // Decrement count. + points[i] = points[count]; + } // This function wil ensure that minFill is satisfied. CondenseTree(dataset->col(point), lvls, true); return true; @@ -447,8 +462,11 @@ bool RectangleTreecol(i) = localDataset->col(--count); - points[i] = points[count]; + if(!auxiliaryInfo.HandlePointDeletion(this,i)) + { + localDataset->col(i) = localDataset->col(--count); + points[i] = points[count]; + } // This function will ensure that minFill is satisfied. CondenseTree(dataset->col(point), relevels, true); return true; @@ -482,7 +500,10 @@ bool RectangleTreeShrinkBoundForBound(bound); - // Reinsert the points at the root node. + stillShrinking = true; + root = parent; + while (root->Parent() != NULL) + { + if (stillShrinking) + stillShrinking = root->AuxiliaryInfo().ShrinkAuxiliaryInfo(root); + root = root->Parent(); + } + if (stillShrinking) + stillShrinking = root->AuxiliaryInfo().ShrinkAuxiliaryInfo(root); + + // Reinsert the points at the root node. for (size_t j = 0; j < count; j++) root->InsertPoint(points[j], relevels); @@ -813,7 +845,10 @@ void RectangleTreeChildren()[j] == this) { // Decrement numChildren. - parent->Children()[j] = parent->Children()[--parent->NumChildren()]; + if(!auxiliaryInfo.HandleNodeRemoval(parent,j)) + { + parent->Children()[j] = parent->Children()[--parent->NumChildren()]; + } size_t level = TreeDepth(); // We find the root and shrink bounds at the same time. @@ -828,6 +863,17 @@ void RectangleTreeShrinkBoundForBound(bound); + stillShrinking = true; + root = parent; + while (root->Parent() != NULL) + { + if (stillShrinking) + stillShrinking = root->AuxiliaryInfo().ShrinkAuxiliaryInfo(root); + root = root->Parent(); + } + if (stillShrinking) + stillShrinking = root->AuxiliaryInfo().ShrinkAuxiliaryInfo(root); + // Reinsert the nodes at the root node. for (size_t i = 0; i < numChildren; i++) root->InsertNode(children[i], level, relevels); @@ -867,6 +913,8 @@ void RectangleTreecol(i) = child->LocalDataset().col(i); } + auxiliaryInfo.Copy(this,child); + count = child->Count(); child->SoftDelete(); return; @@ -874,9 +922,13 @@ void RectangleTreeCondenseTree(point, relevels, usePoint); - else if (!usePoint && ShrinkBoundForBound(bound) && parent != NULL) + else if (!usePoint && + (ShrinkBoundForBound(bound) || auxiliaryInfo.ShrinkAuxiliaryInfo(this)) && + parent != NULL) parent->CondenseTree(point, relevels, usePoint); } diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp new file mode 100644 index 0000000000..1366c579df --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp @@ -0,0 +1,113 @@ +/** + * @file recursive_hilbert_value.hpp + * @author Mikhail Lozhnikov + * + * Defintion of the RecursiveHilbertValue class, a class that measures + * ordering of points recursively. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_HPP + +#include + +namespace mlpack { +namespace tree /** Trees and tree-building procedures. */ { + +class RecursiveHilbertValue +{ + public: + + RecursiveHilbertValue() : + largestValue(-1) + { }; + + template + RecursiveHilbertValue(const TreeType *) : + largestValue(-1) + { }; + + template + RecursiveHilbertValue(const TreeType &other) : + largestValue(other.AuxiliaryInfo().LargestHilbertValue().LargestValue()) + { }; + + template + struct tagCompareStruct + { + arma::Col Lo; + arma::Col Hi; + std::vector permutation; + std::vector inversion; + bool invertResult; + + tagCompareStruct(size_t dim) : + Lo(dim), + Hi(dim), + permutation(dim), + inversion(dim), + invertResult(false) + { + for(size_t i = 0; i < dim; i++) + { + Lo[i] = std::numeric_limits::lowest(); + Hi[i] = std::numeric_limits::max(); + permutation[i] = i; + inversion[i] = false; + } + } + }; + template + using CompareStruct = struct tagCompareStruct; + + + + template + static int ComparePoints(const arma::Col &pt1, + const arma::Col &pt2); + + template + static int CompareValues(TreeType *tree, RecursiveHilbertValue &val1, + RecursiveHilbertValue &val2); + + template + int CompareWith(TreeType *tree, RecursiveHilbertValue &val); + + template + int CompareWith(TreeType *tree, const arma::Col &pt); + + template + size_t InsertPoint(TreeType *node, const size_t point); + + template + void InsertNode(TreeType *node); + + template + void DeletePoint(TreeType *node, const size_t localIndex); + + template + void RemoveNode(TreeType *node, const size_t nodeIndex); + + RecursiveHilbertValue operator = (const RecursiveHilbertValue &val); + + template + void Copy(TreeType *dst, TreeType *src); + + size_t LargestValue() const { return largestValue; } + + private: + + ptrdiff_t largestValue; + + template + static int ComparePoints(const arma::Col &pt1, + const arma::Col &pt2, + CompareStruct &comp); + +}; +} // namespace tree +} // namespace mlpack + +// Include implementation +#include "recursive_hilbert_value_impl.hpp" + +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp new file mode 100644 index 0000000000..22b8b684bb --- /dev/null +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp @@ -0,0 +1,209 @@ +/** + * @file recursive_hilbert_value_impl.hpp + * @author Mikhail Lozhnikov + * + * Implementation of the RecursiveHilbertValue class, a class that measures + * ordering of points recursively. + */ +#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_IMPL_HPP +#define MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_IMPL_HPP + +namespace mlpack { +namespace tree /** Trees and tree-building procedures. */ { + + +template +int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, + const arma::Col &pt2) +{ + size_t dim = pt1.n_rows; + CompareStruct comp(dim); + + return ComparePoints(pt1,pt2,comp); +}; + +template +int RecursiveHilbertValue::CompareValues(TreeType *tree, + RecursiveHilbertValue &val1, RecursiveHilbertValue &val2) +{ + size_t point1 = val1.LargestValue(); + size_t point2 = val2.LargestValue(); + + return ComparePoints(tree->Dataset()->col(point1), + tree->Dataset()->col(point2)); +} + +template +int RecursiveHilbertValue::CompareWith(TreeType *tree, + RecursiveHilbertValue &val) +{ + return CompareValues(tree,*this,val); +} + +template +int RecursiveHilbertValue::CompareWith(TreeType *tree, + const arma::Col &pt) +{ + return ComparePoints(tree->Dataset()->col(largestValue),pt); +} + + +template +int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, + const arma::Col &pt2, + CompareStruct &comp) +{ + arma::Col center = comp.Hi * 0.5; + arma::Col vec = comp.Lo * 0.5; + std::vector bits(pt1.n_rows,0); + std::vector bits2(pt1.n_rows,0); + + center += vec; + + for(size_t i = 0; i < pt1.n_rows; i++) + { + size_t j = comp.permutation[i]; + bits[i] = (pt1(j) > center(j) && !comp.inversion[j]) || + (pt1(j) <= center(j) && !comp.inversion[j]); + + bits2[i] = (pt2(j) > center(j) && !comp.inversion[j]) || + (pt2(j) <= center(j) && !comp.inversion[j]); + } + for(size_t i = 1; i < pt1.n_rows; i++) + { + bits[i] ^= bits[i-1]; + bits2[i] ^= bits2[i-1]; + } + + if(comp.invertResult) + { + for(size_t i = 0; i < pt1.n_rows; i++) + { + bits[i] = !bits[i]; + bits2[i] = !bits2[i]; + } + } + + for(size_t i = 0; i < pt1.n_rows; i++) + { + if(bits[i] < bits2[i]) + return -1; + if(bits[i] > bits2[i]) + return 1; + } + + if(bits[pt1.n_rows-1]) + comp.invertResult = !comp.invertResult; + + for(size_t i = 0; i < pt1.n_rows; i++) + { + size_t j = comp.permutation[i]; + size_t j0 = comp.permutation[0]; + if((pt1(j) > center(j) && !comp.inversion[j]) || + (pt1(j) <= center(j) && !comp.inversion[j])) + comp.inversion[j0] = !comp.inversion[j0]; + else + { + size_t tmp; + tmp = comp.permutation[0]; + comp.permutation[0] = comp.permutation[i]; + comp.permutation[i] = tmp; + } + } + + for(size_t i = 0; i < pt1.n_rows; i++) + { + if(pt1(i) > center(i)) + comp.Lo(i) = center(i); + else + comp.Hi(i) = center(i); + } + + return ComparePoints(pt1,pt2,comp); +} + +template +size_t RecursiveHilbertValue::InsertPoint(TreeType *node, const size_t point) +{ + if(node->IsLeaf()) + { + size_t i; + + for(i = 0; i < node->NumPoints(); i++) + if(ComparePoints(node->LocalDataset()->col(i), + node->Dataset()->col(point)) > 0) + break; + if(i == node->NumPoints()) + largestValue = point; + + return i; + } + else + { + if(largestValue < 0) + { + largestValue = point; + return 0; + } + if(ComparePoints(node->Dataset()->col(point), + node->Dataset()->col(largestValue)) > 0) + largestValue = point; + } + return 0; +} + +template +void RecursiveHilbertValue::InsertNode(TreeType *node) +{ + size_t point = node->AuxiliaryInfo().LargestHilbertValue().LargestValue(); + + if(ComparePoints(node->Dataset()->col(point), + node->Dataset()->col(largestValue)) > 0) + largestValue = point; +} + +template +void RecursiveHilbertValue::DeletePoint(TreeType *node, const size_t localIndex) +{ + if(node->NumPoints() <= 1) + { + largestValue = -1; + return; + } + if(localIndex + 1 == node->NumPoints()) + largestValue = node->Points()[localIndex-1]; + +} + +template +void RecursiveHilbertValue::RemoveNode(TreeType *node, const size_t nodeIndex) +{ + if(node->NumChildren() <= 1) + { + largestValue = -1; + return; + } + if(nodeIndex + 1 == node->NumChildren()) + largestValue = node->Children()[nodeIndex-1]->AuxiliaryInfo.LargestHilbertValue().LargestValue(); + +} + +inline RecursiveHilbertValue RecursiveHilbertValue::operator = (const RecursiveHilbertValue &val) +{ + largestValue = val.LargestValue(); + + return *this; +} + +template +void RecursiveHilbertValue::Copy(TreeType *dst, TreeType *src) +{ + dst->AuxiliaryInfo().LargestHilbertValue().LargestValue() = + src->AuxiliaryInfo().LargestHilbertValue().LargestValue(); +} + + +} // namespace tree +} // namespace mlpack + +#endif //MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_IMPL_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/typedef.hpp b/src/mlpack/core/tree/rectangle_tree/typedef.hpp index 59f74d3933..f44eb18fca 100644 --- a/src/mlpack/core/tree/rectangle_tree/typedef.hpp +++ b/src/mlpack/core/tree/rectangle_tree/typedef.hpp @@ -95,6 +95,37 @@ using XTree = RectangleTree; +/** + * The Hilbert R-tree, a variant of the R tree with an ordering along the Hilbert curve. This template typedef + * satisfies the TreeType policy API. + * + */ + +template +using RecursiveHilbertRTreeAuxiliaryInformation = + HilbertRTreeAuxiliaryInformation; + +template +using HilbertRTree = RectangleTree; + +template +using DiscreteHilbertRTreeAuxiliaryInformation = + HilbertRTreeAuxiliaryInformation; + +template +using DiscreteHilbertRTree = RectangleTree; + + } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp index 2a5af572af..4d9d4c4527 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp @@ -32,6 +32,34 @@ class XTreeAuxiliaryInformation splitHistory(other.AuxiliaryInfo().SplitHistory()) { }; + bool HandlePointInsertion(TreeType *, const size_t) + { + return false; + } + + bool HandleNodeInsertion(TreeType *,TreeType *,bool) + { + return false; + } + + bool HandlePointDeletion(TreeType *,const size_t) + { + return false; + } + + bool HandleNodeRemoval(TreeType *,const size_t) + { + return false; + } + + bool ShrinkAuxiliaryInfo(TreeType *) + { + return false; + } + + void Copy(TreeType *,TreeType *) + { } + /** * The X tree requires that the tree records it's "split history". To make * this easy, we use the following structure. From 1d7ec250470d3f74884f2f05e7d0167e54d4afbc Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Wed, 1 Jun 2016 10:49:19 +0300 Subject: [PATCH 04/38] Added a bit of documentation. A lot of style fixes. A lot of bug fixes. Added tests. --- .../rectangle_tree/discrete_hilbert_value.hpp | 113 +++++++++++++- .../discrete_hilbert_value_impl.hpp | 123 +++++++++++---- .../hilbert_r_tree_auxiliary_information.hpp | 60 +++++++- ...bert_r_tree_auxiliary_information_impl.hpp | 36 +++-- .../hilbert_r_tree_descent_heuristic.hpp | 29 +++- .../hilbert_r_tree_descent_heuristic_impl.hpp | 28 +++- .../rectangle_tree/hilbert_r_tree_split.hpp | 45 +++++- .../hilbert_r_tree_split_impl.hpp | 134 +++++++++++----- .../no_auxiliary_information.hpp | 25 ++- .../r_star_tree_descent_heuristic.hpp | 13 ++ .../r_star_tree_descent_heuristic_impl.hpp | 9 ++ .../r_tree_descent_heuristic.hpp | 13 ++ .../r_tree_descent_heuristic_impl.hpp | 7 + .../rectangle_tree/rectangle_tree_impl.hpp | 18 +-- .../recursive_hilbert_value.hpp | 112 +++++++++++++- .../recursive_hilbert_value_impl.hpp | 56 +++++-- .../core/tree/rectangle_tree/typedef.hpp | 2 +- .../x_tree_auxiliary_information.hpp | 45 +++++- src/mlpack/tests/rectangle_tree_test.cpp | 145 ++++++++++++++++++ 19 files changed, 892 insertions(+), 121 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp index 4890a967c2..6bcee8f850 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -16,61 +16,172 @@ namespace tree /** Trees and tree-building procedures. */ { class DiscreteHilbertValue { public: - + //! Default constructor DiscreteHilbertValue(); + /** + * Construct this for the node tree. If the node is the root this method + * computes the Hilbert value for each point in the tree's dataset. + * @param node The node that stores this Hilbert value. + */ template DiscreteHilbertValue(const TreeType *tree); + /** + * Create a Hilbert value object by copying from the other node. + * @param other The node from which the value will be copied. + */ template DiscreteHilbertValue(const TreeType &other); + //! Free memory ~DiscreteHilbertValue(); + /** + * Compare two points. It returns 1 if the first point is greater than + * the second one, -1 if the first point is less than the second one and + * 0 if the Hilbert values of the points are equal. In order to do it + * this method computes the Hilbert values of the points. + * @param pt1 The first point. + * @param pt2 The second point. + */ template static int ComparePoints(const arma::Col &pt1, const arma::Col &pt2); + /** + * Compare two Hilbert values. It returns 1 if the first value is greater than + * the second one, -1 if the first value is less than the second one and + * 0 if the values are equal. This method does not compute the Hilbert values. + * @param val1 The first point. + * @param val2 The second point. + */ template static int CompareValues(TreeType *tree, DiscreteHilbertValue &val1, DiscreteHilbertValue &val2); + /** + * Compare the largest Hilbert value of the node with the val value. + * It returns 1 if the value of the node is greater than val, + * -1 if the value of the node is less than val and + * 0 if the values are equal. This method does not compute the Hilbert values. + * @param tree Not used + * @param val The Hilbert value to compare with. + */ template int CompareWith(TreeType *tree, DiscreteHilbertValue &val); + /** + * Compare the largest Hilbert value of the node with the Hilbert value + * of the point. It returns 1 if the value of the node is greater than + * the value of the point, -1 if the value of the node is less than + * the value of the point and 0 if the values are equal. + * This method computes the Hilbert value of the point. + * @param tree Not used + * @param val The point to compare with. + */ template int CompareWith(TreeType *tree, const arma::Col &pt); + /** + * Compare the largest Hilbert value of the node with the Hilbert value + * of the point. It returns 1 if the value of the node is greater than + * the value of the point, -1 if the value of the node is less than + * the value of the point and 0 if the values are equal. + * This method computes the Hilbert value of the point. + * @param tree Not used + * @param val The number of the point to compare with. + */ + template + int CompareWith(TreeType *tree, const size_t point); + + /** + * Update the largest Hilbert value of the node and insert the point + * in the local dataset if the node is a leaf. + * @param node The node in which the point is being inserted. + * @param point The number of the point being inserted. + */ template size_t InsertPoint(TreeType *node, const size_t point); + /** + * Update the largest Hilbert value of the node. + * @param node The node being inserted. + */ template void InsertNode(TreeType *node); + /** + * Update the largest Hilbert value of the node and delete the point + * from the local dataset. + * @param node The node from which the point is being deleted. + * @param localIndex The number of the point in the local dataset. + */ template void DeletePoint(TreeType *node, const size_t localIndex); + /** + * Update the largest Hilbert value of the node. + * @param node The node from which another node is being deleted. + * @param nodeIndex The number of the node being deleted. + */ template void RemoveNode(TreeType *node, const size_t nodeIndex); + /** + * Copy the largest Hilbert value and the local dataset + * @param dst The node to which the information is being copied. + * @param src The node from which the information is being copied. + */ template void Copy(TreeType *dst, TreeType *src); + /** + * Update the largest Hilbert value and the local dataset. + * The children of the node (or the points that the node contains) should be + * arranged according to their Hilbert values. + * @param node The node in which the information should be updated. + */ + template + void UpdateLargestValue(TreeType *node); + + //! Copy the largest Hilbert value. DiscreteHilbertValue operator = (DiscreteHilbertValue &val); + //! Return the largest Hilbert value std::list>::iterator LargestValue() const { return largestValue; } + //! Modify the local dataset std::list> *LocalDataset() { return localDataset; } + //! Modify the dataset arma::Mat *Dataset() { return dataset; } private: + //! The dataset arma::Mat *dataset; + //! Indicates that the node owns the dataset bool ownsDataset; + //! The local dataset std::list> *localDataset; + //! The largest Hilbert value std::list>::iterator largestValue; + /** + * Calculate the Hilbert value of the point pt. + * @param pt The point for which the Hilbert value should be calculated. + */ template static arma::Col CalculateValue(const arma::Col &pt); + + /** + * Compare two Hilbert values. It returns 1 if the first value is greater than + * the second one, -1 if the first value is less than the second one and + * 0 if the values are equal. This method does not compute the Hilbert values. + * @param value1 The first value. + * @param value2 The second value. + */ + static int CompareValues(const arma::Col &value1, + const arma::Col &value2); }; } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index 881fe51b59..ce4df87805 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -39,6 +39,7 @@ DiscreteHilbertValue::DiscreteHilbertValue(const TreeType *tree) : localDataset(new std::list>()), largestValue(localDataset->end()) { + // Calculate the Hilbert value for all points if(!tree->Parent()) { for(size_t i = 0; i < tree->Dataset()->n_rows; i++) @@ -59,12 +60,14 @@ template arma::Col CalculateValue(const arma::Col &pt) { arma::Col res(pt.n_rows); - constexpr int order = 64; + constexpr int order = 64; // The number of bits that we can store constexpr double numPowers = std::log2(std::numeric_limits::max_exponent - std::numeric_limits::min_exponent + 1.0); + // The number of bits for the exponent constexpr int numExpBits = std::ceil(numPowers); + // The number of bits for the mantissa constexpr int numMantBits = order - numExpBits - 1; for(size_t i = 0; i < pt.n_rows; i++) @@ -82,11 +85,13 @@ arma::Col CalculateValue(const arma::Col &pt) e = std::numeric_limits::min_exponent; normalizedVal /= tmp; } - + // Extract the mantissa uint64_t tmp = 1 << numMantBits; res(i) = std::floor(normalizedVal / numMantBits); + // Add the exponent res(i) |= (e - std::numeric_limits::min_exponent) << numMantBits; + // Negative values should be inverted if(sgn) res(i) = 1 << (order - 1) - 1 - res(i); else @@ -95,15 +100,17 @@ arma::Col CalculateValue(const arma::Col &pt) uint64_t M = 1 << (order - 1); + // Since the Hilbert curve is continuous we should permutate and intend + // coordinate axes depending on the position of the point for(uint64_t Q = M; Q > 1; Q >>= 1) { uint64_t P = Q - 1; for(size_t i = 0; i < pt.n_rows; i++) { - if(res(i) & Q) + if(res(i) & Q) // Invert res(0) ^= P; - else + else // Permutate { uint64_t t = (res(0) ^ res(i)) & P; res(0) ^= t; @@ -112,11 +119,13 @@ arma::Col CalculateValue(const arma::Col &pt) } } + // Gray encode for(size_t i = 1; i < pt.n_rows; i++) res(i) ^= res(i-1); uint64_t t = 0; + // Some coordinate axes should be inverted for(uint64_t Q = M; Q > 1; Q >>= 1) if( res(pt.n_rows - 1) & Q) t ^= Q - 1; @@ -124,9 +133,37 @@ arma::Col CalculateValue(const arma::Col &pt) for(size_t i = 0; i < pt.n_rows; i++) res(i) ^= t; - return res; + // We should rearrange bits in order to compare two Hilbert values faster + arma::Col rearrangedResult(pt.n_rows,arma::fill::zeros); + + for(size_t i = 0; i < order; i++) + for(size_t j = 0; j < pt.n_rows; j++) + { + size_t bit = (i * pt.n_rows + j) % order; + size_t row = (i * pt.n_rows + j) / order; + + rearrangedResult(row) |= (res(j) & (1 << i)) >> (i - bit); + } + + return rearrangedResult; } +inline int DiscreteHilbertValue:: +CompareValues(const arma::Col &value1, + const arma::Col &value2) +{ + for(size_t i = 0;i < value1.n_rows; i++) + { + if(value1(i) > value2(i)) + return 1; + else if(value1(i) < value2(i)) + return -1; + } + + return 0; +} + + template int DiscreteHilbertValue::ComparePoints(const arma::Col &pt1, @@ -135,34 +172,20 @@ int DiscreteHilbertValue::ComparePoints(const arma::Col &pt1, arma::Col val1 = CalculateValue(pt1); arma::Col val2 = CalculateValue(pt2); - if(val1 > val2) - return 1; - else if(val2 > val1) - return -1; - return 0; + return CompareValues(val1,val2); } template int DiscreteHilbertValue::CompareValues(TreeType *tree, DiscreteHilbertValue &val1, DiscreteHilbertValue &val2) { - if(*val1.LargestValue() > *val1.LargestValue()) - return 1; - else if(*val1.LargestValue() < *val1.LargestValue()) - return -1; - - return 0; + return CompareValues(*val1.LargestValue(),*val2.LargestValue()); } template int DiscreteHilbertValue::CompareWith(TreeType *tree, DiscreteHilbertValue &val) { - if(*largestValue > *val.LargestValue()) - return 1; - else if(*largestValue < *val.LargestValue()) - return -1; - - return 0; + return CompareValues(*largestValue,*val.LargestValue()); } template @@ -171,12 +194,14 @@ int DiscreteHilbertValue::CompareWith(TreeType *tree, { arma::Col val = CalculateValue(pt); - if(*largestValue > val) - return 1; - else if(*largestValue < val) - return -1; + return CompareValues(*largestValue,val); +} - return 0; +template +int DiscreteHilbertValue::CompareWith(TreeType *tree, + const size_t point) +{ + return CompareValues(*largestValue,dataset->col(point)); } template @@ -187,17 +212,20 @@ size_t DiscreteHilbertValue::InsertPoint(TreeType *node, const size_t point) if(node->IsLeaf()) { + // Find an appropriate place for(it = localDataset->begin(); it != localDataset->end(); it++) { - if(*it > dataset->col(point)) + if(CompareValues(*it, dataset->col(point)) > 0) break; i++; } std::list>::iterator insertedIterator = localDataset->insert(it,dataset->col(point)); + // Update the largest Hilbert value if(it == localDataset->end()) largestValue = insertedIterator; + // Propogate changes of the largest Hilbert value downward TreeType *root = node->Parent(); while(root != NULL) @@ -211,6 +239,8 @@ size_t DiscreteHilbertValue::InsertPoint(TreeType *node, const size_t point) } else if(largestValue != localDataset->end()) { + // We do not update the largest Hilbert value since we do not know the + // iterator if(*largestValue < dataset->col(point)) largestValue = localDataset->end(); } @@ -224,6 +254,7 @@ void DiscreteHilbertValue::InsertNode(TreeType *node) std::list>::iterator it = node->AuxiliaryInfo().LargestHilbertValue().LargestValue(); + // Update the largest Hilbert value if(largestValue != localDataset->end() && it != node->AuxiliaryInfo().LargestHilbertValue().LocalDataset()->end()) if(*it > *largestValue) @@ -235,9 +266,12 @@ void DiscreteHilbertValue::DeletePoint(TreeType *node, const size_t localIndex) { std::list>::iterator it = localDataset->begin(); + // Delete the Hilbert value from the local dataset for(size_t i=0; i < localIndex; i++) it++; localDataset->erase(it); + + // Update the largest Hilbert value if(localDataset->size() == 0) largestValue = localDataset->end(); else @@ -257,6 +291,7 @@ void DiscreteHilbertValue::RemoveNode(TreeType *node, const size_t nodeIndex) } if(nodeIndex + 1 == node->NumChildren()) { + // Update the largest Hilbert value if the value exists TreeType *child = node->Children()[nodeIndex-1]; if(child->AuxiliaryInfo.LargestHilbertValue().LargestValue() != child->AuxiliaryInfo.LargestHilbertValue().LocalDataset()->end()) @@ -272,6 +307,7 @@ void DiscreteHilbertValue::Copy(TreeType *dst, TreeType *src) DiscreteHilbertValue &dstVal = dst->AuxiliaryInfo().LargestHilbertValue(); DiscreteHilbertValue &srcVal = src->AuxiliaryInfo().LargestHilbertValue(); + // Copy the largest Hilbert value and the local dataset dst.LargestValue() = src.LargestValue(); dst.LocalDataset()->clear(); @@ -283,11 +319,42 @@ void DiscreteHilbertValue::Copy(TreeType *dst, TreeType *src) inline DiscreteHilbertValue DiscreteHilbertValue::operator = (DiscreteHilbertValue &val) { + // Copy the largest Hilbert value largestValue = val.LargestValue(); return *this; } +template +void DiscreteHilbertValue::UpdateLargestValue(TreeType *node) +{ + if(node->IsLeaf()) + { + // Update the largest Hilbert value and the local dataset + localDataset->clear(); + if(node->NumPoints() == 0) + { + largestValue = localDataset->end(); + return; + } + for(size_t i = 0; i < node->NumPoints(); i++) + localDataset->push_back(dataset->col(node->Points()[i])); + largestValue = localDataset->end(); + localDataset--; + } + else + { + // Update the largest Hilbert value; + if(node->NumChildren() == 0) + largestValue = localDataset->end(); + else if(node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue().LargestValue() != + node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue().LocalDataset()->end()) + largestValue = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue(); + else + largestValue = localDataset->end(); + } +} + } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp index c46846299e..4d3070c1bb 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp @@ -16,26 +16,82 @@ template class HilbertRTreeAuxiliaryInformation { public: + //! Default constructor HilbertRTreeAuxiliaryInformation(); + /** + * Construct this as an axiliary information for the node node. + * @param node The node that stores this auxiliary information. + */ HilbertRTreeAuxiliaryInformation(const TreeType *node); + /** + * Create an auxiliary information object by copying from the other node. + * @param other The node from which the information will be copied. + */ HilbertRTreeAuxiliaryInformation(const TreeType &other); - + + /** + * The Hilbert R tree requires to insert points according to their + * Hilbert value. This method should take care of it. + * It returns false if it does nothing and true if it handles + * the insertion process. + * @param node The node in which the point is being inserted. + * @param point The number of the point being inserted. + */ bool HandlePointInsertion(TreeType *node, const size_t point); + /** + * The Hilbert R tree requires to insert nodes according to their + * Hilbert value. This method should take care of it. + * It returns false if it does nothing and true if it handles + * the insertion process. + * @param node The node in which the nodeToInsert is being inserted. + * @param nodeToInsert The node being inserted. + * @param insertionLevel The level of the tree at which the nodeToInsert + * should be inserted. + */ bool HandleNodeInsertion(TreeType *node, TreeType *nodeToInsert,bool insertionLevel); + /** + * The Hilbert R tree requires all points to be arranged according to their + * Hilbert value. This method should take care of saving this property + * after the deletion process. + * It returns false if it does nothing and true if it handles + * the deletion process. + * @param node The node from which the point is being deleted. + * @param localIndex The index of the point being deleted. + */ bool HandlePointDeletion(TreeType *node,const size_t localIndex); + /** + * The Hilbert R tree requires all nodes to be arranged according to their + * Hilbert value. This method should take care of saving this property + * after the deletion process. + * It returns false if it does nothing and true if it handles + * the deletion process. + * @param node The node from which the node is being deleted. + * @param nodeIndex The index of the node being deleted. + */ bool HandleNodeRemoval(TreeType *node,const size_t nodeIndex); - bool ShrinkAuxiliaryInfo(TreeType *node); + /** + * Update the auxiliary information in the node. The method returns true + * if the update should be propogated downward. + * @param node The node in which the auxiliary information being update. + */ + bool UpdateAuxiliaryInfo(TreeType *node); + /** + * Copy the auxiliary information from one node to another. + * @param dst The node to which the information is being copied. + * @param src The node from which the information is being copied. + */ void Copy(TreeType *dst,TreeType *src); private: + //! The largest Hilbert value of a point enclosed by the node. HilbertValue largestHilbertValue; public: diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index 836b1af4a9..bd190d587d 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -44,19 +44,23 @@ HandlePointInsertion(TreeType *node,const size_t point) { if(node->IsLeaf()) { + // Get the position at which the point should be inserted + // Update the largest Hilbert value of the node size_t pos = largestHilbertValue.InsertPoint(node,point); - + + // Move points for(size_t i = node->NumPoints(); i > pos; i--) { node->Points()[i] = node->Points()[i-1]; - node->LocalDataset()->col(i) = node->LocalDataset()->col(i-1); + node->LocalDataset().col(i) = node->LocalDataset().col(i-1); } + // Insert the point node->Points()[pos] = point; - node->LocalDataset()->col(pos) = node->Dataset()->col(point); - node->NumPoints()++; + node->LocalDataset().col(pos) = node->Dataset().col(point); + node->Count()++; } else - largestHilbertValue.InsertPoint(node,point); + largestHilbertValue.InsertPoint(node,point); // Update LHV return true; } @@ -68,22 +72,28 @@ HandleNodeInsertion(TreeType *node,TreeType *nodeToInsert,bool insertionLevel) if(insertionLevel) { size_t pos; - + + // Find the best position for the node being inserted. + // The node should be inserted according to its Hilbert value. for(pos = 0; pos < node->NumChildren(); pos++) if(HilbertValue::CompareValues( node->Children()[pos]->AuxiliaryInfo().LargestHilbertValue(), nodeToInsert->AuxiliaryInfo().LargestHilbertValue()) < 0) break; - + + // Move nodes for(size_t i = node->NumChildren(); i > pos; i--) node->Children()[i] = node->Children()[i-1]; - + + // Insert the node node->Children()[pos] = nodeToInsert; nodeToInsert->Parent() = node; + + // Update the largest Hilbert value largestHilbertValue.InsertNode(nodeToInsert); } else - largestHilbertValue.InsertNode(nodeToInsert); + largestHilbertValue.InsertNode(nodeToInsert); // Update LHV return true; } @@ -92,6 +102,7 @@ template bool HilbertRTreeAuxiliaryInformation:: HandlePointDeletion(TreeType *node,const size_t localIndex) { + // Update the largest Hilbert value largestHilbertValue.DeletePoint(node,localIndex); for(size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) @@ -107,6 +118,7 @@ template bool HilbertRTreeAuxiliaryInformation:: HandleNodeRemoval(TreeType *node,const size_t nodeIndex) { + // Update the largest Hilbert value largestHilbertValue.RemoveNode(node,nodeIndex); for(size_t i = nodeIndex + 1; nodeIndex < node->NumChildren(); i++) @@ -118,14 +130,14 @@ HandleNodeRemoval(TreeType *node,const size_t nodeIndex) template bool HilbertRTreeAuxiliaryInformation:: -ShrinkAuxiliaryInfo(TreeType *node) +UpdateAuxiliaryInfo(TreeType *node) { - if(node->IsLeaf()) + if(node->IsLeaf()) // Should already be updated return true; TreeType *child = node->Children()[node->NumChildren()-1]; if(HilbertValue::CompareValues(largestHilbertValue, - child->AuxiliaryInfo().LargestHilbertValue()) > 0) + child->AuxiliaryInfo().LargestHilbertValue()) < 0) { largestHilbertValue = child->AuxiliaryInfo().LargestHilbertValue(); return true; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp index a647475570..51bafd0e71 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp @@ -16,9 +16,36 @@ namespace tree { class HilbertRTreeDescentHeuristic { public: - template + /** + * Evaluate the node using a heuristic. Returns the number of the node + * with minimum largest Hilbert value is greater than the Hilbert value of + * the point being inserted. + * + * @param node The node that is being evaluated. + * @param point The point that is being inserted. + */ + template static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point); + /** + * Evaluate the node using a heuristic. Returns the number of the node + * with minimum largest Hilbert value is greater than the Hilbert value of + * the point being inserted. + * + * @param node The node that is being evaluated. + * @param point The number of the point that is being inserted. + */ + template + static size_t ChooseDescentNode(const TreeType* node, const size_t point); + + /** + * Evaluate the node using a heuristic. Returns the number of the node + * with minimum largest Hilbert value is greater than the largest + * Hilbert value of the point being inserted. + * + * @param node The node that is being evaluated. + * @param insertedNode The node that is being inserted. + */ template static size_t ChooseDescentNode(const TreeType* node, const TreeType* insertedNode); diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp index a61cf0e450..4a40357caa 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp @@ -14,25 +14,39 @@ namespace mlpack { namespace tree { template -size_t HilbertRTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, const arma::vec& point) +size_t HilbertRTreeDescentHeuristic:: +ChooseDescentNode(const TreeType* node, const size_t point) { size_t bestIndex = 0; - for(bestIndex = node->NumChildren() - 1; bestIndex > 0; bestIndex--) - if(node->Children()[bestIndex]->Split().LargestHilbertValue().CompareWith(node,point) < 0) + for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) + if(node->Children()[bestIndex]->AuxiliaryInfo().LargestHilbertValue().CompareWith(node,point) > 0) break; return bestIndex; } template -size_t HilbertRTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, - const TreeType* insertedNode) +size_t HilbertRTreeDescentHeuristic:: +ChooseDescentNode(const TreeType* node, const arma::vec& point) { size_t bestIndex = 0; - for(bestIndex = node->NumChildren() - 1; bestIndex > 0; bestIndex--) - if(node->Children()[bestIndex]->Split().LargestHilbertValue().CompareWith(node,node->Split().LargestHilbertValue()) < 0) + for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) + if(node->Children()[bestIndex]->AuxiliaryInfo().LargestHilbertValue().CompareWith(node,point) > 0) + break; + + return bestIndex; +} + +template +size_t HilbertRTreeDescentHeuristic:: +ChooseDescentNode(const TreeType* node, const TreeType* insertedNode) +{ + size_t bestIndex = 0; + + for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) + if(node->Children()[bestIndex]->AuxiliaryInfo().LargestHilbertValue().CompareWith(node,node->AuxiliaryInfo().LargestHilbertValue()) > 0) break; return bestIndex; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp index 038aa491a2..e046a74ff9 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp @@ -13,7 +13,11 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { -const int splitOrder = 2; +/** + * The order of the splitting policy. The Hilbert R tree splits a node + * on overflow, turnung splitOrder node to (splitOrder+1) nodes. + */ +constexpr int splitOrder = 2; class HilbertRTreeSplit { @@ -21,26 +25,55 @@ class HilbertRTreeSplit /** * Split a leaf node using the "default" algorithm. If necessary, this split * will propagate upwards through the tree. + * @param node. The node that is being split. + * @param relevels Not used. */ template - void SplitLeafNode(TreeType *tree,std::vector& relevels); + static 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. + * @param node. The node that is being split. + * @param relevels Not used. */ template - bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + static bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); private: + /** + * Try to find splitOrder cooperating siblings in order to redistribute + * their children evenly. Returns true on success. + * @param parent The parent of of the overflowing node. + * @param iTree The number of the overflowing node. + * @param firstSibling The first cooperating sibling. + * @param lastSibling The last cooperating sibling. + */ template - bool FindCooperatingSiblings(TreeType *parent,size_t iTree,size_t &firstSibling,size_t &lastSibling); + static bool FindCooperatingSiblings(TreeType *parent,size_t iTree, + size_t &firstSibling,size_t &lastSibling); + /** + * Redistribute the children of the cooperating siblings evenly + * among them. + * @param parent The parent of of the overflowing node. + * @param firstSibling The first cooperating sibling. + * @param lastSibling The last cooperating sibling. + */ template - void RedistributeNodesEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling); + static void RedistributeNodesEvenly(const TreeType *parent, + size_t firstSibling,size_t lastSibling); + /** + * Redistribute the points of the cooperating siblings evenly + * among them. + * @param parent The parent of of the overflowing node. + * @param firstSibling The first cooperating sibling. + * @param lastSibling The last cooperating sibling. + */ template - void RedistributePointsEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling); + static void RedistributePointsEvenly(const TreeType *parent, + size_t firstSibling,size_t lastSibling); }; } // namespace tree diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index 0f1a7bdbfa..0f0ea4ba13 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -15,7 +15,8 @@ namespace mlpack { namespace tree { template -void HilbertRTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) +void HilbertRTreeSplit:: +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 @@ -29,7 +30,7 @@ void HilbertRTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels tree->NullifyData(); // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; - copy->AuxiliarityInfo().SplitLeafNode(copy,relevels); + HilbertRTreeSplit::SplitLeafNode(copy,relevels); return; } @@ -38,6 +39,8 @@ void HilbertRTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels size_t iTree = 0; for(iTree = 0;parent->Children()[iTree] != tree; iTree++); + // Try to find splitOrder cooperating siblings in order to redistribute + // points among them and avoid split. size_t firstSibling,lastSibling; if(FindCooperatingSiblings(parent,iTree,firstSibling,lastSibling)) { @@ -45,28 +48,38 @@ void HilbertRTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels return; } + // We can not find splitOrder cooperating siblings since they are all full. + // We introduce new one instead. - size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); + size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? + iTree + splitOrder : parent->NumChildren()); for(size_t i = parent->NumChildren(); i > iNewSibling ; i--) - parent->Children()[i] = parent->Children[i-1]; + parent->Children()[i] = parent->Children()[i-1]; parent->NumChildren()++; parent->Children()[iNewSibling] = new TreeType(parent); - lastSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren() - 1); + lastSibling = (iTree + splitOrder < parent->NumChildren() ? + iTree + splitOrder : parent->NumChildren() - 1); firstSibling = (lastSibling > splitOrder ? lastSibling - splitOrder : 0); + assert(lastSibling - firstSibling == splitOrder); + assert(firstSibling >= 0); + assert(lastSibling < parent->NumChildren()); + + // Redistribute the points among (splitOrder+1) cooperating siblings evenly. RedistributePointsEvenly(parent,firstSibling,lastSibling); if(parent->NumChildren() == parent->MaxNumChildren() + 1) - parent->AuxiliarityInfo().SplitNonLeafNode(parent,relevels); + HilbertRTreeSplit::SplitNonLeafNode(parent,relevels); } template -bool HilbertRTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relevels) +bool HilbertRTreeSplit:: +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 @@ -90,70 +103,100 @@ bool HilbertRTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector& relev size_t iTree = 0; for(iTree = 0;parent->Children()[iTree] != tree; iTree++); + // Try to find splitOrder cooperating siblings in order to redistribute + // children among them and avoid split. size_t firstSibling,lastSibling; if(FindCooperatingSiblings(parent,iTree,firstSibling,lastSibling)) { RedistributeNodesEvenly(parent,firstSibling,lastSibling); - return; + return false; } - size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); + // We can not find splitOrder cooperating siblings since they are all full. + // We introduce new one instead. + + size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? + iTree + splitOrder : parent->NumChildren()); for(size_t i = parent->NumChildren(); i > iNewSibling ; i--) - parent->Children()[i] = parent->Children[i-1]; + parent->Children()[i] = parent->Children()[i-1]; parent->NumChildren()++; parent->Children()[iNewSibling] = new TreeType(parent); - lastSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren() - 1); - firstSibling = (lastSibling > splitOrder ? lastSibling - splitOrder : 0); + lastSibling = (iTree + splitOrder < parent->NumChildren() ? + iTree + splitOrder : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder ? + lastSibling - splitOrder : 0); + assert(lastSibling - firstSibling == splitOrder); + assert(firstSibling >= 0); + assert(lastSibling < parent->NumChildren()); + + // Redistribute children among (splitOrder+1) cooperating siblings evenly. RedistributeNodesEvenly(parent,firstSibling,lastSibling); if(parent->NumChildren() == parent->MaxNumChildren() + 1) - parent->AuxiliarityInfo().SplitNonLeafNode(parent,relevels); + HilbertRTreeSplit::SplitNonLeafNode(parent,relevels); + return false; } template -bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent,size_t iTree,size_t &firstSibling,size_t &lastSibling) +bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent, size_t iTree, + size_t &firstSibling, size_t &lastSibling) { size_t start = (iTree > splitOrder-1 ? iTree - splitOrder + 1 : 0); - size_t end = (iTree + splitOrder <= parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); + size_t end = (iTree + splitOrder <= parent->NumChildren() ? + iTree + splitOrder : parent->NumChildren()); size_t iUnderfullSibling; + + // Try to find empty space among cooperating siblings. if(parent->Children()[iTree]->NumChildren() != 0) { for(iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) - if(parent->Children()[iUnderfullSibling]->NumChildren() < parent->Children()[iUnderfullSibling]->MaxNumChildren() - 1) + if(parent->Children()[iUnderfullSibling]->NumChildren() < + parent->Children()[iUnderfullSibling]->MaxNumChildren() - 1) break; } else { for(iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) - if(parent->Children()[iUnderfullSibling]->NumPoints() < parent->Children()[iUnderfullSibling]->MaxLeafSize() - 1) + if(parent->Children()[iUnderfullSibling]->NumPoints() < + parent->Children()[iUnderfullSibling]->MaxLeafSize() - 1) break; } - if(iUnderfullSibling == end) + if(iUnderfullSibling == end) // All nodes are full. return false; if(iUnderfullSibling > iTree) { - lastSibling = (iTree + splitOrder-1 < parent->NumChildren() ? iTree + splitOrder-1 : parent->NumChildren() - 1); - firstSibling = (lastSibling > splitOrder-1 ? lastSibling - splitOrder + 1 : 0); + lastSibling = (iTree + splitOrder-1 < parent->NumChildren() ? + iTree + splitOrder-1 : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder-1 ? + lastSibling - splitOrder + 1 : 0); } else { - lastSibling = (iUnderfullSibling + splitOrder-1 < parent->NumChildren() ? iUnderfullSibling + splitOrder-1 : parent->NumChildren() - 1); - firstSibling = (lastSibling > splitOrder-1 ? lastSibling - splitOrder + 1 : 0); + lastSibling = (iUnderfullSibling + splitOrder-1 < parent->NumChildren() ? + iUnderfullSibling + splitOrder-1 : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder-1 ? + lastSibling - splitOrder + 1 : 0); } + assert(lastSibling - firstSibling <= splitOrder - 1); + assert(firstSibling >= 0); + assert(lastSibling < parent->NumChildren()); + return true; } template -void HilbertRTreeSplit::RedistributeNodesEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling) +void HilbertRTreeSplit:: +RedistributeNodesEvenly(const TreeType *parent, + size_t firstSibling, size_t lastSibling) { size_t numChildren = 0; size_t numChildrenPerNode,numRestChildren; @@ -166,6 +209,7 @@ void HilbertRTreeSplit::RedistributeNodesEvenly(const TreeType *parent,size_t fi std::vector children(numChildren); + // Copy children's children in order to redistribute them. size_t iChild = 0; for(size_t i = firstSibling; i <= lastSibling; i++) { @@ -179,14 +223,20 @@ void HilbertRTreeSplit::RedistributeNodesEvenly(const TreeType *parent,size_t fi iChild = 0; for(size_t i = firstSibling; i <= lastSibling; i++) { + // Since we redistribute children of a sibling we should + // recalculate the bound. + parent->Children()[i]->Bound().Clear(); + for(size_t j = 0; j < numChildrenPerNode; j++) { + parent->Children()[i]->Bound() |= children[iChild]->Bound(); parent->Children()[i]->Children()[j] = children[iChild]; children[iChild]->Parent() = parent->Children()[i]; iChild++; } if(numRestChildren > 0) { + parent->Children()[i]->Bound() |= children[iChild]->Bound(); parent->Children()[i]->Children()[numChildrenPerNode] = children[iChild]; children[iChild]->Parent() = parent->Children()[i]; parent->Children()[i]->NumChildren() = numChildrenPerNode + 1; @@ -197,13 +247,19 @@ void HilbertRTreeSplit::RedistributeNodesEvenly(const TreeType *parent,size_t fi { parent->Children()[i]->NumChildren() = numChildrenPerNode; } - parent->Children()[i]->AuxiliarityInfo().largestHilbertValue = - children[iChild-1]->AuxiliarityInfo().largestHilbertValue; + assert(parent->Children()[i]->NumChildren() <= + parent->Children()[i]->MaxNumChildren()); + + // Fix the largest Hilbert value of the sibling. + parent->Children()[i]->AuxiliaryInfo().LargestHilbertValue() = + children[iChild-1]->AuxiliaryInfo().LargestHilbertValue(); } } template -void HilbertRTreeSplit::RedistributePointsEvenly(const TreeType *parent,size_t firstSibling,size_t lastSibling) +void HilbertRTreeSplit:: +RedistributePointsEvenly(const TreeType *parent, + size_t firstSibling, size_t lastSibling) { size_t numPoints = 0; size_t numPointsPerNode,numRestPoints; @@ -216,6 +272,7 @@ void HilbertRTreeSplit::RedistributePointsEvenly(const TreeType *parent,size_t f std::vector points(numPoints); + // Copy children's points in order to redistribute them. size_t iPoint = 0; for(size_t i = firstSibling; i <= lastSibling; i++) { @@ -229,32 +286,39 @@ void HilbertRTreeSplit::RedistributePointsEvenly(const TreeType *parent,size_t f iPoint = 0; for(size_t i = firstSibling; i <= lastSibling; i++) { + // Since we redistribute points of a sibling we should + // recalculate the bound. parent->Children()[i]->Bound().Clear(); size_t j; for(j = 0; j < numPointsPerNode; j++) { - parent->Children()[i]->Bound() |= parent->Children()[i]->Dataset()->col(points[iPoint]); + parent->Children()[i]->Bound() |= + parent->Children()[i]->Dataset().col(points[iPoint]); parent->Children()[i]->Points()[j] = points[iPoint]; - parent->Children()[i]->LocalDataset()->col(j) = parent->Children()[i]->Dataset()->col(points[iPoint]); + parent->Children()[i]->LocalDataset().col(j) = + parent->Children()[i]->Dataset().col(points[iPoint]); iPoint++; } if(numRestPoints > 0) { - parent->Children()[i]->Bound() |= parent->Children()[i]->Dataset()->col(points[iPoint]); + parent->Children()[i]->Bound() |= + parent->Children()[i]->Dataset().col(points[iPoint]); parent->Children()[i]->Points()[j] = points[iPoint]; - parent->Children()[i]->LocalDataset()->col(j) = parent->Children()[i]->Dataset()->col(points[iPoint]); - parent->Children()[i]->NumPoints() = numPointsPerNode + 1; + parent->Children()[i]->LocalDataset().col(j) = + parent->Children()[i]->Dataset().col(points[iPoint]); + parent->Children()[i]->Count() = numPointsPerNode + 1; numRestPoints--; iPoint++; } else { - parent->Children()[i]->NumPoints() = numPointsPerNode; + parent->Children()[i]->Count() = numPointsPerNode; } -// TODO: -// Adjust the largestHilbertValue -// parent->Children()[i]->Split().largestHilbertValue.AdjustValue(); + assert(parent->Children()[i]->NumPoints() <= + parent->Children()[i]->MaxLeafSize()); + // Fix the largest Hilbert value of the sibling. + parent->Children()[i]->AuxiliaryInfo().LargestHilbertValue().UpdateLargestValue(parent->Children()[i]); } } diff --git a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp index 2dcde7304b..7e68f3f417 100644 --- a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp @@ -19,31 +19,54 @@ class NoAuxiliaryInformation NoAuxiliaryInformation(const TreeType *) { }; NoAuxiliaryInformation(const TreeType &) { }; + /** + * Some tree types require to save some properties at the insertion process. + * This method should return false if it does not handle the process. + */ bool HandlePointInsertion(TreeType *, const size_t) { return false; } + /** + * Some tree types require to save some properties at the insertion process. + * This method should return false if it does not handle the process. + */ bool HandleNodeInsertion(TreeType *,TreeType *,bool) { return false; } + /** + * Some tree types require to save some properties at the deletion process. + * This method should return false if it does not handle the process. + */ bool HandlePointDeletion(TreeType *,const size_t) { return false; } + /** + * Some tree types require to save some properties at the deletion process. + * This method should return false if it does not handle the process. + */ bool HandleNodeRemoval(TreeType *,const size_t) { return false; } - bool ShrinkAuxiliaryInfo(TreeType *) + /** + * Some tree types require to propagate the information downward. + * This method should return false if this is not the case. + */ + bool UpdateAuxiliaryInfo(TreeType *) { return false; } + /** + * Nothing to copy. + */ void Copy(TreeType *,TreeType *) { } 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 index e52169ba94..d4302d6d3b 100644 --- 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 @@ -34,6 +34,19 @@ class RStarTreeDescentHeuristic template static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point); + /** + * 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 number of the point that is being inserted. + */ + template + static size_t ChooseDescentNode(const TreeType* node, const size_t point); + template static size_t ChooseDescentNode(const TreeType* node, const TreeType* insertedNode); 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 index 49d716bd4a..adb2f38109 100644 --- 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 @@ -13,6 +13,15 @@ namespace mlpack { namespace tree { +template +inline size_t RStarTreeDescentHeuristic::ChooseDescentNode( + const TreeType* node, + const size_t point) +{ + return ChooseDescentNode(node,node->Dataset().col(point)); +} + + template inline size_t RStarTreeDescentHeuristic::ChooseDescentNode( const TreeType* node, diff --git a/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp index b7392d35a3..124e1677aa 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp @@ -34,6 +34,19 @@ class RTreeDescentHeuristic template static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point); + /** + * Evaluate the node using a heuristic. The heuristic guarantees two things: + * + * 1. If point is contained in (or on) the bound, the value returned is zero. + * 2. If the point is not contained in (or on) the bound, the value returned + * is greater than zero. + * + * @param node The node that is being evaluated. + * @param point The number of the point that is being inserted. + */ + template + static size_t ChooseDescentNode(const TreeType* node, const size_t point); + /** * Evaluate the node using a heuristic. The heuristic guarantees two things: * 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 75994b15bd..6fd95ba866 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,6 +13,13 @@ namespace mlpack { namespace tree { +template +inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, + const size_t point) +{ + return ChooseDescentNode(node,node->Dataset().col(point)); +} + template inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, const arma::vec& point) 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 24983cfa65..3b397a8a1f 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -309,8 +309,7 @@ void RectangleTreecol(point)); + const size_t descentNode = DescentType::ChooseDescentNode(this, point); children[descentNode]->InsertPoint(point, lvls); } @@ -348,8 +347,7 @@ void RectangleTreecol(point)); + const size_t descentNode = DescentType::ChooseDescentNode(this,point); children[descentNode]->InsertPoint(point, relevels); } @@ -814,11 +812,11 @@ void RectangleTreeParent() != NULL) { if (stillShrinking) - stillShrinking = root->AuxiliaryInfo().ShrinkAuxiliaryInfo(root); + stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root); root = root->Parent(); } if (stillShrinking) - stillShrinking = root->AuxiliaryInfo().ShrinkAuxiliaryInfo(root); + stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root); // Reinsert the points at the root node. for (size_t j = 0; j < count; j++) @@ -868,11 +866,11 @@ void RectangleTreeParent() != NULL) { if (stillShrinking) - stillShrinking = root->AuxiliaryInfo().ShrinkAuxiliaryInfo(root); + stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root); root = root->Parent(); } if (stillShrinking) - stillShrinking = root->AuxiliaryInfo().ShrinkAuxiliaryInfo(root); + stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root); // Reinsert the nodes at the root node. for (size_t i = 0; i < numChildren; i++) @@ -923,11 +921,11 @@ void RectangleTreeCondenseTree(point, relevels, usePoint); else if (!usePoint && - (ShrinkBoundForBound(bound) || auxiliaryInfo.ShrinkAuxiliaryInfo(this)) && + (ShrinkBoundForBound(bound) || auxiliaryInfo.UpdateAuxiliaryInfo(this)) && parent != NULL) parent->CondenseTree(point, relevels, usePoint); } diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp index 1366c579df..2f90ed5eb6 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp @@ -13,39 +13,57 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { +constexpr int recursionDepth = 500; class RecursiveHilbertValue { public: - + //! Default constructor RecursiveHilbertValue() : largestValue(-1) { }; + /** + * Construct this for the node tree. If the node is the root this method + * computes the Hilbert value for each point in the tree's dataset. + * @param node The node that stores this Hilbert value. + */ template RecursiveHilbertValue(const TreeType *) : largestValue(-1) { }; + /** + * Create a Hilbert value object by copying from the other node. + * @param other The node from which the value will be copied. + */ template RecursiveHilbertValue(const TreeType &other) : largestValue(other.AuxiliaryInfo().LargestHilbertValue().LargestValue()) { }; + //! This struct is designed in order to facilitate the recursion. template struct tagCompareStruct { + //! Lower bound arma::Col Lo; + //! High bound arma::Col Hi; + //! Permutation of axes std::vector permutation; + //! Indicates that the axis should be inverted std::vector inversion; + //! Indicates that the result should be inverted bool invertResult; + int recursionLevel; tagCompareStruct(size_t dim) : Lo(dim), Hi(dim), permutation(dim), inversion(dim), - invertResult(false) + invertResult(false), + recursionLevel(0) { for(size_t i = 0; i < dim; i++) { @@ -59,45 +77,129 @@ class RecursiveHilbertValue template using CompareStruct = struct tagCompareStruct; - - + /** + * Compare two points. It returns 1 if the first point is greater than + * the second one, -1 if the first point is less than the second one and + * 0 if the Hilbert values of the points are equal. + * @param pt1 The first point. + * @param pt2 The second point. + */ template static int ComparePoints(const arma::Col &pt1, const arma::Col &pt2); + /** + * Compare two Hilbert values. It returns 1 if the first value is greater than + * the second one, -1 if the first value is less than the second one and + * 0 if the values are equal. + * @param val1 The first Hilbert value. + * @param val2 The second Hilbert value. + */ template static int CompareValues(TreeType *tree, RecursiveHilbertValue &val1, RecursiveHilbertValue &val2); + /** + * Compare the largest Hilbert value of the node with the val value. + * It returns 1 if the value of the node is greater than val, + * -1 if the value of the node is less than val and + * 0 if the values are equal. + * @param tree The pointer to the tree. + * @param val The Hilbert value to compare with. + */ template int CompareWith(TreeType *tree, RecursiveHilbertValue &val); + /** + * Compare the largest Hilbert value of the node with the Hilbert value + * of the point. It returns 1 if the value of the node is greater than + * the value of the point, -1 if the value of the node is less than + * the value of the point and 0 if the values are equal. + * @param tree The pointer to the tree. + * @param pt The point to compare with. + */ template int CompareWith(TreeType *tree, const arma::Col &pt); + /** + * Compare the largest Hilbert value of the node with the Hilbert value + * of the point. It returns 1 if the value of the node is greater than + * the value of the point, -1 if the value of the node is less than + * the value of the point and 0 if the values are equal. + * @param tree The pointer to the tree. + * @param point The number of the point to compare with. + */ + template + int CompareWith(TreeType *tree, const size_t point); + + /** + * Update the largest Hilbert value of the node. + * @param node The node in which the point is being inserted. + * @param point The number of the point being inserted. + */ template size_t InsertPoint(TreeType *node, const size_t point); + /** + * Update the largest Hilbert value of the node. + * @param node The node being inserted. + */ template void InsertNode(TreeType *node); + /** + * Update the largest Hilbert value of the node. + * @param node The node from which another node is being deleted. + * @param nodeIndex The number of the node being deleted. + */ template void DeletePoint(TreeType *node, const size_t localIndex); + /** + * Update the largest Hilbert value of the node. + * @param node The node from which another node is being deleted. + * @param nodeIndex The number of the node being deleted. + */ template void RemoveNode(TreeType *node, const size_t nodeIndex); + /** + * Copy the largest Hilbert value. + * @param dst The node to which the information is being copied. + * @param src The node from which the information is being copied. + */ RecursiveHilbertValue operator = (const RecursiveHilbertValue &val); + /** + * Copy the largest Hilbert value. + * @param dst The node to which the information is being copied. + * @param src The node from which the information is being copied. + */ template void Copy(TreeType *dst, TreeType *src); + /** + * Update the largest Hilbert value. + * @param node The node in which the information should be updated. + */ + template + void UpdateLargestValue(TreeType *node); + + //! Return the largest Hilbert value size_t LargestValue() const { return largestValue; } private: - + //! The largest Hilbert value i.e. the number of the point in the dataset. ptrdiff_t largestValue; + /** + * Compare two points. It returns 1 if the first point is greater than + * the second one, -1 if the first point is less than the second one and + * 0 if the Hilbert values of the points are equal. + * @param pt1 The first point. + * @param pt2 The second point. + * @param comp An object of CompareStruct. + */ template static int ComparePoints(const arma::Col &pt1, const arma::Col &pt2, diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp index 22b8b684bb..b78cd769df 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp @@ -26,11 +26,12 @@ template int RecursiveHilbertValue::CompareValues(TreeType *tree, RecursiveHilbertValue &val1, RecursiveHilbertValue &val2) { + typedef typename TreeType::ElemType ElemType; size_t point1 = val1.LargestValue(); size_t point2 = val2.LargestValue(); - return ComparePoints(tree->Dataset()->col(point1), - tree->Dataset()->col(point2)); + return ComparePoints(arma::Col(tree->Dataset().col(point1)), + arma::Col(tree->Dataset().col(point2))); } template @@ -44,7 +45,16 @@ template int RecursiveHilbertValue::CompareWith(TreeType *tree, const arma::Col &pt) { - return ComparePoints(tree->Dataset()->col(largestValue),pt); + return ComparePoints(arma::Col(tree->Dataset()->col(largestValue)),pt); +} + +template +int RecursiveHilbertValue::CompareWith(TreeType *tree, + const size_t point) +{ + typedef typename TreeType::ElemType ElemType; + return ComparePoints(arma::Col(tree->Dataset().col(largestValue)), + arma::Col(tree->Dataset().col(point))); } @@ -60,6 +70,7 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, center += vec; + // Get bits in order to use the Gray code for(size_t i = 0; i < pt1.n_rows; i++) { size_t j = comp.permutation[i]; @@ -69,6 +80,8 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, bits2[i] = (pt2(j) > center(j) && !comp.inversion[j]) || (pt2(j) <= center(j) && !comp.inversion[j]); } + + // Gray encode for(size_t i = 1; i < pt1.n_rows; i++) { bits[i] ^= bits[i-1]; @@ -92,9 +105,16 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, return 1; } + if(comp.recursionLevel >= recursionDepth) + return 0; + + comp.recursionLevel++; + if(bits[pt1.n_rows-1]) comp.invertResult = !comp.invertResult; + // Since the Hilbert curve is continuous we should permutate and intend + // coordinate axes depending on the position of the point for(size_t i = 0; i < pt1.n_rows; i++) { size_t j = comp.permutation[i]; @@ -111,6 +131,7 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, } } + // Choose an appropriate subhypercube for(size_t i = 0; i < pt1.n_rows; i++) { if(pt1(i) > center(i)) @@ -125,13 +146,14 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, template size_t RecursiveHilbertValue::InsertPoint(TreeType *node, const size_t point) { + typedef typename TreeType::ElemType ElemType; if(node->IsLeaf()) { size_t i; for(i = 0; i < node->NumPoints(); i++) - if(ComparePoints(node->LocalDataset()->col(i), - node->Dataset()->col(point)) > 0) + if(ComparePoints(arma::Col(node->LocalDataset().col(i)), + arma::Col(node->Dataset().col(point)))> 0) break; if(i == node->NumPoints()) largestValue = point; @@ -145,8 +167,8 @@ size_t RecursiveHilbertValue::InsertPoint(TreeType *node, const size_t point) largestValue = point; return 0; } - if(ComparePoints(node->Dataset()->col(point), - node->Dataset()->col(largestValue)) > 0) + if(ComparePoints(arma::Col(node->Dataset().col(point)), + arma::Col(node->Dataset().col(largestValue))) > 0) largestValue = point; } return 0; @@ -155,10 +177,11 @@ size_t RecursiveHilbertValue::InsertPoint(TreeType *node, const size_t point) template void RecursiveHilbertValue::InsertNode(TreeType *node) { + typedef typename TreeType::ElemType ElemType; size_t point = node->AuxiliaryInfo().LargestHilbertValue().LargestValue(); - if(ComparePoints(node->Dataset()->col(point), - node->Dataset()->col(largestValue)) > 0) + if(ComparePoints(arma::Col(node->Dataset()->col(point)), + arma::Col(node->Dataset()->col(largestValue))) > 0) largestValue = point; } @@ -202,6 +225,21 @@ void RecursiveHilbertValue::Copy(TreeType *dst, TreeType *src) src->AuxiliaryInfo().LargestHilbertValue().LargestValue(); } +template +void RecursiveHilbertValue::UpdateLargestValue(TreeType *node) +{ + if(node->IsLeaf()) + { + largestValue = (node->NumPoints() > 0 ? + node->Points()[node->NumPoints() - 1] : -1); + } + else + { + largestValue = (node->NumChildren() > 0 ? + node->Children()[node->NumChildren() - 1]->AuxiliaryInfo().LargestHilbertValue().LargestValue() : -1); + } +} + } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/typedef.hpp b/src/mlpack/core/tree/rectangle_tree/typedef.hpp index f44eb18fca..cb87d07aaa 100644 --- a/src/mlpack/core/tree/rectangle_tree/typedef.hpp +++ b/src/mlpack/core/tree/rectangle_tree/typedef.hpp @@ -106,7 +106,7 @@ using RecursiveHilbertRTreeAuxiliaryInformation = HilbertRTreeAuxiliaryInformation; template -using HilbertRTree = RectangleTree class XTreeAuxiliaryInformation { public: + //! Default constructor XTreeAuxiliaryInformation() : normalNodeMaxNumChildren(0), splitHistory(0) { }; + /** + * Construct this whith the specified node. + * @param node The node that stores this auxiliary information. + */ XTreeAuxiliaryInformation(const TreeType *node) : normalNodeMaxNumChildren(node->Parent() ? node->Parent()->AuxiliaryInfo().NormalNodeMaxNumChildren() : @@ -27,38 +32,72 @@ class XTreeAuxiliaryInformation splitHistory(node->Bound().Dim()) { }; + /** + * Create an auxiliary information object by copying from the other node. + * @param other The node from which the information will be copied. + */ XTreeAuxiliaryInformation(const TreeType &other) : normalNodeMaxNumChildren(other.AuxiliaryInfo().NormalNodeMaxNumChildren()), splitHistory(other.AuxiliaryInfo().SplitHistory()) { }; + /** + * Some tree types require to save some properties at the insertion process. + * This method should return false if it does not handle the process. + */ bool HandlePointInsertion(TreeType *, const size_t) { return false; } + /** + * Some tree types require to save some properties at the insertion process. + * This method should return false if it does not handle the process. + */ bool HandleNodeInsertion(TreeType *,TreeType *,bool) { return false; } + /** + * Some tree types require to save some properties at the deletion process. + * This method should return false if it does not handle the process. + */ bool HandlePointDeletion(TreeType *,const size_t) { return false; } + /** + * Some tree types require to save some properties at the deletion process. + * This method should return false if it does not handle the process. + */ bool HandleNodeRemoval(TreeType *,const size_t) { return false; } - bool ShrinkAuxiliaryInfo(TreeType *) + /** + * Some tree types require to propagate the information downward. + * This method should return false if this is not the case. + */ + bool UpdateAuxiliaryInfo(TreeType *) { return false; } - void Copy(TreeType *,TreeType *) - { } + /** + * Copy the auxiliary information from one node to another. + * @param dst The node to which the information being copied. + * @param src The node from which the information being copied. + */ + void Copy(TreeType *dst,TreeType *src) + { + dst->AuxiliaryInfo().NormalNodeMaxNumChildren() = + src->AuxiliaryInfo().NormalNodeMaxNumChildren(); + + dst->AuxiliaryInfo().SplitHistory() = src->AuxiliaryInfo().SplitHistory(); + } /** * The X tree requires that the tree records it's "split history". To make diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index 9e5b2036bc..8a86b05255 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -614,6 +614,151 @@ BOOST_AUTO_TEST_CASE(XTreeTraverserTest) } } +BOOST_AUTO_TEST_CASE(DiscreteHilbertRTreeTraverserTest) +{ + arma::mat dataset; + + const int numP = 1000; + + dataset.randu(8, numP); // 1000 points in 8 dimensions. + arma::Mat neighbors1; + arma::mat distances1; + arma::Mat neighbors2; + arma::mat distances2; + + typedef DiscreteHilbertRTree,arma::mat> TreeType; + TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); + + // Nearest neighbor search with the Hilbert R tree. + + NeighborSearch, arma::mat, + DiscreteHilbertRTree > knn1(&hilbertRTree, true); + + BOOST_REQUIRE_EQUAL(hilbertRTree.NumDescendants(), numP); + + CheckSync(hilbertRTree); + CheckContainment(hilbertRTree); + CheckExactContainment(hilbertRTree); + CheckHierarchy(hilbertRTree); + + knn1.Search(5, neighbors1, distances1); + + // Nearest neighbor search the naive way. + KNN knn2(dataset, true, true); + + knn2.Search(5, neighbors2, distances2); + + for (size_t i = 0; i < neighbors1.size(); i++) + { + BOOST_REQUIRE_EQUAL(neighbors1[i], neighbors2[i]); + BOOST_REQUIRE_EQUAL(distances1[i], distances2[i]); + } +} + +BOOST_AUTO_TEST_CASE(RecursiveHilbertRTreeTraverserTest) +{ + arma::mat dataset; + + const int numP = 1000; + + dataset.randu(8, numP); // 1000 points in 8 dimensions. + arma::Mat neighbors1; + arma::mat distances1; + arma::Mat neighbors2; + arma::mat distances2; + + typedef RecursiveHilbertRTree,arma::mat> TreeType; + TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); + + // Nearest neighbor search with the Hilbert R tree. + + NeighborSearch, arma::mat, + RecursiveHilbertRTree > knn1(&hilbertRTree, true); + + BOOST_REQUIRE_EQUAL(hilbertRTree.NumDescendants(), numP); + + CheckSync(hilbertRTree); + CheckContainment(hilbertRTree); + CheckExactContainment(hilbertRTree); + CheckHierarchy(hilbertRTree); + + knn1.Search(5, neighbors1, distances1); + + // Nearest neighbor search the naive way. + KNN knn2(dataset, true, true); + + knn2.Search(5, neighbors2, distances2); + + for (size_t i = 0; i < neighbors1.size(); i++) + { + BOOST_REQUIRE_EQUAL(neighbors1[i], neighbors2[i]); + BOOST_REQUIRE_EQUAL(distances1[i], distances2[i]); + } +} + +template +void CheckHilbertOrdering(TreeType *tree) +{ + if(tree->IsLeaf()) + { + for(size_t i = 0; i < tree->NumPoints() - 1; i++) + BOOST_REQUIRE_LE( + tree->AuxiliaryInfo().LargestHilbertValue().ComparePoints( + arma::vec(tree->LocalDataset().col(i-1)), + arma::vec(tree->LocalDataset().col(i))), + 0); + + BOOST_REQUIRE_EQUAL( + tree->AuxiliaryInfo().LargestHilbertValue().CompareWith( + tree, + tree->Points()[tree->NumPoints() - 1]), + 0); + } + else + { + for(size_t i = 0; i < tree->NumChildren() - 1; i++) + BOOST_REQUIRE_LE( + tree->AuxiliaryInfo().LargestHilbertValue().CompareValues(tree, + tree->Children()[i-1]->AuxiliaryInfo().LargestHilbertValue(), + tree->Children()[i]->AuxiliaryInfo().LargestHilbertValue()), + 0); + + BOOST_REQUIRE_EQUAL( + tree->AuxiliaryInfo().LargestHilbertValue().CompareWith( + tree, + tree->Children()[tree->NumChildren() - 1]->AuxiliaryInfo().LargestHilbertValue()), + 0); + + for(size_t i = 0; i < tree->NumChildren(); i++) + CheckHilbertOrdering(tree->Children()[i]); + } +} + +BOOST_AUTO_TEST_CASE(DiscreteHilbertOrderingTest) +{ + arma::mat dataset; + dataset.randu(8, 1000); // 1000 points in 8 dimensions. + + typedef DiscreteHilbertRTree,arma::mat> TreeType; + TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); + + CheckHilbertOrdering(&hilbertRTree); +} + +BOOST_AUTO_TEST_CASE(RecursiveHilbertOrderingTest) +{ + arma::mat dataset; + dataset.randu(8, 1000); // 1000 points in 8 dimensions. + + typedef RecursiveHilbertRTree,arma::mat> TreeType; + TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); + + CheckHilbertOrdering(&hilbertRTree); +} // Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low // to allow us to test by hand without adding hundreds of points. From 72f53d600fb7b511af6d4e0acb2fecfa4bc17593 Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Wed, 1 Jun 2016 16:34:47 +0300 Subject: [PATCH 05/38] Hilbert R tree fixes. --- .../rectangle_tree/discrete_hilbert_value.hpp | 12 +- .../discrete_hilbert_value_impl.hpp | 104 ++++++++++++------ .../hilbert_r_tree_auxiliary_information.hpp | 9 +- ...bert_r_tree_auxiliary_information_impl.hpp | 28 +++-- .../rectangle_tree/hilbert_r_tree_split.hpp | 2 +- .../hilbert_r_tree_split_impl.hpp | 17 ++- .../tree/rectangle_tree/rectangle_tree.hpp | 10 +- .../rectangle_tree/rectangle_tree_impl.hpp | 35 +++--- .../recursive_hilbert_value.hpp | 14 ++- .../recursive_hilbert_value_impl.hpp | 42 ++++--- .../core/tree/rectangle_tree/typedef.hpp | 2 +- src/mlpack/tests/rectangle_tree_test.cpp | 8 +- 12 files changed, 179 insertions(+), 104 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp index 6bcee8f850..3350100288 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -92,7 +92,7 @@ class DiscreteHilbertValue * @param tree Not used * @param val The number of the point to compare with. */ - template + template int CompareWith(TreeType *tree, const size_t point); /** @@ -146,12 +146,16 @@ class DiscreteHilbertValue void UpdateLargestValue(TreeType *node); //! Copy the largest Hilbert value. - DiscreteHilbertValue operator = (DiscreteHilbertValue &val); + DiscreteHilbertValue operator = (const DiscreteHilbertValue &val); //! Return the largest Hilbert value std::list>::iterator LargestValue() const { return largestValue; } + //! Modify the largest Hilbert value + std::list>::iterator &LargestValue() + { return largestValue; } + //! Modify the local dataset std::list> *LocalDataset() { return localDataset; } //! Modify the dataset @@ -182,6 +186,10 @@ class DiscreteHilbertValue */ static int CompareValues(const arma::Col &value1, const arma::Col &value2); + /** + * Returns true if the node has the largest Hilbert value. + */ + bool HasValue(); }; } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index ce4df87805..787ae8c2a1 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -33,31 +33,45 @@ template DiscreteHilbertValue::DiscreteHilbertValue(const TreeType *tree) : dataset(tree->Parent() ? tree->Parent()->AuxiliaryInfo().LargestHilbertValue().Dataset() : - new arma::Mat(tree->Dataset()->n_rows, - tree->MaxLeafSize()+1)), + new arma::Mat(tree->Dataset().n_rows, + tree->Dataset().n_cols)), ownsDataset(!tree->Parent()), localDataset(new std::list>()), largestValue(localDataset->end()) { + typedef typename TreeType::ElemType ElemType; // Calculate the Hilbert value for all points if(!tree->Parent()) { - for(size_t i = 0; i < tree->Dataset()->n_rows; i++) - dataset->col(i) = CalculateValue(tree->Dataset()->col(i)); + for(size_t i = 0; i < tree->Dataset().n_cols; i++) + dataset->col(i) = CalculateValue((arma::Col)tree->Dataset().col(i)); } -}; +} template DiscreteHilbertValue::DiscreteHilbertValue(const TreeType &other) : dataset(other.AuxiliaryInfo().LargestHilbertValue().Dataset()), - ownsDataset(!other.Parent()), - localDataset(other.AuxiliaryInfo().LargestHilbertValue().LocalDataset()), + ownsDataset(false), + localDataset(new std::list>()), largestValue(other.AuxiliaryInfo().LargestHilbertValue().LargestValue()) { -}; + if(other.IsLeaf()) + { + std::list> *otherDataset = + other.AuxiliaryInfo().LargestHilbertValue().LocalDataset(); + for(std::list>::iterator it = otherDataset->begin(); it != otherDataset->end(); it++) + { + localDataset->push_back(*it); + } + largestValue = localDataset->end(); + if(otherDataset->size() > 0) + largestValue--; + } +} template -arma::Col CalculateValue(const arma::Col &pt) +arma::Col DiscreteHilbertValue:: +CalculateValue(const arma::Col &pt) { arma::Col res(pt.n_rows); constexpr int order = 64; // The number of bits that we can store @@ -86,19 +100,19 @@ arma::Col CalculateValue(const arma::Col &pt) normalizedVal /= tmp; } // Extract the mantissa - uint64_t tmp = 1 << numMantBits; - res(i) = std::floor(normalizedVal / numMantBits); + uint64_t tmp = (uint64_t)1 << numMantBits; + res(i) = std::floor(normalizedVal / tmp); // Add the exponent - res(i) |= (e - std::numeric_limits::min_exponent) << numMantBits; + res(i) |= ((uint64_t)(e - std::numeric_limits::min_exponent)) << numMantBits; // Negative values should be inverted if(sgn) - res(i) = 1 << (order - 1) - 1 - res(i); + res(i) = ((uint64_t)1 << (order - 1)) - 1 - res(i); else - res(i) |= 1 << (order - 1); + res(i) |= (uint64_t)1 << (order - 1); } - uint64_t M = 1 << (order - 1); + uint64_t M = (uint64_t)1 << (order - 1); // Since the Hilbert curve is continuous we should permutate and intend // coordinate axes depending on the position of the point @@ -176,14 +190,21 @@ int DiscreteHilbertValue::ComparePoints(const arma::Col &pt1, } template -int DiscreteHilbertValue::CompareValues(TreeType *tree, +int DiscreteHilbertValue::CompareValues(TreeType *, DiscreteHilbertValue &val1, DiscreteHilbertValue &val2) { + if(val1.HasValue() && !val2.HasValue()) + return 1; + else if(!val1.HasValue() && val2.HasValue()) + return -1; + else if(!val1.HasValue() && !val2.HasValue()) + return 0; + return CompareValues(*val1.LargestValue(),*val2.LargestValue()); } template -int DiscreteHilbertValue::CompareWith(TreeType *tree, DiscreteHilbertValue &val) +int DiscreteHilbertValue::CompareWith(TreeType *, DiscreteHilbertValue &val) { return CompareValues(*largestValue,*val.LargestValue()); } @@ -194,13 +215,18 @@ int DiscreteHilbertValue::CompareWith(TreeType *tree, { arma::Col val = CalculateValue(pt); + if(!HasValue()) + return -1; + return CompareValues(*largestValue,val); } -template -int DiscreteHilbertValue::CompareWith(TreeType *tree, +template +int DiscreteHilbertValue::CompareWith(TreeType *, const size_t point) { + if(!HasValue()) + return -1; return CompareValues(*largestValue,dataset->col(point)); } @@ -208,7 +234,7 @@ template size_t DiscreteHilbertValue::InsertPoint(TreeType *node, const size_t point) { size_t i = 0; - std::list>::iterator it; + std::list>::iterator it = localDataset->end(); if(node->IsLeaf()) { @@ -241,7 +267,7 @@ size_t DiscreteHilbertValue::InsertPoint(TreeType *node, const size_t point) { // We do not update the largest Hilbert value since we do not know the // iterator - if(*largestValue < dataset->col(point)) + if(CompareValues(*largestValue,dataset->col(point)) < 0) largestValue = localDataset->end(); } @@ -278,7 +304,7 @@ void DiscreteHilbertValue::DeletePoint(TreeType *node, const size_t localIndex) { largestValue = localDataset->end(); largestValue--; - } + } } template @@ -308,16 +334,22 @@ void DiscreteHilbertValue::Copy(TreeType *dst, TreeType *src) DiscreteHilbertValue &srcVal = src->AuxiliaryInfo().LargestHilbertValue(); // Copy the largest Hilbert value and the local dataset - dst.LargestValue() = src.LargestValue(); + dstVal.LargestValue() = srcVal.LargestValue(); - dst.LocalDataset()->clear(); - std::list>::iterator it = src.LocalDataset()->begin(); - for( ; it != src.LocalDataset()->end(); it++) - dst.LocalDataset()->push_back(*it); - + dstVal.LocalDataset()->clear(); + std::list>::iterator it = srcVal.LocalDataset()->begin(); + for( ; it != srcVal.LocalDataset()->end(); it++) + dstVal.LocalDataset()->push_back(*it); + + if(dst->IsLeaf()) + { + dstVal.LargestValue() = dstVal.LocalDataset()->end(); + if(dst->NumPoints() > 0) + dstVal.LargestValue()--; + } } -inline DiscreteHilbertValue DiscreteHilbertValue::operator = (DiscreteHilbertValue &val) +inline DiscreteHilbertValue DiscreteHilbertValue::operator = (const DiscreteHilbertValue &val) { // Copy the largest Hilbert value largestValue = val.LargestValue(); @@ -340,21 +372,27 @@ void DiscreteHilbertValue::UpdateLargestValue(TreeType *node) for(size_t i = 0; i < node->NumPoints(); i++) localDataset->push_back(dataset->col(node->Points()[i])); largestValue = localDataset->end(); - localDataset--; + largestValue--; } else { + if(localDataset->size() > 0) + localDataset->clear(); // Update the largest Hilbert value; if(node->NumChildren() == 0) largestValue = localDataset->end(); - else if(node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue().LargestValue() != - node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue().LocalDataset()->end()) - largestValue = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue(); + else if(node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue().HasValue()) + largestValue = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue().LargestValue(); else largestValue = localDataset->end(); } } +inline bool DiscreteHilbertValue::HasValue() +{ + return largestValue != localDataset->end(); +} + } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp index 4d3070c1bb..01bbf99832 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp @@ -31,6 +31,9 @@ class HilbertRTreeAuxiliaryInformation */ HilbertRTreeAuxiliaryInformation(const TreeType &other); + //! Free memory + ~HilbertRTreeAuxiliaryInformation(); + /** * The Hilbert R tree requires to insert points according to their * Hilbert value. This method should take care of it. @@ -92,13 +95,13 @@ class HilbertRTreeAuxiliaryInformation private: //! The largest Hilbert value of a point enclosed by the node. - HilbertValue largestHilbertValue; + HilbertValue *largestHilbertValue; public: //! Return the largest Hilbert value of a point covered by the node. - HilbertValue LargestHilbertValue() const { return largestHilbertValue; } + HilbertValue& LargestHilbertValue() const { return *largestHilbertValue; } //! Modify the largest Hilbert value of a point covered by the node. - HilbertValue& LargestHilbertValue() { return largestHilbertValue; } + HilbertValue& LargestHilbertValue() { return *largestHilbertValue; } /** * Serialize the information. diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index bd190d587d..0341d30372 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -25,7 +25,7 @@ HilbertRTreeAuxiliaryInformation() template HilbertRTreeAuxiliaryInformation:: HilbertRTreeAuxiliaryInformation(const TreeType *node) : - largestHilbertValue(node) + largestHilbertValue(new HilbertValue(node)) { }; @@ -33,10 +33,17 @@ HilbertRTreeAuxiliaryInformation(const TreeType *node) : template HilbertRTreeAuxiliaryInformation:: HilbertRTreeAuxiliaryInformation(const TreeType &other) : - largestHilbertValue(other) + largestHilbertValue(new HilbertValue(other)) { }; + +template +HilbertRTreeAuxiliaryInformation:: +~HilbertRTreeAuxiliaryInformation() +{ + delete largestHilbertValue; +} template bool HilbertRTreeAuxiliaryInformation:: @@ -46,7 +53,7 @@ HandlePointInsertion(TreeType *node,const size_t point) { // Get the position at which the point should be inserted // Update the largest Hilbert value of the node - size_t pos = largestHilbertValue.InsertPoint(node,point); + size_t pos = largestHilbertValue->InsertPoint(node,point); // Move points for(size_t i = node->NumPoints(); i > pos; i--) @@ -60,7 +67,7 @@ HandlePointInsertion(TreeType *node,const size_t point) node->Count()++; } else - largestHilbertValue.InsertPoint(node,point); // Update LHV + largestHilbertValue->InsertPoint(node,point); // Update LHV return true; } @@ -90,10 +97,10 @@ HandleNodeInsertion(TreeType *node,TreeType *nodeToInsert,bool insertionLevel) nodeToInsert->Parent() = node; // Update the largest Hilbert value - largestHilbertValue.InsertNode(nodeToInsert); + largestHilbertValue->InsertNode(nodeToInsert); } else - largestHilbertValue.InsertNode(nodeToInsert); // Update LHV + largestHilbertValue->InsertNode(nodeToInsert); // Update LHV return true; } @@ -103,7 +110,7 @@ bool HilbertRTreeAuxiliaryInformation:: HandlePointDeletion(TreeType *node,const size_t localIndex) { // Update the largest Hilbert value - largestHilbertValue.DeletePoint(node,localIndex); + largestHilbertValue->DeletePoint(node,localIndex); for(size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) { @@ -119,7 +126,7 @@ bool HilbertRTreeAuxiliaryInformation:: HandleNodeRemoval(TreeType *node,const size_t nodeIndex) { // Update the largest Hilbert value - largestHilbertValue.RemoveNode(node,nodeIndex); + largestHilbertValue->RemoveNode(node,nodeIndex); for(size_t i = nodeIndex + 1; nodeIndex < node->NumChildren(); i++) node->Children()[i-1] = node->Children()[i]; @@ -139,7 +146,8 @@ UpdateAuxiliaryInfo(TreeType *node) if(HilbertValue::CompareValues(largestHilbertValue, child->AuxiliaryInfo().LargestHilbertValue()) < 0) { - largestHilbertValue = child->AuxiliaryInfo().LargestHilbertValue(); + largestHilbertValue->Copy(node,child); +// largestHilbertValue = child->AuxiliaryInfo().LargestHilbertValue(); return true; } return false; @@ -149,7 +157,7 @@ template void HilbertRTreeAuxiliaryInformation:: Copy(TreeType *dst,TreeType *src) { - largestHilbertValue.Copy(dst,src); + largestHilbertValue->Copy(dst,src); } template diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp index e046a74ff9..f830c134ba 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp @@ -72,7 +72,7 @@ class HilbertRTreeSplit * @param lastSibling The last cooperating sibling. */ template - static void RedistributePointsEvenly(const TreeType *parent, + static void RedistributePointsEvenly(TreeType *parent, size_t firstSibling,size_t lastSibling); }; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index 0f0ea4ba13..2315e88c65 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -65,7 +65,7 @@ SplitLeafNode(TreeType *tree,std::vector& relevels) iTree + splitOrder : parent->NumChildren() - 1); firstSibling = (lastSibling > splitOrder ? lastSibling - splitOrder : 0); - assert(lastSibling - firstSibling == splitOrder); + assert(lastSibling - firstSibling <= splitOrder); assert(firstSibling >= 0); assert(lastSibling < parent->NumChildren()); @@ -130,7 +130,7 @@ SplitNonLeafNode(TreeType *tree,std::vector& relevels) firstSibling = (lastSibling > splitOrder ? lastSibling - splitOrder : 0); - assert(lastSibling - firstSibling == splitOrder); + assert(lastSibling - firstSibling <= splitOrder); assert(firstSibling >= 0); assert(lastSibling < parent->NumChildren()); @@ -251,14 +251,13 @@ RedistributeNodesEvenly(const TreeType *parent, parent->Children()[i]->MaxNumChildren()); // Fix the largest Hilbert value of the sibling. - parent->Children()[i]->AuxiliaryInfo().LargestHilbertValue() = - children[iChild-1]->AuxiliaryInfo().LargestHilbertValue(); + parent->Children()[i]->AuxiliaryInfo().LargestHilbertValue().UpdateLargestValue(parent->Children()[i]); } } template void HilbertRTreeSplit:: -RedistributePointsEvenly(const TreeType *parent, +RedistributePointsEvenly(TreeType *parent, size_t firstSibling, size_t lastSibling) { size_t numPoints = 0; @@ -320,6 +319,14 @@ RedistributePointsEvenly(const TreeType *parent, // Fix the largest Hilbert value of the sibling. parent->Children()[i]->AuxiliaryInfo().LargestHilbertValue().UpdateLargestValue(parent->Children()[i]); } + + TreeType *root = parent; + + while(root != NULL) + { + root->AuxiliaryInfo().LargestHilbertValue().UpdateLargestValue(root); + root = root->Parent(); + } } } // namespace tree diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index b2fa544f17..84b728e06b 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -94,7 +94,7 @@ class RectangleTree //! The local dataset MatType* localDataset; //! A tree-specific information - AuxiliaryInformationType auxiliaryInfo; + AuxiliaryInformationType *auxiliaryInfo; public: //! A single traverser for rectangle type trees. See @@ -294,11 +294,11 @@ class RectangleTree StatisticType& Stat() { return stat; } //! Return the auxiliary information object of this node. - const AuxiliaryInformationType& AuxiliaryInfo() const - { return auxiliaryInfo; } + const AuxiliaryInformationType &AuxiliaryInfo() const + { return *auxiliaryInfo; } //! Modify the split object of this node. - AuxiliaryInformationType& AuxiliaryInfo() - { return auxiliaryInfo; } + AuxiliaryInformationType& AuxiliaryInfo() + { return *auxiliaryInfo; } //! 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 3b397a8a1f..c9be471bed 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -50,7 +50,7 @@ RectangleTree(const MatType& data, { stat = StatisticType(*this); - auxiliaryInfo = AuxiliaryInformationType(this); + auxiliaryInfo = new AuxiliaryInformationType(this); // For now, just insert the points in order. RectangleTree* root = this; @@ -92,7 +92,7 @@ RectangleTree(MatType&& data, { stat = StatisticType(*this); - auxiliaryInfo = AuxiliaryInformationType(this); + auxiliaryInfo = new AuxiliaryInformationType(this); // For now, just insert the points in order. RectangleTree* root = this; @@ -132,7 +132,7 @@ RectangleTree( maxLeafSize + 1))) { stat = StatisticType(*this); - auxiliaryInfo = AuxiliaryInformationType(this); + auxiliaryInfo = new AuxiliaryInformationType(this); } /** @@ -166,7 +166,7 @@ RectangleTree( points(other.Points()), localDataset(NULL) { - auxiliaryInfo = AuxiliaryInformationType(other); + auxiliaryInfo = new AuxiliaryInformationType(other); if (deepCopy) { if (numChildren > 0) @@ -225,6 +225,7 @@ RectangleTree:: ~RectangleTree() { + delete auxiliaryInfo; for (size_t i = 0; i < numChildren; i++) delete children[i]; @@ -297,7 +298,7 @@ void RectangleTreeHandlePointInsertion(this,point)) { localDataset->col(count) = dataset->col(point); points[count++] = point; @@ -308,7 +309,7 @@ void RectangleTreeHandlePointInsertion(this,point); const size_t descentNode = DescentType::ChooseDescentNode(this, point); children[descentNode]->InsertPoint(point, lvls); } @@ -335,7 +336,7 @@ void RectangleTreeHandlePointInsertion(this,point)) { localDataset->col(count) = dataset->col(point); points[count++] = point; @@ -346,7 +347,7 @@ void RectangleTreeHandlePointInsertion(this,point); const size_t descentNode = DescentType::ChooseDescentNode(this,point); children[descentNode]->InsertPoint(point, relevels); } @@ -375,7 +376,7 @@ void RectangleTreeBound(); if (level == TreeDepth()) { - if(!auxiliaryInfo.HandleNodeInsertion(this,node,true)) + if(!auxiliaryInfo->HandleNodeInsertion(this,node,true)) { children[numChildren++] = node; node->Parent() = this; @@ -384,7 +385,7 @@ void RectangleTreeHandleNodeInsertion(this,node,false); const size_t descentNode = DescentType::ChooseDescentNode(this, node); children[descentNode]->InsertNode(node, level, relevels); } @@ -420,7 +421,7 @@ bool RectangleTreeHandlePointDeletion(this,i)) { localDataset->col(i) = localDataset->col(--count); // Decrement count. points[i] = points[count]; @@ -460,7 +461,7 @@ bool RectangleTreeHandlePointDeletion(this,i)) { localDataset->col(i) = localDataset->col(--count); points[i] = points[count]; @@ -498,7 +499,7 @@ bool RectangleTreeHandleNodeRemoval(this,i)) { children[i] = children[--numChildren]; // Decrement numChildren. } @@ -843,7 +844,7 @@ void RectangleTreeChildren()[j] == this) { // Decrement numChildren. - if(!auxiliaryInfo.HandleNodeRemoval(parent,j)) + if(!auxiliaryInfo->HandleNodeRemoval(parent,j)) { parent->Children()[j] = parent->Children()[--parent->NumChildren()]; } @@ -911,7 +912,7 @@ void RectangleTreecol(i) = child->LocalDataset().col(i); } - auxiliaryInfo.Copy(this,child); + auxiliaryInfo->Copy(this,child); count = child->Count(); child->SoftDelete(); @@ -921,11 +922,11 @@ void RectangleTreeUpdateAuxiliaryInfo(this)) && parent != NULL) parent->CondenseTree(point, relevels, usePoint); else if (!usePoint && - (ShrinkBoundForBound(bound) || auxiliaryInfo.UpdateAuxiliaryInfo(this)) && + (ShrinkBoundForBound(bound) || auxiliaryInfo->UpdateAuxiliaryInfo(this)) && parent != NULL) parent->CondenseTree(point, relevels, usePoint); } diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp index 2f90ed5eb6..d020461ae1 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp @@ -54,14 +54,23 @@ class RecursiveHilbertValue //! Indicates that the axis should be inverted std::vector inversion; //! Indicates that the result should be inverted + arma::Col center; + arma::Col vec; + std::vector bits; + std::vector bits2; bool invertResult; int recursionLevel; + tagCompareStruct(size_t dim) : Lo(dim), Hi(dim), permutation(dim), inversion(dim), + center(dim), + vec(dim), + bits(dim), + bits2(dim), invertResult(false), recursionLevel(0) { @@ -186,7 +195,10 @@ class RecursiveHilbertValue void UpdateLargestValue(TreeType *node); //! Return the largest Hilbert value - size_t LargestValue() const { return largestValue; } + ptrdiff_t LargestValue() const { return largestValue; } + + //! Modify the largest Hilbert value + ptrdiff_t& LargestValue() { return largestValue; } private: //! The largest Hilbert value i.e. the number of the point in the dataset. diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp index b78cd769df..e95daaa1df 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp @@ -63,45 +63,43 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, const arma::Col &pt2, CompareStruct &comp) { - arma::Col center = comp.Hi * 0.5; - arma::Col vec = comp.Lo * 0.5; - std::vector bits(pt1.n_rows,0); - std::vector bits2(pt1.n_rows,0); + comp.center = comp.Hi * 0.5; + comp.vec = comp.Lo * 0.5; - center += vec; + comp.center += comp.vec; // Get bits in order to use the Gray code for(size_t i = 0; i < pt1.n_rows; i++) { size_t j = comp.permutation[i]; - bits[i] = (pt1(j) > center(j) && !comp.inversion[j]) || - (pt1(j) <= center(j) && !comp.inversion[j]); + comp.bits[i] = (pt1(j) > comp.center(j) && !comp.inversion[j]) || + (pt1(j) <= comp.center(j) && !comp.inversion[j]); - bits2[i] = (pt2(j) > center(j) && !comp.inversion[j]) || - (pt2(j) <= center(j) && !comp.inversion[j]); + comp.bits2[i] = (pt2(j) > comp.center(j) && !comp.inversion[j]) || + (pt2(j) <= comp.center(j) && !comp.inversion[j]); } // Gray encode for(size_t i = 1; i < pt1.n_rows; i++) { - bits[i] ^= bits[i-1]; - bits2[i] ^= bits2[i-1]; + comp.bits[i] ^= comp.bits[i-1]; + comp.bits2[i] ^= comp.bits2[i-1]; } if(comp.invertResult) { for(size_t i = 0; i < pt1.n_rows; i++) { - bits[i] = !bits[i]; - bits2[i] = !bits2[i]; + comp.bits[i] = !comp.bits[i]; + comp.bits2[i] = !comp.bits2[i]; } } for(size_t i = 0; i < pt1.n_rows; i++) { - if(bits[i] < bits2[i]) + if(comp.bits[i] < comp.bits2[i]) return -1; - if(bits[i] > bits2[i]) + if(comp.bits[i] > comp.bits2[i]) return 1; } @@ -110,7 +108,7 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, comp.recursionLevel++; - if(bits[pt1.n_rows-1]) + if(comp.bits[pt1.n_rows-1]) comp.invertResult = !comp.invertResult; // Since the Hilbert curve is continuous we should permutate and intend @@ -119,8 +117,8 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, { size_t j = comp.permutation[i]; size_t j0 = comp.permutation[0]; - if((pt1(j) > center(j) && !comp.inversion[j]) || - (pt1(j) <= center(j) && !comp.inversion[j])) + if((pt1(j) > comp.center(j) && !comp.inversion[j]) || + (pt1(j) <= comp.center(j) && !comp.inversion[j])) comp.inversion[j0] = !comp.inversion[j0]; else { @@ -128,16 +126,16 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, tmp = comp.permutation[0]; comp.permutation[0] = comp.permutation[i]; comp.permutation[i] = tmp; - } + } } // Choose an appropriate subhypercube for(size_t i = 0; i < pt1.n_rows; i++) { - if(pt1(i) > center(i)) - comp.Lo(i) = center(i); + if(pt1(i) > comp.center(i)) + comp.Lo(i) = comp.center(i); else - comp.Hi(i) = center(i); + comp.Hi(i) = comp.center(i); } return ComparePoints(pt1,pt2,comp); diff --git a/src/mlpack/core/tree/rectangle_tree/typedef.hpp b/src/mlpack/core/tree/rectangle_tree/typedef.hpp index cb87d07aaa..6622099a8d 100644 --- a/src/mlpack/core/tree/rectangle_tree/typedef.hpp +++ b/src/mlpack/core/tree/rectangle_tree/typedef.hpp @@ -115,7 +115,7 @@ using RecursiveHilbertRTree = RectangleTree using DiscreteHilbertRTreeAuxiliaryInformation = - HilbertRTreeAuxiliaryInformation; + HilbertRTreeAuxiliaryInformation; template using DiscreteHilbertRTree = RectangleTreeNumPoints() - 1; i++) BOOST_REQUIRE_LE( tree->AuxiliaryInfo().LargestHilbertValue().ComparePoints( - arma::vec(tree->LocalDataset().col(i-1)), - arma::vec(tree->LocalDataset().col(i))), + arma::vec(tree->LocalDataset().col(i)), + arma::vec(tree->LocalDataset().col(i+1))), 0); BOOST_REQUIRE_EQUAL( @@ -721,8 +721,8 @@ void CheckHilbertOrdering(TreeType *tree) for(size_t i = 0; i < tree->NumChildren() - 1; i++) BOOST_REQUIRE_LE( tree->AuxiliaryInfo().LargestHilbertValue().CompareValues(tree, - tree->Children()[i-1]->AuxiliaryInfo().LargestHilbertValue(), - tree->Children()[i]->AuxiliaryInfo().LargestHilbertValue()), + tree->Children()[i]->AuxiliaryInfo().LargestHilbertValue(), + tree->Children()[i+1]->AuxiliaryInfo().LargestHilbertValue()), 0); BOOST_REQUIRE_EQUAL( From be1e9d2fb198560c39d06c842ba1bfe294686204 Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Sun, 5 Jun 2016 02:25:54 +0300 Subject: [PATCH 06/38] Added full localDataset support (not tested). Redesigned HilbertValueType. --- .../rectangle_tree/discrete_hilbert_value.hpp | 122 +++-- .../discrete_hilbert_value_impl.hpp | 445 ++++++++++-------- .../hilbert_r_tree_auxiliary_information.hpp | 37 +- ...bert_r_tree_auxiliary_information_impl.hpp | 151 ++++-- .../hilbert_r_tree_descent_heuristic_impl.hpp | 6 +- .../hilbert_r_tree_split_impl.hpp | 48 +- .../no_auxiliary_information.hpp | 23 +- .../rectangle_tree/r_star_tree_split_impl.hpp | 56 ++- .../tree/rectangle_tree/rectangle_tree.hpp | 10 +- .../rectangle_tree/rectangle_tree_impl.hpp | 224 ++++++++- .../recursive_hilbert_value.hpp | 124 +++-- .../recursive_hilbert_value_impl.hpp | 247 ++++++---- .../x_tree_auxiliary_information.hpp | 23 +- .../tree/rectangle_tree/x_tree_split_impl.hpp | 56 ++- src/mlpack/tests/rectangle_tree_test.cpp | 22 +- 15 files changed, 1027 insertions(+), 567 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp index 3350100288..07c16ec7ce 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -13,9 +13,13 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { +template class DiscreteHilbertValue { public: + typedef typename std::conditional::type HilbertElemType; //! Default constructor DiscreteHilbertValue(); @@ -25,14 +29,13 @@ class DiscreteHilbertValue * @param node The node that stores this Hilbert value. */ template - DiscreteHilbertValue(const TreeType *tree); + DiscreteHilbertValue(const TreeType* tree); /** - * Create a Hilbert value object by copying from the other node. - * @param other The node from which the value will be copied. + * Create a Hilbert value object by copying from another one. + * @param other The Hilbert value object from which the value will be copied. */ - template - DiscreteHilbertValue(const TreeType &other); + DiscreteHilbertValue(const DiscreteHilbertValue& other); //! Free memory ~DiscreteHilbertValue(); @@ -45,9 +48,10 @@ class DiscreteHilbertValue * @param pt1 The first point. * @param pt2 The second point. */ - template - static int ComparePoints(const arma::Col &pt1, - const arma::Col &pt2); + template + static int ComparePoints(const VecType1& pt1, const VecType2& pt2, + typename boost::enable_if>* = 0, + typename boost::enable_if>* = 0); /** * Compare two Hilbert values. It returns 1 if the first value is greater than @@ -56,20 +60,17 @@ class DiscreteHilbertValue * @param val1 The first point. * @param val2 The second point. */ - template - static int CompareValues(TreeType *tree, DiscreteHilbertValue &val1, - DiscreteHilbertValue &val2); + static int CompareValues(const DiscreteHilbertValue& val1, + const DiscreteHilbertValue& val2); /** * Compare the largest Hilbert value of the node with the val value. * It returns 1 if the value of the node is greater than val, * -1 if the value of the node is less than val and * 0 if the values are equal. This method does not compute the Hilbert values. - * @param tree Not used * @param val The Hilbert value to compare with. */ - template - int CompareWith(TreeType *tree, DiscreteHilbertValue &val); + int CompareWith(const DiscreteHilbertValue& val) const; /** * Compare the largest Hilbert value of the node with the Hilbert value @@ -80,8 +81,9 @@ class DiscreteHilbertValue * @param tree Not used * @param val The point to compare with. */ - template - int CompareWith(TreeType *tree, const arma::Col &pt); + template + int CompareWith(const VecType& pt, + typename boost::enable_if>* = 0) const; /** * Compare the largest Hilbert value of the node with the Hilbert value @@ -92,8 +94,10 @@ class DiscreteHilbertValue * @param tree Not used * @param val The number of the point to compare with. */ - template - int CompareWith(TreeType *tree, const size_t point); + + template + int CompareWithCachedPoint(const VecType& pt, + typename boost::enable_if>* = 0) const; /** * Update the largest Hilbert value of the node and insert the point @@ -101,15 +105,15 @@ class DiscreteHilbertValue * @param node The node in which the point is being inserted. * @param point The number of the point being inserted. */ - template - size_t InsertPoint(TreeType *node, const size_t point); - + template + size_t InsertPoint(TreeType *node, const VecType& pt, + typename boost::enable_if>* = 0); /** * Update the largest Hilbert value of the node. * @param node The node being inserted. */ template - void InsertNode(TreeType *node); + void InsertNode(TreeType* node); /** * Update the largest Hilbert value of the node and delete the point @@ -118,7 +122,7 @@ class DiscreteHilbertValue * @param localIndex The number of the point in the local dataset. */ template - void DeletePoint(TreeType *node, const size_t localIndex); + void DeletePoint(TreeType* node, const size_t localIndex); /** * Update the largest Hilbert value of the node. @@ -126,7 +130,7 @@ class DiscreteHilbertValue * @param nodeIndex The number of the node being deleted. */ template - void RemoveNode(TreeType *node, const size_t nodeIndex); + void RemoveNode(TreeType* node, const size_t nodeIndex); /** * Copy the largest Hilbert value and the local dataset @@ -134,8 +138,10 @@ class DiscreteHilbertValue * @param src The node from which the information is being copied. */ template - void Copy(TreeType *dst, TreeType *src); - + void Copy(TreeType* dst, TreeType* src); + + void NullifyData(); + /** * Update the largest Hilbert value and the local dataset. * The children of the node (or the points that the node contains) should be @@ -143,39 +149,55 @@ class DiscreteHilbertValue * @param node The node in which the information should be updated. */ template - void UpdateLargestValue(TreeType *node); + void UpdateLargestValue(TreeType* node); - //! Copy the largest Hilbert value. - DiscreteHilbertValue operator = (const DiscreteHilbertValue &val); + template + void UpdateHilbertValues(TreeType* parent, size_t firstSibling, + size_t lastSibling); - //! Return the largest Hilbert value - std::list>::iterator LargestValue() const - { return largestValue; } + //! Return the number of values + size_t NumValues() const + { return numValues; } - //! Modify the largest Hilbert value - std::list>::iterator &LargestValue() - { return largestValue; } + //! Modify the number of values + size_t& NumValues() + { return numValues; } + + //! Return the local dataset + const arma::Mat* LocalDataset() const + { return localDataset; } - //! Modify the local dataset - std::list> *LocalDataset() { return localDataset; } //! Modify the dataset - arma::Mat *Dataset() { return dataset; } + arma::Mat*& LocalDataset() { return localDataset; } + + //! Modify the valueToInsert + arma::Col* ValueToInsert() { return valueToInsert; } + + //! Modify the valueToInsert + const arma::Col* ValueToInsert() const + { return valueToInsert; } + private: - //! The dataset - arma::Mat *dataset; - //! Indicates that the node owns the dataset - bool ownsDataset; + //! The number of bits that we can store + static constexpr size_t order = sizeof(HilbertElemType) * CHAR_BIT; //! The local dataset - std::list> *localDataset; - //! The largest Hilbert value - std::list>::iterator largestValue; + arma::Mat* localDataset; + //! Indicates that the node owns the local dataset + bool ownsLocalDataset; + //! The number of values in the local dataset + size_t numValues; + //! The Hilbert value of the point that is being inserted + arma::Col* valueToInsert; + //! Indicates that the node owns the valueToInsert + bool ownsValueToInsert; /** * Calculate the Hilbert value of the point pt. * @param pt The point for which the Hilbert value should be calculated. */ - template - static arma::Col CalculateValue(const arma::Col &pt); + template + static arma::Col CalculateValue(const VecType& pt, + typename boost::enable_if>* = 0); /** * Compare two Hilbert values. It returns 1 if the first value is greater than @@ -184,12 +206,12 @@ class DiscreteHilbertValue * @param value1 The first value. * @param value2 The second value. */ - static int CompareValues(const arma::Col &value1, - const arma::Col &value2); + static int CompareValues(const arma::Col& value1, + const arma::Col& value2); /** * Returns true if the node has the largest Hilbert value. */ - bool HasValue(); + bool HasValue() const; }; } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index 787ae8c2a1..4d19d1305a 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -13,112 +13,117 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { -inline DiscreteHilbertValue::DiscreteHilbertValue() : - dataset(new arma::Mat()), - ownsDataset(true), - localDataset(new std::list>()), - largestValue(localDataset->end()) +template +DiscreteHilbertValue::DiscreteHilbertValue() : + localDataset(NULL), + ownsLocalDataset(false), + numValues(0), + valueToInsert(NULL), + ownsValueToInsert(false) { -}; +} -inline DiscreteHilbertValue::~DiscreteHilbertValue() +template +DiscreteHilbertValue::~DiscreteHilbertValue() { - delete localDataset; - if(ownsDataset) - delete dataset; -}; + if(ownsLocalDataset) + delete localDataset; + if(ownsValueToInsert) + delete valueToInsert; +} +template template -DiscreteHilbertValue::DiscreteHilbertValue(const TreeType *tree) : - dataset(tree->Parent() ? - tree->Parent()->AuxiliaryInfo().LargestHilbertValue().Dataset() : - new arma::Mat(tree->Dataset().n_rows, - tree->Dataset().n_cols)), - ownsDataset(!tree->Parent()), - localDataset(new std::list>()), - largestValue(localDataset->end()) +DiscreteHilbertValue::DiscreteHilbertValue(const TreeType* tree) : + localDataset(NULL), + ownsLocalDataset(false), + numValues(0), + valueToInsert(tree->Parent() ? + tree->Parent()->AuxiliaryInfo().HilbertValue().ValueToInsert() : + new arma::Col(tree->LocalDataset().n_rows)), + ownsValueToInsert(tree->Parent() ? false : true) { - typedef typename TreeType::ElemType ElemType; // Calculate the Hilbert value for all points - if(!tree->Parent()) + if(!tree->Parent()) // This is the root node + ownsLocalDataset = true; + else if(tree->Parent()->Children()[0]->IsLeaf()) { - for(size_t i = 0; i < tree->Dataset().n_cols; i++) - dataset->col(i) = CalculateValue((arma::Col)tree->Dataset().col(i)); + // This is a leaf node + assert(tree->Parent()->NumChildren() > 0); + ownsLocalDataset = true; } + + if(ownsLocalDataset) + { + localDataset = new arma::Mat(tree->LocalDataset().n_rows, + tree->MaxLeafSize() + 1); + } + } -template -DiscreteHilbertValue::DiscreteHilbertValue(const TreeType &other) : - dataset(other.AuxiliaryInfo().LargestHilbertValue().Dataset()), - ownsDataset(false), - localDataset(new std::list>()), - largestValue(other.AuxiliaryInfo().LargestHilbertValue().LargestValue()) +template +DiscreteHilbertValue:: +DiscreteHilbertValue(const DiscreteHilbertValue& other) : + localDataset(const_cast*>(other.LocalDataset())), + ownsLocalDataset(other.ownsLocalDataset), + numValues(other.NumValues()), + valueToInsert(const_cast*>(other.ValueToInsert())), + ownsValueToInsert(false) { - if(other.IsLeaf()) - { - std::list> *otherDataset = - other.AuxiliaryInfo().LargestHilbertValue().LocalDataset(); - for(std::list>::iterator it = otherDataset->begin(); it != otherDataset->end(); it++) - { - localDataset->push_back(*it); - } - largestValue = localDataset->end(); - if(otherDataset->size() > 0) - largestValue--; - } + } -template -arma::Col DiscreteHilbertValue:: -CalculateValue(const arma::Col &pt) +template +template +arma::Col::HilbertElemType> +DiscreteHilbertValue:: +CalculateValue(const VecType& pt,typename boost::enable_if>*) { - arma::Col res(pt.n_rows); - constexpr int order = 64; // The number of bits that we can store - constexpr double numPowers = - std::log2(std::numeric_limits::max_exponent - - std::numeric_limits::min_exponent + 1.0); - + typedef typename VecType::elem_type VecElemType; + arma::Col res(pt.n_rows); // The number of bits for the exponent - constexpr int numExpBits = std::ceil(numPowers); + const int numExpBits = + std::ceil(std::log2(std::numeric_limits::max_exponent - + std::numeric_limits::min_exponent + 1.0)); // The number of bits for the mantissa - constexpr int numMantBits = order - numExpBits - 1; + const int numMantBits = order - numExpBits - 1; for(size_t i = 0; i < pt.n_rows; i++) { int e; - ElemType normalizedVal = std::frexp(pt(i),&e); + VecElemType normalizedVal = std::frexp(pt(i),&e); bool sgn = std::signbit(normalizedVal); if(sgn) normalizedVal = -normalizedVal; - if(e < std::numeric_limits::min_exponent) + if(e < std::numeric_limits::min_exponent) { - uint64_t tmp = 1 << (std::numeric_limits::min_exponent - e); - e = std::numeric_limits::min_exponent; + HilbertElemType tmp = 1 << (std::numeric_limits::min_exponent - e); + e = std::numeric_limits::min_exponent; normalizedVal /= tmp; } // Extract the mantissa - uint64_t tmp = (uint64_t)1 << numMantBits; + HilbertElemType tmp = (HilbertElemType)1 << numMantBits; res(i) = std::floor(normalizedVal / tmp); // Add the exponent - res(i) |= ((uint64_t)(e - std::numeric_limits::min_exponent)) << numMantBits; + res(i) |= ((HilbertElemType)(e - std::numeric_limits::min_exponent)) << numMantBits; // Negative values should be inverted if(sgn) - res(i) = ((uint64_t)1 << (order - 1)) - 1 - res(i); + res(i) = ((HilbertElemType)1 << (order - 1)) - 1 - res(i); else - res(i) |= (uint64_t)1 << (order - 1); + res(i) |= (HilbertElemType)1 << (order - 1); } - uint64_t M = (uint64_t)1 << (order - 1); + HilbertElemType M = (HilbertElemType)1 << (order - 1); // Since the Hilbert curve is continuous we should permutate and intend // coordinate axes depending on the position of the point - for(uint64_t Q = M; Q > 1; Q >>= 1) + for(HilbertElemType Q = M; Q > 1; Q >>= 1) { - uint64_t P = Q - 1; + HilbertElemType P = Q - 1; for(size_t i = 0; i < pt.n_rows; i++) { @@ -126,7 +131,7 @@ CalculateValue(const arma::Col &pt) res(0) ^= P; else // Permutate { - uint64_t t = (res(0) ^ res(i)) & P; + HilbertElemType t = (res(0) ^ res(i)) & P; res(0) ^= t; res(i) ^= t; } @@ -137,10 +142,10 @@ CalculateValue(const arma::Col &pt) for(size_t i = 1; i < pt.n_rows; i++) res(i) ^= res(i-1); - uint64_t t = 0; + HilbertElemType t = 0; // Some coordinate axes should be inverted - for(uint64_t Q = M; Q > 1; Q >>= 1) + for(HilbertElemType Q = M; Q > 1; Q >>= 1) if( res(pt.n_rows - 1) & Q) t ^= Q - 1; @@ -148,7 +153,7 @@ CalculateValue(const arma::Col &pt) res(i) ^= t; // We should rearrange bits in order to compare two Hilbert values faster - arma::Col rearrangedResult(pt.n_rows,arma::fill::zeros); + arma::Col rearrangedResult(pt.n_rows,arma::fill::zeros); for(size_t i = 0; i < order; i++) for(size_t j = 0; j < pt.n_rows; j++) @@ -162,9 +167,10 @@ CalculateValue(const arma::Col &pt) return rearrangedResult; } -inline int DiscreteHilbertValue:: -CompareValues(const arma::Col &value1, - const arma::Col &value2) +template +int DiscreteHilbertValue:: +CompareValues(const arma::Col& value1, + const arma::Col& value2) { for(size_t i = 0;i < value1.n_rows; i++) { @@ -179,19 +185,23 @@ CompareValues(const arma::Col &value1, -template -int DiscreteHilbertValue::ComparePoints(const arma::Col &pt1, - const arma::Col &pt2) +template +template +int DiscreteHilbertValue:: +ComparePoints(const VecType1& pt1, const VecType2& pt2, + typename boost::enable_if>*, + typename boost::enable_if>*) { - arma::Col val1 = CalculateValue(pt1); - arma::Col val2 = CalculateValue(pt2); + arma::Col val1 = CalculateValue(pt1); + arma::Col val2 = CalculateValue(pt2); return CompareValues(val1,val2); } -template -int DiscreteHilbertValue::CompareValues(TreeType *, - DiscreteHilbertValue &val1, DiscreteHilbertValue &val2) +template +int DiscreteHilbertValue:: +CompareValues(const DiscreteHilbertValue& val1, + const DiscreteHilbertValue& val2) { if(val1.HasValue() && !val2.HasValue()) return 1; @@ -200,197 +210,214 @@ int DiscreteHilbertValue::CompareValues(TreeType *, else if(!val1.HasValue() && !val2.HasValue()) return 0; - return CompareValues(*val1.LargestValue(),*val2.LargestValue()); + return CompareValues(val1.LocalDataset()->col(val1.NumValues() - 1), + val2.LocalDataset()->col(val2.NumValues() - 1)); } -template -int DiscreteHilbertValue::CompareWith(TreeType *, DiscreteHilbertValue &val) +template +int DiscreteHilbertValue:: +CompareWith(const DiscreteHilbertValue& val) const { - return CompareValues(*largestValue,*val.LargestValue()); + return CompareValues(*this, val); } -template -int DiscreteHilbertValue::CompareWith(TreeType *tree, - const arma::Col &pt) +template +template +int DiscreteHilbertValue:: +CompareWith(const VecType& pt, + typename boost::enable_if>*) const { - arma::Col val = CalculateValue(pt); + arma::Col val = CalculateValue(pt); if(!HasValue()) return -1; - return CompareValues(*largestValue,val); + return CompareValues(localDataset->col(numValues - 1),val); } -template -int DiscreteHilbertValue::CompareWith(TreeType *, - const size_t point) +template +template +int DiscreteHilbertValue:: +CompareWithCachedPoint(const VecType& , + typename boost::enable_if>*) const { if(!HasValue()) return -1; - return CompareValues(*largestValue,dataset->col(point)); + + return CompareValues(localDataset->col(numValues - 1),*valueToInsert); } -template -size_t DiscreteHilbertValue::InsertPoint(TreeType *node, const size_t point) +template +template +size_t DiscreteHilbertValue:: +InsertPoint(TreeType *node, const VecType& pt, + typename boost::enable_if>*) { size_t i = 0; - std::list>::iterator it = localDataset->end(); + // All point are inserted to the root node + if(!node->Parent()) + *valueToInsert = CalculateValue(pt); if(node->IsLeaf()) { // Find an appropriate place - for(it = localDataset->begin(); it != localDataset->end(); it++) - { - if(CompareValues(*it, dataset->col(point)) > 0) + for(i = 0; i < numValues; i++) + if(CompareValues(localDataset->col(i), *valueToInsert) > 0) break; - i++; - } - std::list>::iterator insertedIterator = - localDataset->insert(it,dataset->col(point)); - // Update the largest Hilbert value - if(it == localDataset->end()) - largestValue = insertedIterator; + + for(size_t j = numValues; j > i; j--) + localDataset->col(j) = localDataset->col(j-1); + + localDataset->col(i) = *valueToInsert; + numValues++; // Propogate changes of the largest Hilbert value downward TreeType *root = node->Parent(); while(root != NULL) { - if(root->AuxiliaryInfo().LargestHilbertValue().LargestValue() == - root->AuxiliaryInfo().LargestHilbertValue().LocalDataset()->end()) - root->AuxiliaryInfo().LargestHilbertValue().LargestValue() = insertedIterator; + root->AuxiliaryInfo().HilbertValue().LocalDataset() = localDataset; + root->AuxiliaryInfo().HilbertValue().NumValues() = numValues; root = root->Parent(); } } - else if(largestValue != localDataset->end()) - { - // We do not update the largest Hilbert value since we do not know the - // iterator - if(CompareValues(*largestValue,dataset->col(point)) < 0) - largestValue = localDataset->end(); - } return i; } +template template -void DiscreteHilbertValue::InsertNode(TreeType *node) +void DiscreteHilbertValue::InsertNode(TreeType* node) { - std::list>::iterator it = - node->AuxiliaryInfo().LargestHilbertValue().LargestValue(); - - // Update the largest Hilbert value - if(largestValue != localDataset->end() && - it != node->AuxiliaryInfo().LargestHilbertValue().LocalDataset()->end()) - if(*it > *largestValue) - largestValue = it; -} - -template -void DiscreteHilbertValue::DeletePoint(TreeType *node, const size_t localIndex) -{ - std::list>::iterator it = localDataset->begin(); - - // Delete the Hilbert value from the local dataset - for(size_t i=0; i < localIndex; i++) - it++; - localDataset->erase(it); - - // Update the largest Hilbert value - if(localDataset->size() == 0) - largestValue = localDataset->end(); - else + DiscreteHilbertValue &val = node->AuxiliaryInfo().HilbertValue(); + + if(CompareWith(node,val) < 0) { - largestValue = localDataset->end(); - largestValue--; + localDataset = val.LocalDataset(); + numValues = val.NumValues(); } } +template template -void DiscreteHilbertValue::RemoveNode(TreeType *node, const size_t nodeIndex) +void DiscreteHilbertValue:: +DeletePoint(TreeType* node, const size_t localIndex) +{ + + // Delete the Hilbert value from the local dataset + for(size_t i = numValues - 1; i > localIndex; i--) + localDataset->col(i-1) = localDataset->col(i); + + numValues--; +} + +template +template +void DiscreteHilbertValue:: +RemoveNode(TreeType* node, const size_t nodeIndex) { if(node->NumChildren() <= 1) { - largestValue = localDataset->end(); + localDataset = NULL; + numValues = 0; return; } if(nodeIndex + 1 == node->NumChildren()) { // Update the largest Hilbert value if the value exists - TreeType *child = node->Children()[nodeIndex-1]; - if(child->AuxiliaryInfo.LargestHilbertValue().LargestValue() != - child->AuxiliaryInfo.LargestHilbertValue().LocalDataset()->end()) - largestValue = child->AuxiliaryInfo.LargestHilbertValue().LargestValue(); - else - largestValue = localDataset->end(); - } -} - -template -void DiscreteHilbertValue::Copy(TreeType *dst, TreeType *src) -{ - DiscreteHilbertValue &dstVal = dst->AuxiliaryInfo().LargestHilbertValue(); - DiscreteHilbertValue &srcVal = src->AuxiliaryInfo().LargestHilbertValue(); - - // Copy the largest Hilbert value and the local dataset - dstVal.LargestValue() = srcVal.LargestValue(); - - dstVal.LocalDataset()->clear(); - std::list>::iterator it = srcVal.LocalDataset()->begin(); - for( ; it != srcVal.LocalDataset()->end(); it++) - dstVal.LocalDataset()->push_back(*it); - - if(dst->IsLeaf()) - { - dstVal.LargestValue() = dstVal.LocalDataset()->end(); - if(dst->NumPoints() > 0) - dstVal.LargestValue()--; - } -} - -inline DiscreteHilbertValue DiscreteHilbertValue::operator = (const DiscreteHilbertValue &val) -{ - // Copy the largest Hilbert value - largestValue = val.LargestValue(); - - return *this; -} - -template -void DiscreteHilbertValue::UpdateLargestValue(TreeType *node) -{ - if(node->IsLeaf()) - { - // Update the largest Hilbert value and the local dataset - localDataset->clear(); - if(node->NumPoints() == 0) + TreeType* child = node->Children()[nodeIndex-1]; + if(child->AuxiliaryInfo.HilbertValue().NumValues() != 0) { - largestValue = localDataset->end(); - return; + numValues = child->AuxiliaryInfo.HilbertValue().NumValues(); + localDataset = child->AuxiliaryInfo.HilbertValue().LocalDataset(); } - for(size_t i = 0; i < node->NumPoints(); i++) - localDataset->push_back(dataset->col(node->Points()[i])); - largestValue = localDataset->end(); - largestValue--; - } - else - { - if(localDataset->size() > 0) - localDataset->clear(); - // Update the largest Hilbert value; - if(node->NumChildren() == 0) - largestValue = localDataset->end(); - else if(node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue().HasValue()) - largestValue = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().LargestHilbertValue().LargestValue(); else - largestValue = localDataset->end(); + { + localDataset = NULL; + numValues = 0; + } } } -inline bool DiscreteHilbertValue::HasValue() +template +template +void DiscreteHilbertValue::Copy(TreeType* dst, TreeType* src) { - return largestValue != localDataset->end(); +} + +template +void DiscreteHilbertValue::NullifyData() +{ + ownsLocalDataset = false; +} + +template +template +void DiscreteHilbertValue::UpdateLargestValue(TreeType* node) +{ + if(!node->IsLeaf()) + { + // Update the largest Hilbert value + localDataset = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().HilbertValue().LocalDataset(); + numValues = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().HilbertValue().NumValues(); + } +} + +template +template +void DiscreteHilbertValue:: +UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) +{ + // We should update the local dataset if points were redistributed + + size_t numPoints = 0; + + for(size_t i = firstSibling; i<= lastSibling; i++) + numPoints += parent->Children()[i]->NumPoints(); + + // Copy the local datasets + arma::Mat tmp(localDataset->n_rows,numPoints); + + size_t iPoint = 0; + for(size_t i = firstSibling; i<= lastSibling; i++) + { + DiscreteHilbertValue &value = + parent->Children()[i]->AuxiliaryInfo().HilbertValue(); + + for(size_t j = 0; j < value.NumValues(); j++) + { + tmp.col(iPoint) = value.LocalDataset()->col(j); + iPoint++; + } + } + assert(iPoint == numPoints); + + iPoint = 0; + + // Redistribute the Hilbert values + for(size_t i = firstSibling; i<= lastSibling; i++) + { + DiscreteHilbertValue &value = + parent->Children()[i]->AuxiliaryInfo().HilbertValue(); + + for(size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) + { + value.LocalDataset()->col(j) = tmp.col(iPoint); + iPoint++; + } + value.NumValues() = parent->Children()[i]->NumPoints(); + } + + assert(iPoint == numPoints); + +} + + +template +bool DiscreteHilbertValue::HasValue() const +{ + return numValues > 0; } } // namespace tree diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp index 01bbf99832..12a467a3dc 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp @@ -12,10 +12,13 @@ namespace mlpack { namespace tree { -template +template class HilbertValueType> class HilbertRTreeAuxiliaryInformation { public: + //! The element type held by the tree. + typedef typename TreeType::ElemType ElemType; //! Default constructor HilbertRTreeAuxiliaryInformation(); @@ -23,13 +26,13 @@ class HilbertRTreeAuxiliaryInformation * Construct this as an axiliary information for the node node. * @param node The node that stores this auxiliary information. */ - HilbertRTreeAuxiliaryInformation(const TreeType *node); + HilbertRTreeAuxiliaryInformation(const TreeType* node); /** * Create an auxiliary information object by copying from the other node. * @param other The node from which the information will be copied. */ - HilbertRTreeAuxiliaryInformation(const TreeType &other); + HilbertRTreeAuxiliaryInformation(const HilbertRTreeAuxiliaryInformation& other); //! Free memory ~HilbertRTreeAuxiliaryInformation(); @@ -42,7 +45,12 @@ class HilbertRTreeAuxiliaryInformation * @param node The node in which the point is being inserted. * @param point The number of the point being inserted. */ - bool HandlePointInsertion(TreeType *node, const size_t point); + bool HandlePointInsertion(TreeType* node, const size_t point); + + template + bool HandlePointInsertion(TreeType* node, const VecType& point, + typename boost::enable_if>* = 0); + /** * The Hilbert R tree requires to insert nodes according to their @@ -54,8 +62,8 @@ class HilbertRTreeAuxiliaryInformation * @param insertionLevel The level of the tree at which the nodeToInsert * should be inserted. */ - bool HandleNodeInsertion(TreeType *node, - TreeType *nodeToInsert,bool insertionLevel); + bool HandleNodeInsertion(TreeType* node, + TreeType* nodeToInsert,bool insertionLevel); /** * The Hilbert R tree requires all points to be arranged according to their @@ -66,7 +74,7 @@ class HilbertRTreeAuxiliaryInformation * @param node The node from which the point is being deleted. * @param localIndex The index of the point being deleted. */ - bool HandlePointDeletion(TreeType *node,const size_t localIndex); + bool HandlePointDeletion(TreeType* node,const size_t localIndex); /** * The Hilbert R tree requires all nodes to be arranged according to their @@ -77,31 +85,34 @@ class HilbertRTreeAuxiliaryInformation * @param node The node from which the node is being deleted. * @param nodeIndex The index of the node being deleted. */ - bool HandleNodeRemoval(TreeType *node,const size_t nodeIndex); + bool HandleNodeRemoval(TreeType* node,const size_t nodeIndex); /** * Update the auxiliary information in the node. The method returns true * if the update should be propogated downward. * @param node The node in which the auxiliary information being update. */ - bool UpdateAuxiliaryInfo(TreeType *node); + bool UpdateAuxiliaryInfo(TreeType* node); /** * Copy the auxiliary information from one node to another. * @param dst The node to which the information is being copied. * @param src The node from which the information is being copied. */ - void Copy(TreeType *dst,TreeType *src); + void Copy(TreeType* dst,TreeType* src); + + void NullifyData(); private: //! The largest Hilbert value of a point enclosed by the node. - HilbertValue *largestHilbertValue; + HilbertValueType hilbertValue; public: //! Return the largest Hilbert value of a point covered by the node. - HilbertValue& LargestHilbertValue() const { return *largestHilbertValue; } + const HilbertValueType& HilbertValue() const + { return hilbertValue; } //! Modify the largest Hilbert value of a point covered by the node. - HilbertValue& LargestHilbertValue() { return *largestHilbertValue; } + HilbertValueType& HilbertValue() { return hilbertValue; } /** * Serialize the information. diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index 0341d30372..08549d0a7b 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -15,45 +15,50 @@ namespace mlpack { namespace tree { -template -HilbertRTreeAuxiliaryInformation:: +template class HilbertValueType> +HilbertRTreeAuxiliaryInformation:: HilbertRTreeAuxiliaryInformation() { }; -template -HilbertRTreeAuxiliaryInformation:: -HilbertRTreeAuxiliaryInformation(const TreeType *node) : - largestHilbertValue(new HilbertValue(node)) +template class HilbertValueType> +HilbertRTreeAuxiliaryInformation:: +HilbertRTreeAuxiliaryInformation(const TreeType* node) : + hilbertValue(node) { }; -template -HilbertRTreeAuxiliaryInformation:: -HilbertRTreeAuxiliaryInformation(const TreeType &other) : - largestHilbertValue(new HilbertValue(other)) +template class HilbertValueType> +HilbertRTreeAuxiliaryInformation:: +HilbertRTreeAuxiliaryInformation(const HilbertRTreeAuxiliaryInformation& other) : + hilbertValue(other.HilbertValue()) { }; -template -HilbertRTreeAuxiliaryInformation:: +template class HilbertValueType> +HilbertRTreeAuxiliaryInformation:: ~HilbertRTreeAuxiliaryInformation() { - delete largestHilbertValue; + } -template -bool HilbertRTreeAuxiliaryInformation:: -HandlePointInsertion(TreeType *node,const size_t point) +template class HilbertValueType> +bool HilbertRTreeAuxiliaryInformation:: +HandlePointInsertion(TreeType* node, const size_t point) { if(node->IsLeaf()) { // Get the position at which the point should be inserted // Update the largest Hilbert value of the node - size_t pos = largestHilbertValue->InsertPoint(node,point); + size_t pos = hilbertValue.InsertPoint(node, node->Dataset().col(point)); // Move points for(size_t i = node->NumPoints(); i > pos; i--) @@ -67,14 +72,51 @@ HandlePointInsertion(TreeType *node,const size_t point) node->Count()++; } else - largestHilbertValue->InsertPoint(node,point); // Update LHV + { + // Calculate the Hilbert value + hilbertValue.InsertPoint(node, node->Dataset().col(point)); + } return true; } -template -bool HilbertRTreeAuxiliaryInformation:: -HandleNodeInsertion(TreeType *node,TreeType *nodeToInsert,bool insertionLevel) +template class HilbertValueType> +template +bool HilbertRTreeAuxiliaryInformation:: +HandlePointInsertion(TreeType* node, const VecType& point, + typename boost::enable_if>*) +{ + if(node->IsLeaf()) + { + // Get the position at which the point should be inserted + // Update the largest Hilbert value of the node + size_t pos = hilbertValue.InsertPoint(node, point); + + // Move points + for(size_t i = node->NumPoints(); i > pos; i--) + { + node->Points()[i] = node->Points()[i-1]; + node->LocalDataset().col(i) = node->LocalDataset().col(i-1); + } + // Insert the point + node->Points()[pos] = node->Dataset().n_cols; + node->LocalDataset().col(pos) = point; + node->Count()++; + } + else + { + // Calculate the Hilbert value + hilbertValue.InsertPoint(node, point); + } + + return true; +} + +template class HilbertValueType> +bool HilbertRTreeAuxiliaryInformation:: +HandleNodeInsertion(TreeType* node,TreeType* nodeToInsert,bool insertionLevel) { if(insertionLevel) { @@ -83,9 +125,9 @@ HandleNodeInsertion(TreeType *node,TreeType *nodeToInsert,bool insertionLevel) // Find the best position for the node being inserted. // The node should be inserted according to its Hilbert value. for(pos = 0; pos < node->NumChildren(); pos++) - if(HilbertValue::CompareValues( - node->Children()[pos]->AuxiliaryInfo().LargestHilbertValue(), - nodeToInsert->AuxiliaryInfo().LargestHilbertValue()) < 0) + if(HilbertValueType::CompareValues( + node->Children()[pos]->AuxiliaryInfo().HilbertValue(), + nodeToInsert->AuxiliaryInfo().HilbertValue()) < 0) break; // Move nodes @@ -97,20 +139,21 @@ HandleNodeInsertion(TreeType *node,TreeType *nodeToInsert,bool insertionLevel) nodeToInsert->Parent() = node; // Update the largest Hilbert value - largestHilbertValue->InsertNode(nodeToInsert); + hilbertValue.InsertNode(nodeToInsert); } else - largestHilbertValue->InsertNode(nodeToInsert); // Update LHV + hilbertValue.InsertNode(nodeToInsert); // Update LHV return true; } -template -bool HilbertRTreeAuxiliaryInformation:: -HandlePointDeletion(TreeType *node,const size_t localIndex) +template class HilbertValueType> +bool HilbertRTreeAuxiliaryInformation:: +HandlePointDeletion(TreeType* node,const size_t localIndex) { // Update the largest Hilbert value - largestHilbertValue->DeletePoint(node,localIndex); + hilbertValue.DeletePoint(node,localIndex); for(size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) { @@ -121,12 +164,13 @@ HandlePointDeletion(TreeType *node,const size_t localIndex) return true; } -template -bool HilbertRTreeAuxiliaryInformation:: -HandleNodeRemoval(TreeType *node,const size_t nodeIndex) +template class HilbertValueType> +bool HilbertRTreeAuxiliaryInformation:: +HandleNodeRemoval(TreeType* node,const size_t nodeIndex) { // Update the largest Hilbert value - largestHilbertValue->RemoveNode(node,nodeIndex); + hilbertValue.RemoveNode(node,nodeIndex); for(size_t i = nodeIndex + 1; nodeIndex < node->NumChildren(); i++) node->Children()[i-1] = node->Children()[i]; @@ -135,39 +179,50 @@ HandleNodeRemoval(TreeType *node,const size_t nodeIndex) return true; } -template -bool HilbertRTreeAuxiliaryInformation:: -UpdateAuxiliaryInfo(TreeType *node) +template class HilbertValueType> +bool HilbertRTreeAuxiliaryInformation:: +UpdateAuxiliaryInfo(TreeType* node) { if(node->IsLeaf()) // Should already be updated return true; TreeType *child = node->Children()[node->NumChildren()-1]; - if(HilbertValue::CompareValues(largestHilbertValue, - child->AuxiliaryInfo().LargestHilbertValue()) < 0) + if(HilbertValueType::CompareValues(hilbertValue, + child->AuxiliaryInfo().hilbertValue()) < 0) { - largestHilbertValue->Copy(node,child); -// largestHilbertValue = child->AuxiliaryInfo().LargestHilbertValue(); + hilbertValue.Copy(node,child); +// hilbertValue = child->AuxiliaryInfo().hilbertValue(); return true; } return false; } -template -void HilbertRTreeAuxiliaryInformation:: -Copy(TreeType *dst,TreeType *src) +template class HilbertValueType> +void HilbertRTreeAuxiliaryInformation:: +Copy(TreeType* dst,TreeType* src) { - largestHilbertValue->Copy(dst,src); + hilbertValue.Copy(dst,src); } -template +template class HilbertValueType> +void HilbertRTreeAuxiliaryInformation:: +NullifyData() +{ + hilbertValue.NullifyData(); +} + +template class HilbertValueType> template -void HilbertRTreeAuxiliaryInformation:: +void HilbertRTreeAuxiliaryInformation:: Serialize(Archive& ar, const unsigned int /* version */) { using data::CreateNVP; - ar & CreateNVP(largestHilbertValue, "largestHilbertValue"); + ar & CreateNVP(hilbertValue, "hilbertValue"); } diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp index 4a40357caa..83474a1fa5 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp @@ -20,7 +20,7 @@ ChooseDescentNode(const TreeType* node, const size_t point) size_t bestIndex = 0; for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if(node->Children()[bestIndex]->AuxiliaryInfo().LargestHilbertValue().CompareWith(node,point) > 0) + if(node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWithCachedPoint(node->Dataset().col(point)) > 0) break; return bestIndex; @@ -33,7 +33,7 @@ ChooseDescentNode(const TreeType* node, const arma::vec& point) size_t bestIndex = 0; for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if(node->Children()[bestIndex]->AuxiliaryInfo().LargestHilbertValue().CompareWith(node,point) > 0) + if(node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWithCachedPoint(point) > 0) break; return bestIndex; @@ -46,7 +46,7 @@ ChooseDescentNode(const TreeType* node, const TreeType* insertedNode) size_t bestIndex = 0; for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if(node->Children()[bestIndex]->AuxiliaryInfo().LargestHilbertValue().CompareWith(node,node->AuxiliaryInfo().LargestHilbertValue()) > 0) + if(node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWith(node,node->AuxiliaryInfo().HilbertValue()) > 0) break; return bestIndex; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index 2315e88c65..5e066d3888 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -16,7 +16,7 @@ namespace tree { template void HilbertRTreeSplit:: -SplitLeafNode(TreeType *tree,std::vector& relevels) +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 @@ -30,21 +30,20 @@ SplitLeafNode(TreeType *tree,std::vector& relevels) tree->NullifyData(); // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; - HilbertRTreeSplit::SplitLeafNode(copy,relevels); + HilbertRTreeSplit::SplitLeafNode(copy, relevels); return; } TreeType *parent = tree->Parent(); - size_t iTree = 0; for(iTree = 0;parent->Children()[iTree] != tree; iTree++); // Try to find splitOrder cooperating siblings in order to redistribute // points among them and avoid split. size_t firstSibling,lastSibling; - if(FindCooperatingSiblings(parent,iTree,firstSibling,lastSibling)) + if(FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling)) { - RedistributePointsEvenly(parent,firstSibling,lastSibling); + RedistributePointsEvenly(parent, firstSibling, lastSibling); return; } @@ -70,16 +69,16 @@ SplitLeafNode(TreeType *tree,std::vector& relevels) assert(lastSibling < parent->NumChildren()); // Redistribute the points among (splitOrder+1) cooperating siblings evenly. - RedistributePointsEvenly(parent,firstSibling,lastSibling); + RedistributePointsEvenly(parent, firstSibling, lastSibling); if(parent->NumChildren() == parent->MaxNumChildren() + 1) - HilbertRTreeSplit::SplitNonLeafNode(parent,relevels); + HilbertRTreeSplit::SplitNonLeafNode(parent, relevels); } template bool HilbertRTreeSplit:: -SplitNonLeafNode(TreeType *tree,std::vector& relevels) +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 @@ -94,7 +93,7 @@ SplitNonLeafNode(TreeType *tree,std::vector& relevels) tree->NullifyData(); tree->Children()[(tree->NumChildren())++] = copy; - HilbertRTreeSplit::SplitNonLeafNode(copy,relevels); + HilbertRTreeSplit::SplitNonLeafNode(copy, relevels); return true; } @@ -106,9 +105,9 @@ SplitNonLeafNode(TreeType *tree,std::vector& relevels) // Try to find splitOrder cooperating siblings in order to redistribute // children among them and avoid split. size_t firstSibling,lastSibling; - if(FindCooperatingSiblings(parent,iTree,firstSibling,lastSibling)) + if(FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling)) { - RedistributeNodesEvenly(parent,firstSibling,lastSibling); + RedistributeNodesEvenly(parent, firstSibling, lastSibling); return false; } @@ -135,10 +134,10 @@ SplitNonLeafNode(TreeType *tree,std::vector& relevels) assert(lastSibling < parent->NumChildren()); // Redistribute children among (splitOrder+1) cooperating siblings evenly. - RedistributeNodesEvenly(parent,firstSibling,lastSibling); + RedistributeNodesEvenly(parent, firstSibling, lastSibling); if(parent->NumChildren() == parent->MaxNumChildren() + 1) - HilbertRTreeSplit::SplitNonLeafNode(parent,relevels); + HilbertRTreeSplit::SplitNonLeafNode(parent, relevels); return false; } @@ -251,7 +250,7 @@ RedistributeNodesEvenly(const TreeType *parent, parent->Children()[i]->MaxNumChildren()); // Fix the largest Hilbert value of the sibling. - parent->Children()[i]->AuxiliaryInfo().LargestHilbertValue().UpdateLargestValue(parent->Children()[i]); + parent->Children()[i]->AuxiliaryInfo().HilbertValue().UpdateLargestValue(parent->Children()[i]); } } @@ -270,6 +269,8 @@ RedistributePointsEvenly(TreeType *parent, numRestPoints = numPoints % (lastSibling - firstSibling + 1); std::vector points(numPoints); + arma::Mat tmp(parent->Child(firstSibling).LocalDataset().n_rows, + numPoints); // Copy children's points in order to redistribute them. size_t iPoint = 0; @@ -278,6 +279,7 @@ RedistributePointsEvenly(TreeType *parent, for(size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) { points[iPoint] = parent->Children()[i]->Points()[j]; + tmp.col(iPoint) = parent->Children()[i]->LocalDataset().col(j); iPoint++; } } @@ -292,20 +294,16 @@ RedistributePointsEvenly(TreeType *parent, size_t j; for(j = 0; j < numPointsPerNode; j++) { - parent->Children()[i]->Bound() |= - parent->Children()[i]->Dataset().col(points[iPoint]); + parent->Children()[i]->Bound() |= tmp.col(iPoint); parent->Children()[i]->Points()[j] = points[iPoint]; - parent->Children()[i]->LocalDataset().col(j) = - parent->Children()[i]->Dataset().col(points[iPoint]); + parent->Children()[i]->LocalDataset().col(j) = tmp.col(iPoint); iPoint++; } if(numRestPoints > 0) { - parent->Children()[i]->Bound() |= - parent->Children()[i]->Dataset().col(points[iPoint]); + parent->Children()[i]->Bound() |= tmp.col(iPoint); parent->Children()[i]->Points()[j] = points[iPoint]; - parent->Children()[i]->LocalDataset().col(j) = - parent->Children()[i]->Dataset().col(points[iPoint]); + parent->Children()[i]->LocalDataset().col(j) = tmp.col(iPoint); parent->Children()[i]->Count() = numPointsPerNode + 1; numRestPoints--; iPoint++; @@ -316,15 +314,15 @@ RedistributePointsEvenly(TreeType *parent, } assert(parent->Children()[i]->NumPoints() <= parent->Children()[i]->MaxLeafSize()); - // Fix the largest Hilbert value of the sibling. - parent->Children()[i]->AuxiliaryInfo().LargestHilbertValue().UpdateLargestValue(parent->Children()[i]); } + // Fix the largest Hilbert values of the siblings. + parent->AuxiliaryInfo().HilbertValue().UpdateHilbertValues(parent, firstSibling, lastSibling); TreeType *root = parent; while(root != NULL) { - root->AuxiliaryInfo().LargestHilbertValue().UpdateLargestValue(root); + root->AuxiliaryInfo().HilbertValue().UpdateLargestValue(root); root = root->Parent(); } } diff --git a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp index 7e68f3f417..e0f3f1b682 100644 --- a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp @@ -16,14 +16,18 @@ class NoAuxiliaryInformation { public: NoAuxiliaryInformation() { }; - NoAuxiliaryInformation(const TreeType *) { }; - NoAuxiliaryInformation(const TreeType &) { }; + NoAuxiliaryInformation(const TreeType* ) { }; + NoAuxiliaryInformation(const TreeType& ) { }; /** * Some tree types require to save some properties at the insertion process. * This method should return false if it does not handle the process. */ - bool HandlePointInsertion(TreeType *, const size_t) + bool HandlePointInsertion(TreeType* , const size_t) + { + return false; + } + bool HandlePointInsertion(TreeType* , const arma::vec& ) { return false; } @@ -32,7 +36,7 @@ class NoAuxiliaryInformation * Some tree types require to save some properties at the insertion process. * This method should return false if it does not handle the process. */ - bool HandleNodeInsertion(TreeType *,TreeType *,bool) + bool HandleNodeInsertion(TreeType* , TreeType* ,bool) { return false; } @@ -41,7 +45,7 @@ class NoAuxiliaryInformation * Some tree types require to save some properties at the deletion process. * This method should return false if it does not handle the process. */ - bool HandlePointDeletion(TreeType *,const size_t) + bool HandlePointDeletion(TreeType* , const size_t) { return false; } @@ -50,7 +54,7 @@ class NoAuxiliaryInformation * Some tree types require to save some properties at the deletion process. * This method should return false if it does not handle the process. */ - bool HandleNodeRemoval(TreeType *,const size_t) + bool HandleNodeRemoval(TreeType* , const size_t) { return false; } @@ -59,7 +63,7 @@ class NoAuxiliaryInformation * Some tree types require to propagate the information downward. * This method should return false if this is not the case. */ - bool UpdateAuxiliaryInfo(TreeType *) + bool UpdateAuxiliaryInfo(TreeType* ) { return false; } @@ -67,7 +71,10 @@ class NoAuxiliaryInformation /** * Nothing to copy. */ - void Copy(TreeType *,TreeType *) + void Copy(TreeType* , TreeType* ) + { } + + void NullifyData() { } 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 060c2c079b..2a5e19be16 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 @@ -73,19 +73,39 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) } std::sort(sorted.begin(), sorted.end(), StructComp); - std::vector pointIndices(p); + std::vector pointIndices(p); + arma::Mat localDataset(tree->Dataset().n_rows, p); for (size_t i = 0; i < p; i++) { // We start from the end of sorted. pointIndices[i] = tree->Points()[sorted[sorted.size() - 1 - i].n]; - root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], - relevels); + localDataset.col(i) = + tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n); + + if(tree->Points()[sorted[sorted.size() - 1 - i].n] < + tree->Dataset().n_cols) + root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], + relevels); + else + { + tree->Count()--; + tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n) = + tree->LocalDataset().col(tree->Count()); + tree->Points()[sorted[sorted.size() - 1 - i].n] = + tree->Points()[tree->Count()]; + // This function will ensure that minFill is satisfied. + tree->CondenseTree(localDataset.col(i), relevels, true); + + } } for (size_t i = 0; i < p; i++) { // We reverse the order again to reinsert the closest points first. - root->InsertPoint(pointIndices[p - 1 - i], relevels); + if(pointIndices[p - 1 - i] < tree->Dataset().n_cols) + root->InsertPoint(pointIndices[p - 1 - i], relevels); + else + root->InsertPoint(localDataset[p - 1 - i], relevels); } return; @@ -222,9 +242,19 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); + { + if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + treeOne->InsertPoint(tree->Points()[sorted[i].n]); + else + treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); + } else - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + { + if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + else + treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); + } } } else @@ -232,9 +262,19 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); + { + if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + treeOne->InsertPoint(tree->Points()[sorted[i].n]); + else + treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); + } else - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + { + if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + else + treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); + } } } diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index 84b728e06b..4149559f5b 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -94,7 +94,7 @@ class RectangleTree //! The local dataset MatType* localDataset; //! A tree-specific information - AuxiliaryInformationType *auxiliaryInfo; + AuxiliaryInformationType auxiliaryInfo; public: //! A single traverser for rectangle type trees. See @@ -202,6 +202,7 @@ class RectangleTree * @param point The point (arma::vec&) to be inserted. */ void InsertPoint(const size_t point); + void InsertPoint(const arma::vec &point); /** * Inserts a point into the tree, tracking which levels have been inserted @@ -214,6 +215,7 @@ class RectangleTree * insertion. */ void InsertPoint(const size_t point, std::vector& relevels); + void InsertPoint(const arma::vec &point, std::vector& relevels); /** * Inserts a node into the tree, tracking which levels have been inserted @@ -239,6 +241,7 @@ class RectangleTree * removed and false if it is not. (ie. the point is not in the tree) */ bool DeletePoint(const size_t point); + bool DeletePoint(const arma::vec &point); /** * Deletes a point in the tree, tracking levels. The point will be removed @@ -250,6 +253,7 @@ class RectangleTree * the tree) */ bool DeletePoint(const size_t point, std::vector& relevels); + bool DeletePoint(const arma::vec &point, std::vector& relevels); /** * Removes a node from the tree. You are responsible for deleting it if you @@ -295,10 +299,10 @@ class RectangleTree //! Return the auxiliary information object of this node. const AuxiliaryInformationType &AuxiliaryInfo() const - { return *auxiliaryInfo; } + { return auxiliaryInfo; } //! Modify the split object of this node. AuxiliaryInformationType& AuxiliaryInfo() - { return *auxiliaryInfo; } + { return auxiliaryInfo; } //! 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 c9be471bed..f76fc6c721 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -46,12 +46,11 @@ RectangleTree(const MatType& data, ownsDataset(true), points(maxLeafSize + 1), // Add one to make splitting the node simpler. localDataset(new MatType(arma::zeros(data.n_rows, - maxLeafSize + 1))) + maxLeafSize + 1))), + auxiliaryInfo(this) { stat = StatisticType(*this); - auxiliaryInfo = new AuxiliaryInformationType(this); - // For now, just insert the points in order. RectangleTree* root = this; @@ -88,12 +87,11 @@ RectangleTree(MatType&& data, ownsDataset(true), points(maxLeafSize + 1), // Add one to make splitting the node simpler. localDataset(new MatType(arma::zeros(dataset->n_rows, - maxLeafSize + 1))) + maxLeafSize + 1))), + auxiliaryInfo(this) { stat = StatisticType(*this); - auxiliaryInfo = new AuxiliaryInformationType(this); - // For now, just insert the points in order. RectangleTree* root = this; @@ -129,10 +127,10 @@ RectangleTree( ownsDataset(false), points(maxLeafSize + 1), // Add one to make splitting the node simpler. localDataset(new MatType(arma::zeros(parentNode->Bound().Dim(), - maxLeafSize + 1))) + maxLeafSize + 1))), + auxiliaryInfo(this) { stat = StatisticType(*this); - auxiliaryInfo = new AuxiliaryInformationType(this); } /** @@ -164,9 +162,9 @@ RectangleTree( dataset(deepCopy ? new MatType(*other.dataset) : &other.Dataset()), ownsDataset(deepCopy), points(other.Points()), - localDataset(NULL) + localDataset(NULL), + auxiliaryInfo(other.auxiliaryInfo) { - auxiliaryInfo = new AuxiliaryInformationType(other); if (deepCopy) { if (numChildren > 0) @@ -225,7 +223,6 @@ RectangleTree:: ~RectangleTree() { - delete auxiliaryInfo; for (size_t i = 0; i < numChildren; i++) delete children[i]; @@ -272,6 +269,7 @@ void RectangleTreeHandlePointInsertion(this,point)) + if(!auxiliaryInfo.HandlePointInsertion(this,point)) { localDataset->col(count) = dataset->col(point); points[count++] = point; @@ -309,7 +307,7 @@ void RectangleTreeHandlePointInsertion(this,point); + auxiliaryInfo.HandlePointInsertion(this,point); const size_t descentNode = DescentType::ChooseDescentNode(this, point); children[descentNode]->InsertPoint(point, lvls); } @@ -336,7 +334,7 @@ void RectangleTreeHandlePointInsertion(this,point)) + if(!auxiliaryInfo.HandlePointInsertion(this,point)) { localDataset->col(count) = dataset->col(point); points[count++] = point; @@ -347,7 +345,85 @@ void RectangleTreeHandlePointInsertion(this,point); + auxiliaryInfo.HandlePointInsertion(this,point); + const size_t descentNode = DescentType::ChooseDescentNode(this,point); + children[descentNode]->InsertPoint(point, relevels); +} + +/** + * Recurse through the tree and insert the point at the leaf node chosen + * by the heuristic. + */ +template class AuxiliaryInformationType> +void RectangleTree:: + InsertPoint(const arma::vec &point) +{ + // Expand the bound regardless of whether it is a leaf node. + bound |= point; + + std::vector lvls(TreeDepth()); + for (size_t i = 0; i < lvls.size(); i++) + lvls[i] = true; + + // If this is a leaf node, we stop here and add the point. + if (numChildren == 0) + { + if(!auxiliaryInfo.HandlePointInsertion(this,point)) + { + localDataset->col(count) = point; + points[count++] = dataset->n_cols; + } + SplitNode(lvls); + return; + } + + // If it is not a leaf node, we use the DescentHeuristic to choose a child + // to which we recurse. + auxiliaryInfo.HandlePointInsertion(this,point); + const size_t descentNode = DescentType::ChooseDescentNode(this, point); + children[descentNode]->InsertPoint(point, lvls); +} + +/** + * Inserts a point into the tree, tracking which levels have been inserted into. + * The point will be copied to the data matrix of the leaf node where it is + * finally inserted, but we pass by reference since it may be passed many times + * before it actually reaches a leaf. + */ +template class AuxiliaryInformationType> +void RectangleTree:: + InsertPoint(const arma::vec &point, std::vector& relevels) +{ + // Expand the bound regardless of whether it is a leaf node. + bound |= point; + + // If this is a leaf node, we stop here and add the point. + if (numChildren == 0) + { + if(!auxiliaryInfo.HandlePointInsertion(this,point)) + { + localDataset->col(count) = point; + points[count++] = dataset->n_cols; + } + SplitNode(relevels); + return; + } + + // If it is not a leaf node, we use the DescentHeuristic to choose a child + // to which we recurse. + auxiliaryInfo.HandlePointInsertion(this,point); const size_t descentNode = DescentType::ChooseDescentNode(this,point); children[descentNode]->InsertPoint(point, relevels); } @@ -376,7 +452,7 @@ void RectangleTreeBound(); if (level == TreeDepth()) { - if(!auxiliaryInfo->HandleNodeInsertion(this,node,true)) + if(!auxiliaryInfo.HandleNodeInsertion(this,node,true)) { children[numChildren++] = node; node->Parent() = this; @@ -385,7 +461,7 @@ void RectangleTreeHandleNodeInsertion(this,node,false); + auxiliaryInfo.HandleNodeInsertion(this,node,false); const size_t descentNode = DescentType::ChooseDescentNode(this, node); children[descentNode]->InsertNode(node, level, relevels); } @@ -421,7 +497,7 @@ bool RectangleTreeHandlePointDeletion(this,i)) + if(!auxiliaryInfo.HandlePointDeletion(this,i)) { localDataset->col(i) = localDataset->col(--count); // Decrement count. points[i] = points[count]; @@ -461,7 +537,7 @@ bool RectangleTreeHandlePointDeletion(this,i)) + if(!auxiliaryInfo.HandlePointDeletion(this,i)) { localDataset->col(i) = localDataset->col(--count); points[i] = points[count]; @@ -481,6 +557,96 @@ bool RectangleTree class AuxiliaryInformationType> +bool RectangleTree:: + DeletePoint(const arma::vec &point) +{ + // It is possible that this will cause a reinsertion, so we need to handle the + // levels properly. + RectangleTree* root = this; + while (root->Parent() != NULL) + root = root->Parent(); + + std::vector lvls(root->TreeDepth()); + for (size_t i = 0; i < lvls.size(); i++) + lvls[i] = true; + + if (numChildren == 0) + { + for (size_t i = 0; i < count; i++) + { + if (localDataset[i] == point) + { + if(!auxiliaryInfo.HandlePointDeletion(this,i)) + { + localDataset->col(i) = localDataset->col(--count); // Decrement count. + points[i] = points[count]; + } + // This function wil ensure that minFill is satisfied. + CondenseTree(point, lvls, true); + return true; + } + } + } + + for (size_t i = 0; i < numChildren; i++) + if (children[i]->Bound().Contains(point)) + if (children[i]->DeletePoint(point, lvls)) + return true; + + return false; +} + +/** + * Recurse through the tree to remove the point. Once we find the point, we + * shrink the rectangles if necessary. + */ +template class AuxiliaryInformationType> +bool RectangleTree:: + DeletePoint(const arma::vec &point, std::vector& relevels) +{ + if (numChildren == 0) + { + for (size_t i = 0; i < count; i++) + { + if (localDataset[i] == point) + { + if(!auxiliaryInfo.HandlePointDeletion(this,i)) + { + localDataset->col(i) = localDataset->col(--count); + points[i] = points[count]; + } + // This function will ensure that minFill is satisfied. + CondenseTree(point, relevels, true); + return true; + } + } + } + + for (size_t i = 0; i < numChildren; i++) + if (children[i]->Bound().Contains(point)) + if (children[i]->DeletePoint(point, relevels)) + return true; + + return false; +} + /** * Recurse through the tree to remove the node. Once we find the node, we * shrink the rectangles if necessary. @@ -499,7 +665,7 @@ bool RectangleTreeHandleNodeRemoval(this,i)) + if(!auxiliaryInfo.HandleNodeRemoval(this,i)) { children[i] = children[--numChildren]; // Decrement numChildren. } @@ -794,7 +960,10 @@ void RectangleTreeChildren()[i] == this) { // Decrement numChildren. - parent->Children()[i] = parent->Children()[--parent->NumChildren()]; + if(!auxiliaryInfo.HandleNodeRemoval(parent,i)) + { + parent->Children()[i] = parent->Children()[--parent->NumChildren()]; + } // We find the root and shrink bounds at the same time. bool stillShrinking = true; @@ -821,7 +990,10 @@ void RectangleTreeInsertPoint(points[j], relevels); + if(points[j] < dataset->n_cols) + root->InsertPoint(points[j], relevels); + else + root->InsertPoint(localDataset[j], relevels); // This will check the minFill of the parent. parent->CondenseTree(point, relevels, usePoint); @@ -844,7 +1016,7 @@ void RectangleTreeChildren()[j] == this) { // Decrement numChildren. - if(!auxiliaryInfo->HandleNodeRemoval(parent,j)) + if(!auxiliaryInfo.HandleNodeRemoval(parent,j)) { parent->Children()[j] = parent->Children()[--parent->NumChildren()]; } @@ -912,7 +1084,7 @@ void RectangleTreecol(i) = child->LocalDataset().col(i); } - auxiliaryInfo->Copy(this,child); + auxiliaryInfo.Copy(this,child); count = child->Count(); child->SoftDelete(); @@ -922,11 +1094,11 @@ void RectangleTreeUpdateAuxiliaryInfo(this)) && + (ShrinkBoundForPoint(point) || auxiliaryInfo.UpdateAuxiliaryInfo(this)) && parent != NULL) parent->CondenseTree(point, relevels, usePoint); else if (!usePoint && - (ShrinkBoundForBound(bound) || auxiliaryInfo->UpdateAuxiliaryInfo(this)) && + (ShrinkBoundForBound(bound) || auxiliaryInfo.UpdateAuxiliaryInfo(this)) && parent != NULL) parent->CondenseTree(point, relevels, usePoint); } diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp index d020461ae1..b46ab37568 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp @@ -14,13 +14,13 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { constexpr int recursionDepth = 500; + +template class RecursiveHilbertValue { public: //! Default constructor - RecursiveHilbertValue() : - largestValue(-1) - { }; + RecursiveHilbertValue(); /** * Construct this for the node tree. If the node is the root this method @@ -28,34 +28,30 @@ class RecursiveHilbertValue * @param node The node that stores this Hilbert value. */ template - RecursiveHilbertValue(const TreeType *) : - largestValue(-1) - { }; + RecursiveHilbertValue(const TreeType* tree); /** - * Create a Hilbert value object by copying from the other node. - * @param other The node from which the value will be copied. + * Create a Hilbert value object by copying from another one. + * @param other The Hilbert value object from which the value will be copied. */ - template - RecursiveHilbertValue(const TreeType &other) : - largestValue(other.AuxiliaryInfo().LargestHilbertValue().LargestValue()) - { }; + RecursiveHilbertValue(const RecursiveHilbertValue& other); + + ~RecursiveHilbertValue(); //! This struct is designed in order to facilitate the recursion. - template - struct tagCompareStruct + typedef struct tagCompareStruct { //! Lower bound - arma::Col Lo; + arma::Col Lo; //! High bound - arma::Col Hi; + arma::Col Hi; //! Permutation of axes std::vector permutation; //! Indicates that the axis should be inverted std::vector inversion; //! Indicates that the result should be inverted - arma::Col center; - arma::Col vec; + arma::Col center; + arma::Col vec; std::vector bits; std::vector bits2; bool invertResult; @@ -76,15 +72,13 @@ class RecursiveHilbertValue { for(size_t i = 0; i < dim; i++) { - Lo[i] = std::numeric_limits::lowest(); - Hi[i] = std::numeric_limits::max(); + Lo[i] = std::numeric_limits::lowest(); + Hi[i] = std::numeric_limits::max(); permutation[i] = i; inversion[i] = false; } } - }; - template - using CompareStruct = struct tagCompareStruct; + } CompareStruct; /** * Compare two points. It returns 1 if the first point is greater than @@ -93,9 +87,10 @@ class RecursiveHilbertValue * @param pt1 The first point. * @param pt2 The second point. */ - template - static int ComparePoints(const arma::Col &pt1, - const arma::Col &pt2); + template + static int ComparePoints(const VecType1& pt1, const VecType2& pt2, + typename boost::enable_if>* = 0, + typename boost::enable_if>* = 0); /** * Compare two Hilbert values. It returns 1 if the first value is greater than @@ -104,57 +99,50 @@ class RecursiveHilbertValue * @param val1 The first Hilbert value. * @param val2 The second Hilbert value. */ - template - static int CompareValues(TreeType *tree, RecursiveHilbertValue &val1, - RecursiveHilbertValue &val2); + + static int CompareValues(const RecursiveHilbertValue& val1, + const RecursiveHilbertValue& val2); /** * Compare the largest Hilbert value of the node with the val value. * It returns 1 if the value of the node is greater than val, * -1 if the value of the node is less than val and * 0 if the values are equal. - * @param tree The pointer to the tree. * @param val The Hilbert value to compare with. */ - template - int CompareWith(TreeType *tree, RecursiveHilbertValue &val); + int CompareWith(const RecursiveHilbertValue& val) const; /** * Compare the largest Hilbert value of the node with the Hilbert value * of the point. It returns 1 if the value of the node is greater than * the value of the point, -1 if the value of the node is less than * the value of the point and 0 if the values are equal. - * @param tree The pointer to the tree. - * @param pt The point to compare with. + * @param point The point to compare with. */ - template - int CompareWith(TreeType *tree, const arma::Col &pt); + template + int CompareWith(const VecType& point, + typename boost::enable_if>* = 0) const; + + template + int CompareWithCachedPoint(const VecType& point, + typename boost::enable_if>* = 0) const; - /** - * Compare the largest Hilbert value of the node with the Hilbert value - * of the point. It returns 1 if the value of the node is greater than - * the value of the point, -1 if the value of the node is less than - * the value of the point and 0 if the values are equal. - * @param tree The pointer to the tree. - * @param point The number of the point to compare with. - */ - template - int CompareWith(TreeType *tree, const size_t point); /** * Update the largest Hilbert value of the node. * @param node The node in which the point is being inserted. * @param point The number of the point being inserted. */ - template - size_t InsertPoint(TreeType *node, const size_t point); + template + size_t InsertPoint(TreeType* node, const VecType& point, + typename boost::enable_if>* = 0); /** * Update the largest Hilbert value of the node. * @param node The node being inserted. */ template - void InsertNode(TreeType *node); + void InsertNode(TreeType* node); /** * Update the largest Hilbert value of the node. @@ -162,7 +150,7 @@ class RecursiveHilbertValue * @param nodeIndex The number of the node being deleted. */ template - void DeletePoint(TreeType *node, const size_t localIndex); + void DeletePoint(TreeType* node, const size_t localIndex); /** * Update the largest Hilbert value of the node. @@ -170,14 +158,8 @@ class RecursiveHilbertValue * @param nodeIndex The number of the node being deleted. */ template - void RemoveNode(TreeType *node, const size_t nodeIndex); + void RemoveNode(TreeType* node, const size_t nodeIndex); - /** - * Copy the largest Hilbert value. - * @param dst The node to which the information is being copied. - * @param src The node from which the information is being copied. - */ - RecursiveHilbertValue operator = (const RecursiveHilbertValue &val); /** * Copy the largest Hilbert value. @@ -185,24 +167,32 @@ class RecursiveHilbertValue * @param src The node from which the information is being copied. */ template - void Copy(TreeType *dst, TreeType *src); + void Copy(TreeType* dst, TreeType* src); + + void NullifyData(); /** * Update the largest Hilbert value. * @param node The node in which the information should be updated. */ template - void UpdateLargestValue(TreeType *node); + void UpdateLargestValue(TreeType* node); + + template + void UpdateHilbertValues(TreeType* parent, size_t firstSibling, + size_t lastSibling); //! Return the largest Hilbert value - ptrdiff_t LargestValue() const { return largestValue; } + const arma::Col* LargestValue() const { return largestValue; } //! Modify the largest Hilbert value - ptrdiff_t& LargestValue() { return largestValue; } + arma::Col*& LargestValue() { return largestValue; } private: - //! The largest Hilbert value i.e. the number of the point in the dataset. - ptrdiff_t largestValue; + //! The point that has the largest Hilbert value. + arma::Col* largestValue; + bool ownsLargestValue; + bool hasLargestValue; /** * Compare two points. It returns 1 if the first point is greater than @@ -212,10 +202,10 @@ class RecursiveHilbertValue * @param pt2 The second point. * @param comp An object of CompareStruct. */ - template - static int ComparePoints(const arma::Col &pt1, - const arma::Col &pt2, - CompareStruct &comp); + template + static int ComparePoints(const VecType1& pt1, const VecType2& pt2, + CompareStruct& comp, typename boost::enable_if>* = 0, + typename boost::enable_if>* = 0); }; } // namespace tree diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp index e95daaa1df..49e00d7771 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp @@ -11,57 +11,120 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { +template +RecursiveHilbertValue::RecursiveHilbertValue() : + largestValue(NULL), + ownsLargestValue(false), + hasLargestValue(false) +{ -template -int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, - const arma::Col &pt2) +} + +template +template +RecursiveHilbertValue:: +RecursiveHilbertValue(const TreeType* tree) : + largestValue(NULL), + ownsLargestValue(false), + hasLargestValue(false) +{ + if(!tree->Parent()) // This is the root node + ownsLargestValue = true; + else if(tree->Parent()->Children()[0]->IsLeaf()) + { + // This is a leaf node + assert(tree->Parent()->NumChildren() > 0); + ownsLargestValue = true; + } + + if(ownsLargestValue) + { + largestValue = new arma::Col(tree->LocalDataset().n_rows); + } +} + +template +RecursiveHilbertValue:: +RecursiveHilbertValue(const RecursiveHilbertValue& other) : + largestValue(const_cast*>(other.LargestValue())), + ownsLargestValue(other.ownsLargestValue), + hasLargestValue(other.hasLargestValue) +{ + +} + +template +RecursiveHilbertValue::~RecursiveHilbertValue() +{ + if(ownsLargestValue) + delete largestValue; +} + +template +template +int RecursiveHilbertValue:: +ComparePoints(const VecType1& pt1, const VecType2& pt2, + typename boost::enable_if>*, + typename boost::enable_if>* ) { size_t dim = pt1.n_rows; - CompareStruct comp(dim); + CompareStruct comp(dim); - return ComparePoints(pt1,pt2,comp); + return ComparePoints(pt1, pt2, comp); }; -template -int RecursiveHilbertValue::CompareValues(TreeType *tree, - RecursiveHilbertValue &val1, RecursiveHilbertValue &val2) +template +int RecursiveHilbertValue:: +CompareValues(const RecursiveHilbertValue& val1, + const RecursiveHilbertValue& val2) { - typedef typename TreeType::ElemType ElemType; - size_t point1 = val1.LargestValue(); - size_t point2 = val2.LargestValue(); + if(!val1.hasLargestValue && val2.hasLargestValue) + return -1; + else if(val1.hasLargestValue && !val2.hasLargestValue) + return 1; + else if(!val1.hasLargestValue && !val2.hasLargestValue) + return 0; - return ComparePoints(arma::Col(tree->Dataset().col(point1)), - arma::Col(tree->Dataset().col(point2))); + return ComparePoints(*val1.LargestValue(), + *val2.LargestValue()); } -template -int RecursiveHilbertValue::CompareWith(TreeType *tree, - RecursiveHilbertValue &val) +template +int RecursiveHilbertValue:: +CompareWith(const RecursiveHilbertValue& val) const { - return CompareValues(tree,*this,val); + if(!hasLargestValue) + return -1; + return CompareValues(*this,val); } -template -int RecursiveHilbertValue::CompareWith(TreeType *tree, - const arma::Col &pt) +template +template +int RecursiveHilbertValue:: +CompareWith(const VecType& point, + typename boost::enable_if>* ) const { - return ComparePoints(arma::Col(tree->Dataset()->col(largestValue)),pt); + if(!hasLargestValue) + return -1; + return ComparePoints(*largestValue, point); } -template -int RecursiveHilbertValue::CompareWith(TreeType *tree, - const size_t point) +template +template +int RecursiveHilbertValue:: +CompareWithCachedPoint(const VecType& point, + typename boost::enable_if>* ) const { - typedef typename TreeType::ElemType ElemType; - return ComparePoints(arma::Col(tree->Dataset().col(largestValue)), - arma::Col(tree->Dataset().col(point))); + return CompareWith(point); } +template +template +int RecursiveHilbertValue:: +ComparePoints(const VecType1& pt1, const VecType2& pt2, + CompareStruct& comp, typename boost::enable_if>*, + typename boost::enable_if>* ) -template -int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, - const arma::Col &pt2, - CompareStruct &comp) { comp.center = comp.Hi * 0.5; comp.vec = comp.Lo * 0.5; @@ -141,103 +204,129 @@ int RecursiveHilbertValue::ComparePoints(const arma::Col &pt1, return ComparePoints(pt1,pt2,comp); } -template -size_t RecursiveHilbertValue::InsertPoint(TreeType *node, const size_t point) +template +template +size_t RecursiveHilbertValue:: +InsertPoint(TreeType* node, const VecType& point, + typename boost::enable_if>* ) { - typedef typename TreeType::ElemType ElemType; if(node->IsLeaf()) { size_t i; for(i = 0; i < node->NumPoints(); i++) - if(ComparePoints(arma::Col(node->LocalDataset().col(i)), - arma::Col(node->Dataset().col(point)))> 0) + if(ComparePoints(node->LocalDataset().col(i), point) > 0) break; if(i == node->NumPoints()) - largestValue = point; + *largestValue = point; + + hasLargestValue = true; + + // Propogate changes of the largest Hilbert value downward + TreeType *root = node->Parent(); + + while(root != NULL) + { + root->AuxiliaryInfo().HilbertValue().LargestValue() = largestValue; + root->AuxiliaryInfo().HilbertValue().hasLargestValue = true; + + root = root->Parent(); + } return i; } - else - { - if(largestValue < 0) - { - largestValue = point; - return 0; - } - if(ComparePoints(arma::Col(node->Dataset().col(point)), - arma::Col(node->Dataset().col(largestValue))) > 0) - largestValue = point; - } + return 0; } -template -void RecursiveHilbertValue::InsertNode(TreeType *node) -{ - typedef typename TreeType::ElemType ElemType; - size_t point = node->AuxiliaryInfo().LargestHilbertValue().LargestValue(); - if(ComparePoints(arma::Col(node->Dataset()->col(point)), - arma::Col(node->Dataset()->col(largestValue))) > 0) - largestValue = point; +template +template +void RecursiveHilbertValue::InsertNode(TreeType* node) +{ + if(CompareWith(node->AuxiliaryInfo().HilbertValue()) < 0) + { + largestValue = node->AuxiliaryInfo().HilbertValue().LargestValue(); + hasLargestValue = true; + } } +template template -void RecursiveHilbertValue::DeletePoint(TreeType *node, const size_t localIndex) +void RecursiveHilbertValue:: +DeletePoint(TreeType* node, const size_t localIndex) { if(node->NumPoints() <= 1) { - largestValue = -1; + hasLargestValue = false; return; } if(localIndex + 1 == node->NumPoints()) - largestValue = node->Points()[localIndex-1]; + *largestValue = node->LocalDataset()[localIndex-1]; } +template template -void RecursiveHilbertValue::RemoveNode(TreeType *node, const size_t nodeIndex) +void RecursiveHilbertValue:: +RemoveNode(TreeType* node, const size_t nodeIndex) { if(node->NumChildren() <= 1) { - largestValue = -1; + hasLargestValue = false; return; } if(nodeIndex + 1 == node->NumChildren()) - largestValue = node->Children()[nodeIndex-1]->AuxiliaryInfo.LargestHilbertValue().LargestValue(); + largestValue = node->Children()[nodeIndex-1]->AuxiliaryInfo.HilbertValue().LargestValue(); } -inline RecursiveHilbertValue RecursiveHilbertValue::operator = (const RecursiveHilbertValue &val) -{ - largestValue = val.LargestValue(); - - return *this; -} - +template template -void RecursiveHilbertValue::Copy(TreeType *dst, TreeType *src) +void RecursiveHilbertValue::Copy(TreeType* dst, TreeType* src) { dst->AuxiliaryInfo().LargestHilbertValue().LargestValue() = src->AuxiliaryInfo().LargestHilbertValue().LargestValue(); } -template -void RecursiveHilbertValue::UpdateLargestValue(TreeType *node) +template +void RecursiveHilbertValue::NullifyData() { - if(node->IsLeaf()) - { - largestValue = (node->NumPoints() > 0 ? - node->Points()[node->NumPoints() - 1] : -1); - } - else + ownsLargestValue = false; +} + +template +template +void RecursiveHilbertValue::UpdateLargestValue(TreeType* node) +{ + if(!node->IsLeaf()) { largestValue = (node->NumChildren() > 0 ? - node->Children()[node->NumChildren() - 1]->AuxiliaryInfo().LargestHilbertValue().LargestValue() : -1); + node->Children()[node->NumChildren() - 1]->AuxiliaryInfo().HilbertValue().LargestValue() : NULL); + hasLargestValue = (node->NumChildren() > 0 ? + node->Children()[node->NumChildren() - 1]->AuxiliaryInfo().HilbertValue().hasLargestValue : false); } } +template +template +void RecursiveHilbertValue:: +UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) +{ + for(size_t i = firstSibling; i<= lastSibling; i++) + { + RecursiveHilbertValue &value = + parent->Children()[i]->AuxiliaryInfo().HilbertValue(); + + assert(parent->Children()[i]->NumPoints() > 0); + + *value.LargestValue() = parent->Children()[i]->LocalDataset().col(parent->Children()[i]->NumPoints() - 1); + value.hasLargestValue = true; + } + +} + + } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp index 1ef4400a02..99cf319021 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp @@ -25,7 +25,7 @@ class XTreeAuxiliaryInformation * Construct this whith the specified node. * @param node The node that stores this auxiliary information. */ - XTreeAuxiliaryInformation(const TreeType *node) : + XTreeAuxiliaryInformation(const TreeType* node) : normalNodeMaxNumChildren(node->Parent() ? node->Parent()->AuxiliaryInfo().NormalNodeMaxNumChildren() : node->MaxNumChildren()), @@ -36,7 +36,7 @@ class XTreeAuxiliaryInformation * Create an auxiliary information object by copying from the other node. * @param other The node from which the information will be copied. */ - XTreeAuxiliaryInformation(const TreeType &other) : + XTreeAuxiliaryInformation(const TreeType& other) : normalNodeMaxNumChildren(other.AuxiliaryInfo().NormalNodeMaxNumChildren()), splitHistory(other.AuxiliaryInfo().SplitHistory()) { }; @@ -45,16 +45,20 @@ class XTreeAuxiliaryInformation * Some tree types require to save some properties at the insertion process. * This method should return false if it does not handle the process. */ - bool HandlePointInsertion(TreeType *, const size_t) + bool HandlePointInsertion(TreeType* , const size_t) { return false; } + bool HandlePointInsertion(TreeType* , const arma::vec&) + { + return false; + } /** * Some tree types require to save some properties at the insertion process. * This method should return false if it does not handle the process. */ - bool HandleNodeInsertion(TreeType *,TreeType *,bool) + bool HandleNodeInsertion(TreeType* , TreeType *,bool) { return false; } @@ -63,7 +67,7 @@ class XTreeAuxiliaryInformation * Some tree types require to save some properties at the deletion process. * This method should return false if it does not handle the process. */ - bool HandlePointDeletion(TreeType *,const size_t) + bool HandlePointDeletion(TreeType* , const size_t) { return false; } @@ -72,7 +76,7 @@ class XTreeAuxiliaryInformation * Some tree types require to save some properties at the deletion process. * This method should return false if it does not handle the process. */ - bool HandleNodeRemoval(TreeType *,const size_t) + bool HandleNodeRemoval(TreeType* , const size_t) { return false; } @@ -81,7 +85,7 @@ class XTreeAuxiliaryInformation * Some tree types require to propagate the information downward. * This method should return false if this is not the case. */ - bool UpdateAuxiliaryInfo(TreeType *) + bool UpdateAuxiliaryInfo(TreeType* ) { return false; } @@ -91,7 +95,7 @@ class XTreeAuxiliaryInformation * @param dst The node to which the information being copied. * @param src The node from which the information being copied. */ - void Copy(TreeType *dst,TreeType *src) + void Copy(TreeType* dst,TreeType* src) { dst->AuxiliaryInfo().NormalNodeMaxNumChildren() = src->AuxiliaryInfo().NormalNodeMaxNumChildren(); @@ -99,6 +103,9 @@ class XTreeAuxiliaryInformation dst->AuxiliaryInfo().SplitHistory() = src->AuxiliaryInfo().SplitHistory(); } + void NullifyData() + { } + /** * The X tree requires that the tree records it's "split history". To make * this easy, we use the following structure. 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 7bb875ccaa..b3ecc2f7f3 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 @@ -72,19 +72,39 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) } std::sort(sorted.begin(), sorted.end(), structComp); - std::vector pointIndices(p); + std::vector pointIndices(p); + arma::Mat localDataset(tree->Dataset().n_rows, p); for (size_t i = 0; i < p; i++) { // We start from the end of sorted. pointIndices[i] = tree->Points()[sorted[sorted.size() - 1 - i].n]; - root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], - relevels); + localDataset.col(i) = + tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n); + + if(tree->Points()[sorted[sorted.size() - 1 - i].n] < + tree->Dataset().n_cols) + root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], + relevels); + else + { + tree->Count()--; + tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n) = + tree->LocalDataset().col(tree->Count()); + tree->Points()[sorted[sorted.size() - 1 - i].n] = + tree->Points()[tree->Count()]; + // This function will ensure that minFill is satisfied. + tree->CondenseTree(localDataset.col(i), relevels, true); + + } } for (size_t i = 0; i < p; i++) { // We reverse the order again to reinsert the closest points first. - root->InsertPoint(pointIndices[p - 1 - i], relevels); + if(pointIndices[p - 1 - i] < tree->Dataset().n_cols) + root->InsertPoint(pointIndices[p - 1 - i], relevels); + else + root->InsertPoint(localDataset[p - 1 - i], relevels); } // // If we went below min fill, delete this node and reinsert all points. @@ -231,9 +251,19 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); + { + if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + treeOne->InsertPoint(tree->Points()[sorted[i].n]); + else + treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); + } else - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + { + if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + else + treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); + } } } else @@ -241,9 +271,19 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); + { + if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + treeOne->InsertPoint(tree->Points()[sorted[i].n]); + else + treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); + } else - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + { + if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + else + treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); + } } } diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index bf390bada4..80578400fa 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -705,30 +705,28 @@ void CheckHilbertOrdering(TreeType *tree) { for(size_t i = 0; i < tree->NumPoints() - 1; i++) BOOST_REQUIRE_LE( - tree->AuxiliaryInfo().LargestHilbertValue().ComparePoints( - arma::vec(tree->LocalDataset().col(i)), - arma::vec(tree->LocalDataset().col(i+1))), + tree->AuxiliaryInfo().HilbertValue().ComparePoints( + tree->LocalDataset().col(i), + tree->LocalDataset().col(i+1)), 0); BOOST_REQUIRE_EQUAL( - tree->AuxiliaryInfo().LargestHilbertValue().CompareWith( - tree, - tree->Points()[tree->NumPoints() - 1]), + tree->AuxiliaryInfo().HilbertValue().CompareWith( + tree->LocalDataset().col(tree->NumPoints() - 1)), 0); } else { for(size_t i = 0; i < tree->NumChildren() - 1; i++) BOOST_REQUIRE_LE( - tree->AuxiliaryInfo().LargestHilbertValue().CompareValues(tree, - tree->Children()[i]->AuxiliaryInfo().LargestHilbertValue(), - tree->Children()[i+1]->AuxiliaryInfo().LargestHilbertValue()), + tree->AuxiliaryInfo().HilbertValue().CompareValues( + tree->Children()[i]->AuxiliaryInfo().HilbertValue(), + tree->Children()[i+1]->AuxiliaryInfo().HilbertValue()), 0); BOOST_REQUIRE_EQUAL( - tree->AuxiliaryInfo().LargestHilbertValue().CompareWith( - tree, - tree->Children()[tree->NumChildren() - 1]->AuxiliaryInfo().LargestHilbertValue()), + tree->AuxiliaryInfo().HilbertValue().CompareWith( + tree->Children()[tree->NumChildren() - 1]->AuxiliaryInfo().HilbertValue()), 0); for(size_t i = 0; i < tree->NumChildren(); i++) From 7f8dbf50f99d41d67aec20ee17ce0dc54bfbb081 Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Sun, 5 Jun 2016 10:02:00 +0300 Subject: [PATCH 07/38] Fixed DiscreteHilbertOrderingTest. Added serialization. Minor style fixes. --- .../rectangle_tree/discrete_hilbert_value.hpp | 4 +++ .../discrete_hilbert_value_impl.hpp | 26 ++++++++++++++++--- ...bert_r_tree_auxiliary_information_impl.hpp | 3 +-- .../hilbert_r_tree_split_impl.hpp | 6 ++--- .../recursive_hilbert_value.hpp | 4 ++- .../recursive_hilbert_value_impl.hpp | 18 ++++++++++--- src/mlpack/tests/rectangle_tree_test.cpp | 2 +- 7 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp index 07c16ec7ce..6d85211a6d 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -212,6 +212,10 @@ class DiscreteHilbertValue * Returns true if the node has the largest Hilbert value. */ bool HasValue() const; + + public: + template + void Serialize(Archive& ar, const unsigned int /* version */); }; } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index 4d19d1305a..ef2b313e0a 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -270,17 +270,16 @@ InsertPoint(TreeType *node, const VecType& pt, localDataset->col(i) = *valueToInsert; numValues++; - // Propogate changes of the largest Hilbert value downward - TreeType *root = node->Parent(); + TreeType* root = node->Parent(); while(root != NULL) { - root->AuxiliaryInfo().HilbertValue().LocalDataset() = localDataset; - root->AuxiliaryInfo().HilbertValue().NumValues() = numValues; + root->AuxiliaryInfo().HilbertValue().UpdateLargestValue(root); root = root->Parent(); } + } return i; @@ -344,6 +343,11 @@ template template void DiscreteHilbertValue::Copy(TreeType* dst, TreeType* src) { + DiscreteHilbertValue &dstVal = dst->AuxiliaryInfo().HilbertValue(); + DiscreteHilbertValue &srcVal = src->AuxiliaryInfo().HilbertValue(); + + dst.LocalDataset() = src.LocalDataset(); + dst.NumValues() = src.NumValues(); } template @@ -420,6 +424,20 @@ bool DiscreteHilbertValue::HasValue() const return numValues > 0; } +template +template +void DiscreteHilbertValue:: +Serialize(Archive& ar, const unsigned int /* version */) +{ + using data::CreateNVP; + + ar & CreateNVP(localDataset, "localDataset"); + ar & CreateNVP(ownsLocalDataset, "ownsLocalDataset"); + ar & CreateNVP(numValues, "numValues"); + ar & CreateNVP(valueToInsert, "valueToInsert"); + ar & CreateNVP(ownsValueToInsert, "ownsValueToInsert"); +} + } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index 08549d0a7b..b9924d3064 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -188,8 +188,7 @@ UpdateAuxiliaryInfo(TreeType* node) return true; TreeType *child = node->Children()[node->NumChildren()-1]; - if(HilbertValueType::CompareValues(hilbertValue, - child->AuxiliaryInfo().hilbertValue()) < 0) + if(hilbertValue.CompareWith(child->AuxiliaryInfo().hilbertValue()) < 0) { hilbertValue.Copy(node,child); // hilbertValue = child->AuxiliaryInfo().hilbertValue(); diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index 5e066d3888..d185409a79 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -34,7 +34,7 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) return; } - TreeType *parent = tree->Parent(); + TreeType* parent = tree->Parent(); size_t iTree = 0; for(iTree = 0;parent->Children()[iTree] != tree; iTree++); @@ -97,7 +97,7 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) return true; } - TreeType *parent = tree->Parent(); + TreeType* parent = tree->Parent(); size_t iTree = 0; for(iTree = 0;parent->Children()[iTree] != tree; iTree++); @@ -318,7 +318,7 @@ RedistributePointsEvenly(TreeType *parent, // Fix the largest Hilbert values of the siblings. parent->AuxiliaryInfo().HilbertValue().UpdateHilbertValues(parent, firstSibling, lastSibling); - TreeType *root = parent; + TreeType* root = parent; while(root != NULL) { diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp index b46ab37568..e8da3e1988 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp @@ -206,7 +206,9 @@ class RecursiveHilbertValue static int ComparePoints(const VecType1& pt1, const VecType2& pt2, CompareStruct& comp, typename boost::enable_if>* = 0, typename boost::enable_if>* = 0); - + public: + template + void Serialize(Archive& ar, const unsigned int /* version */); }; } // namespace tree } // namespace mlpack diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp index 49e00d7771..4c4665cab6 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp @@ -223,7 +223,7 @@ InsertPoint(TreeType* node, const VecType& point, hasLargestValue = true; // Propogate changes of the largest Hilbert value downward - TreeType *root = node->Parent(); + TreeType* root = node->Parent(); while(root != NULL) { @@ -285,8 +285,10 @@ template template void RecursiveHilbertValue::Copy(TreeType* dst, TreeType* src) { - dst->AuxiliaryInfo().LargestHilbertValue().LargestValue() = - src->AuxiliaryInfo().LargestHilbertValue().LargestValue(); + dst->AuxiliaryInfo().HilbertValue().LargestValue() = + src->AuxiliaryInfo().HilbertValue().LargestValue(); + dst->AuxiliaryInfo().HilbertValue().hasLargestValue = + src->AuxiliaryInfo().HilbertValue().hasLargestValue; } template @@ -326,7 +328,17 @@ UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) } +template +template +void RecursiveHilbertValue:: +Serialize(Archive& ar, const unsigned int /* version */) +{ + using data::CreateNVP; + ar & CreateNVP(largestValue, "largestValue"); + ar & CreateNVP(ownsLargestValue, "ownsLargestValue"); + ar & CreateNVP(hasLargestValue, "hasLargestValue"); +} } // namespace tree } // namespace mlpack diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index 80578400fa..d4a23eca2d 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -699,7 +699,7 @@ BOOST_AUTO_TEST_CASE(RecursiveHilbertRTreeTraverserTest) } template -void CheckHilbertOrdering(TreeType *tree) +void CheckHilbertOrdering(TreeType* tree) { if(tree->IsLeaf()) { From 1ba13245d63289e1a0b509affaa1c7830839134a Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Mon, 13 Jun 2016 23:43:54 +0300 Subject: [PATCH 08/38] Style fixes. --- .../discrete_hilbert_value_impl.hpp | 88 +++++++++---------- ...bert_r_tree_auxiliary_information_impl.hpp | 34 +++---- .../hilbert_r_tree_descent_heuristic_impl.hpp | 12 +-- .../hilbert_r_tree_split_impl.hpp | 56 ++++++------ .../rectangle_tree/r_star_tree_split_impl.hpp | 14 +-- .../rectangle_tree/rectangle_tree_impl.hpp | 40 ++++----- .../recursive_hilbert_value.hpp | 2 +- .../recursive_hilbert_value_impl.hpp | 72 +++++++-------- .../tree/rectangle_tree/x_tree_split_impl.hpp | 12 +-- src/mlpack/tests/rectangle_tree_test.cpp | 50 +++++++++++ 10 files changed, 215 insertions(+), 165 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index ef2b313e0a..624fe59de1 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -27,9 +27,9 @@ DiscreteHilbertValue::DiscreteHilbertValue() : template DiscreteHilbertValue::~DiscreteHilbertValue() { - if(ownsLocalDataset) + if (ownsLocalDataset) delete localDataset; - if(ownsValueToInsert) + if (ownsValueToInsert) delete valueToInsert; } @@ -45,16 +45,16 @@ DiscreteHilbertValue::DiscreteHilbertValue(const TreeType* tree) : ownsValueToInsert(tree->Parent() ? false : true) { // Calculate the Hilbert value for all points - if(!tree->Parent()) // This is the root node + if (!tree->Parent()) // This is the root node ownsLocalDataset = true; - else if(tree->Parent()->Children()[0]->IsLeaf()) + else if (tree->Parent()->Children()[0]->IsLeaf()) { // This is a leaf node assert(tree->Parent()->NumChildren() > 0); ownsLocalDataset = true; } - if(ownsLocalDataset) + if (ownsLocalDataset) { localDataset = new arma::Mat(tree->LocalDataset().n_rows, tree->MaxLeafSize() + 1); @@ -89,16 +89,16 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) // The number of bits for the mantissa const int numMantBits = order - numExpBits - 1; - for(size_t i = 0; i < pt.n_rows; i++) + for (size_t i = 0; i < pt.n_rows; i++) { int e; VecElemType normalizedVal = std::frexp(pt(i),&e); bool sgn = std::signbit(normalizedVal); - if(sgn) + if (sgn) normalizedVal = -normalizedVal; - if(e < std::numeric_limits::min_exponent) + if (e < std::numeric_limits::min_exponent) { HilbertElemType tmp = 1 << (std::numeric_limits::min_exponent - e); e = std::numeric_limits::min_exponent; @@ -111,7 +111,7 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) res(i) |= ((HilbertElemType)(e - std::numeric_limits::min_exponent)) << numMantBits; // Negative values should be inverted - if(sgn) + if (sgn) res(i) = ((HilbertElemType)1 << (order - 1)) - 1 - res(i); else res(i) |= (HilbertElemType)1 << (order - 1); @@ -121,13 +121,13 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) // Since the Hilbert curve is continuous we should permutate and intend // coordinate axes depending on the position of the point - for(HilbertElemType Q = M; Q > 1; Q >>= 1) + for (HilbertElemType Q = M; Q > 1; Q >>= 1) { HilbertElemType P = Q - 1; - for(size_t i = 0; i < pt.n_rows; i++) + for (size_t i = 0; i < pt.n_rows; i++) { - if(res(i) & Q) // Invert + if (res(i) & Q) // Invert res(0) ^= P; else // Permutate { @@ -139,24 +139,24 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) } // Gray encode - for(size_t i = 1; i < pt.n_rows; i++) + for (size_t i = 1; i < pt.n_rows; i++) res(i) ^= res(i-1); HilbertElemType t = 0; // Some coordinate axes should be inverted - for(HilbertElemType Q = M; Q > 1; Q >>= 1) - if( res(pt.n_rows - 1) & Q) + for (HilbertElemType Q = M; Q > 1; Q >>= 1) + if ( res(pt.n_rows - 1) & Q) t ^= Q - 1; - for(size_t i = 0; i < pt.n_rows; i++) + for (size_t i = 0; i < pt.n_rows; i++) res(i) ^= t; // We should rearrange bits in order to compare two Hilbert values faster arma::Col rearrangedResult(pt.n_rows,arma::fill::zeros); - for(size_t i = 0; i < order; i++) - for(size_t j = 0; j < pt.n_rows; j++) + for (size_t i = 0; i < order; i++) + for (size_t j = 0; j < pt.n_rows; j++) { size_t bit = (i * pt.n_rows + j) % order; size_t row = (i * pt.n_rows + j) / order; @@ -172,11 +172,11 @@ int DiscreteHilbertValue:: CompareValues(const arma::Col& value1, const arma::Col& value2) { - for(size_t i = 0;i < value1.n_rows; i++) + for (size_t i = 0;i < value1.n_rows; i++) { - if(value1(i) > value2(i)) + if (value1(i) > value2(i)) return 1; - else if(value1(i) < value2(i)) + else if (value1(i) < value2(i)) return -1; } @@ -195,7 +195,7 @@ ComparePoints(const VecType1& pt1, const VecType2& pt2, arma::Col val1 = CalculateValue(pt1); arma::Col val2 = CalculateValue(pt2); - return CompareValues(val1,val2); + return CompareValues(val1, val2); } template @@ -203,11 +203,11 @@ int DiscreteHilbertValue:: CompareValues(const DiscreteHilbertValue& val1, const DiscreteHilbertValue& val2) { - if(val1.HasValue() && !val2.HasValue()) + if (val1.HasValue() && !val2.HasValue()) return 1; - else if(!val1.HasValue() && val2.HasValue()) + else if (!val1.HasValue() && val2.HasValue()) return -1; - else if(!val1.HasValue() && !val2.HasValue()) + else if (!val1.HasValue() && !val2.HasValue()) return 0; return CompareValues(val1.LocalDataset()->col(val1.NumValues() - 1), @@ -229,7 +229,7 @@ CompareWith(const VecType& pt, { arma::Col val = CalculateValue(pt); - if(!HasValue()) + if (!HasValue()) return -1; return CompareValues(localDataset->col(numValues - 1),val); @@ -241,7 +241,7 @@ int DiscreteHilbertValue:: CompareWithCachedPoint(const VecType& , typename boost::enable_if>*) const { - if(!HasValue()) + if (!HasValue()) return -1; return CompareValues(localDataset->col(numValues - 1),*valueToInsert); @@ -256,16 +256,16 @@ InsertPoint(TreeType *node, const VecType& pt, size_t i = 0; // All point are inserted to the root node - if(!node->Parent()) + if (!node->Parent()) *valueToInsert = CalculateValue(pt); - if(node->IsLeaf()) + if (node->IsLeaf()) { // Find an appropriate place - for(i = 0; i < numValues; i++) - if(CompareValues(localDataset->col(i), *valueToInsert) > 0) + for (i = 0; i < numValues; i++) + if (CompareValues(localDataset->col(i), *valueToInsert) > 0) break; - for(size_t j = numValues; j > i; j--) + for (size_t j = numValues; j > i; j--) localDataset->col(j) = localDataset->col(j-1); localDataset->col(i) = *valueToInsert; @@ -273,7 +273,7 @@ InsertPoint(TreeType *node, const VecType& pt, // Propogate changes of the largest Hilbert value downward TreeType* root = node->Parent(); - while(root != NULL) + while (root != NULL) { root->AuxiliaryInfo().HilbertValue().UpdateLargestValue(root); @@ -291,7 +291,7 @@ void DiscreteHilbertValue::InsertNode(TreeType* node) { DiscreteHilbertValue &val = node->AuxiliaryInfo().HilbertValue(); - if(CompareWith(node,val) < 0) + if (CompareWith(node,val) < 0) { localDataset = val.LocalDataset(); numValues = val.NumValues(); @@ -305,7 +305,7 @@ DeletePoint(TreeType* node, const size_t localIndex) { // Delete the Hilbert value from the local dataset - for(size_t i = numValues - 1; i > localIndex; i--) + for (size_t i = numValues - 1; i > localIndex; i--) localDataset->col(i-1) = localDataset->col(i); numValues--; @@ -316,17 +316,17 @@ template void DiscreteHilbertValue:: RemoveNode(TreeType* node, const size_t nodeIndex) { - if(node->NumChildren() <= 1) + if (node->NumChildren() <= 1) { localDataset = NULL; numValues = 0; return; } - if(nodeIndex + 1 == node->NumChildren()) + if (nodeIndex + 1 == node->NumChildren()) { // Update the largest Hilbert value if the value exists TreeType* child = node->Children()[nodeIndex-1]; - if(child->AuxiliaryInfo.HilbertValue().NumValues() != 0) + if (child->AuxiliaryInfo.HilbertValue().NumValues() != 0) { numValues = child->AuxiliaryInfo.HilbertValue().NumValues(); localDataset = child->AuxiliaryInfo.HilbertValue().LocalDataset(); @@ -360,7 +360,7 @@ template template void DiscreteHilbertValue::UpdateLargestValue(TreeType* node) { - if(!node->IsLeaf()) + if (!node->IsLeaf()) { // Update the largest Hilbert value localDataset = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().HilbertValue().LocalDataset(); @@ -377,19 +377,19 @@ UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) size_t numPoints = 0; - for(size_t i = firstSibling; i<= lastSibling; i++) + for (size_t i = firstSibling; i<= lastSibling; i++) numPoints += parent->Children()[i]->NumPoints(); // Copy the local datasets arma::Mat tmp(localDataset->n_rows,numPoints); size_t iPoint = 0; - for(size_t i = firstSibling; i<= lastSibling; i++) + for (size_t i = firstSibling; i<= lastSibling; i++) { DiscreteHilbertValue &value = parent->Children()[i]->AuxiliaryInfo().HilbertValue(); - for(size_t j = 0; j < value.NumValues(); j++) + for (size_t j = 0; j < value.NumValues(); j++) { tmp.col(iPoint) = value.LocalDataset()->col(j); iPoint++; @@ -400,12 +400,12 @@ UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) iPoint = 0; // Redistribute the Hilbert values - for(size_t i = firstSibling; i<= lastSibling; i++) + for (size_t i = firstSibling; i<= lastSibling; i++) { DiscreteHilbertValue &value = parent->Children()[i]->AuxiliaryInfo().HilbertValue(); - for(size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) + for (size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) { value.LocalDataset()->col(j) = tmp.col(iPoint); iPoint++; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index b9924d3064..b930d7e315 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -54,14 +54,14 @@ template:: HandlePointInsertion(TreeType* node, const size_t point) { - if(node->IsLeaf()) + if (node->IsLeaf()) { // Get the position at which the point should be inserted // Update the largest Hilbert value of the node size_t pos = hilbertValue.InsertPoint(node, node->Dataset().col(point)); // Move points - for(size_t i = node->NumPoints(); i > pos; i--) + for (size_t i = node->NumPoints(); i > pos; i--) { node->Points()[i] = node->Points()[i-1]; node->LocalDataset().col(i) = node->LocalDataset().col(i-1); @@ -84,17 +84,17 @@ template class HilbertValueType> template bool HilbertRTreeAuxiliaryInformation:: -HandlePointInsertion(TreeType* node, const VecType& point, +HandlePointInsertion (TreeType* node, const VecType& point, typename boost::enable_if>*) { - if(node->IsLeaf()) + if (node->IsLeaf()) { // Get the position at which the point should be inserted // Update the largest Hilbert value of the node size_t pos = hilbertValue.InsertPoint(node, point); // Move points - for(size_t i = node->NumPoints(); i > pos; i--) + for (size_t i = node->NumPoints(); i > pos; i--) { node->Points()[i] = node->Points()[i-1]; node->LocalDataset().col(i) = node->LocalDataset().col(i-1); @@ -116,22 +116,22 @@ HandlePointInsertion(TreeType* node, const VecType& point, template class HilbertValueType> bool HilbertRTreeAuxiliaryInformation:: -HandleNodeInsertion(TreeType* node,TreeType* nodeToInsert,bool insertionLevel) +HandleNodeInsertion(TreeType* node, TreeType* nodeToInsert, bool insertionLevel) { - if(insertionLevel) + if (insertionLevel) { size_t pos; // Find the best position for the node being inserted. // The node should be inserted according to its Hilbert value. - for(pos = 0; pos < node->NumChildren(); pos++) - if(HilbertValueType::CompareValues( + for (pos = 0; pos < node->NumChildren(); pos++) + if (HilbertValueType::CompareValues( node->Children()[pos]->AuxiliaryInfo().HilbertValue(), nodeToInsert->AuxiliaryInfo().HilbertValue()) < 0) break; // Move nodes - for(size_t i = node->NumChildren(); i > pos; i--) + for (size_t i = node->NumChildren(); i > pos; i--) node->Children()[i] = node->Children()[i-1]; // Insert the node @@ -150,12 +150,12 @@ HandleNodeInsertion(TreeType* node,TreeType* nodeToInsert,bool insertionLevel) template class HilbertValueType> bool HilbertRTreeAuxiliaryInformation:: -HandlePointDeletion(TreeType* node,const size_t localIndex) +HandlePointDeletion(TreeType* node, const size_t localIndex) { // Update the largest Hilbert value hilbertValue.DeletePoint(node,localIndex); - for(size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) + for (size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) { node->Points()[i-1] = node->Points()[i]; node->LocalDataset()->col(i-1) = node->LocalDataset()->col(i); @@ -167,12 +167,12 @@ HandlePointDeletion(TreeType* node,const size_t localIndex) template class HilbertValueType> bool HilbertRTreeAuxiliaryInformation:: -HandleNodeRemoval(TreeType* node,const size_t nodeIndex) +HandleNodeRemoval(TreeType* node, const size_t nodeIndex) { // Update the largest Hilbert value hilbertValue.RemoveNode(node,nodeIndex); - for(size_t i = nodeIndex + 1; nodeIndex < node->NumChildren(); i++) + for (size_t i = nodeIndex + 1; nodeIndex < node->NumChildren(); i++) node->Children()[i-1] = node->Children()[i]; node->NumChildren()--; @@ -184,11 +184,11 @@ template:: UpdateAuxiliaryInfo(TreeType* node) { - if(node->IsLeaf()) // Should already be updated + if (node->IsLeaf()) // Should already be updated return true; TreeType *child = node->Children()[node->NumChildren()-1]; - if(hilbertValue.CompareWith(child->AuxiliaryInfo().hilbertValue()) < 0) + if (hilbertValue.CompareWith(child->AuxiliaryInfo().hilbertValue()) < 0) { hilbertValue.Copy(node,child); // hilbertValue = child->AuxiliaryInfo().hilbertValue(); @@ -200,7 +200,7 @@ UpdateAuxiliaryInfo(TreeType* node) template class HilbertValueType> void HilbertRTreeAuxiliaryInformation:: -Copy(TreeType* dst,TreeType* src) +Copy(TreeType* dst, TreeType* src) { hilbertValue.Copy(dst,src); } diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp index 83474a1fa5..48f8c4c466 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp @@ -19,8 +19,8 @@ ChooseDescentNode(const TreeType* node, const size_t point) { size_t bestIndex = 0; - for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if(node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWithCachedPoint(node->Dataset().col(point)) > 0) + for (bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) + if (node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWithCachedPoint(node->Dataset().col(point)) > 0) break; return bestIndex; @@ -32,8 +32,8 @@ ChooseDescentNode(const TreeType* node, const arma::vec& point) { size_t bestIndex = 0; - for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if(node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWithCachedPoint(point) > 0) + for (bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) + if (node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWithCachedPoint(point) > 0) break; return bestIndex; @@ -45,8 +45,8 @@ ChooseDescentNode(const TreeType* node, const TreeType* insertedNode) { size_t bestIndex = 0; - for(bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if(node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWith(node,node->AuxiliaryInfo().HilbertValue()) > 0) + for (bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) + if (node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWith(node,node->AuxiliaryInfo().HilbertValue()) > 0) break; return bestIndex; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index d185409a79..d3d7629e88 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -36,12 +36,12 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) TreeType* parent = tree->Parent(); size_t iTree = 0; - for(iTree = 0;parent->Children()[iTree] != tree; iTree++); + for (iTree = 0;parent->Children()[iTree] != tree; iTree++); // Try to find splitOrder cooperating siblings in order to redistribute // points among them and avoid split. size_t firstSibling,lastSibling; - if(FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling)) + if (FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling)) { RedistributePointsEvenly(parent, firstSibling, lastSibling); return; @@ -53,7 +53,7 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); - for(size_t i = parent->NumChildren(); i > iNewSibling ; i--) + for (size_t i = parent->NumChildren(); i > iNewSibling ; i--) parent->Children()[i] = parent->Children()[i-1]; parent->NumChildren()++; @@ -71,7 +71,7 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) // Redistribute the points among (splitOrder+1) cooperating siblings evenly. RedistributePointsEvenly(parent, firstSibling, lastSibling); - if(parent->NumChildren() == parent->MaxNumChildren() + 1) + if (parent->NumChildren() == parent->MaxNumChildren() + 1) HilbertRTreeSplit::SplitNonLeafNode(parent, relevels); } @@ -100,12 +100,12 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) TreeType* parent = tree->Parent(); size_t iTree = 0; - for(iTree = 0;parent->Children()[iTree] != tree; iTree++); + for (iTree = 0;parent->Children()[iTree] != tree; iTree++); // Try to find splitOrder cooperating siblings in order to redistribute // children among them and avoid split. size_t firstSibling,lastSibling; - if(FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling)) + if (FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling)) { RedistributeNodesEvenly(parent, firstSibling, lastSibling); return false; @@ -117,7 +117,7 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); - for(size_t i = parent->NumChildren(); i > iNewSibling ; i--) + for (size_t i = parent->NumChildren(); i > iNewSibling ; i--) parent->Children()[i] = parent->Children()[i-1]; parent->NumChildren()++; @@ -136,7 +136,7 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) // Redistribute children among (splitOrder+1) cooperating siblings evenly. RedistributeNodesEvenly(parent, firstSibling, lastSibling); - if(parent->NumChildren() == parent->MaxNumChildren() + 1) + if (parent->NumChildren() == parent->MaxNumChildren() + 1) HilbertRTreeSplit::SplitNonLeafNode(parent, relevels); return false; } @@ -152,25 +152,25 @@ bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent, size_t iTree, size_t iUnderfullSibling; // Try to find empty space among cooperating siblings. - if(parent->Children()[iTree]->NumChildren() != 0) + if (parent->Children()[iTree]->NumChildren() != 0) { - for(iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) - if(parent->Children()[iUnderfullSibling]->NumChildren() < + for (iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) + if (parent->Children()[iUnderfullSibling]->NumChildren() < parent->Children()[iUnderfullSibling]->MaxNumChildren() - 1) break; } else { - for(iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) - if(parent->Children()[iUnderfullSibling]->NumPoints() < + for (iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) + if (parent->Children()[iUnderfullSibling]->NumPoints() < parent->Children()[iUnderfullSibling]->MaxLeafSize() - 1) break; } - if(iUnderfullSibling == end) // All nodes are full. + if (iUnderfullSibling == end) // All nodes are full. return false; - if(iUnderfullSibling > iTree) + if (iUnderfullSibling > iTree) { lastSibling = (iTree + splitOrder-1 < parent->NumChildren() ? iTree + splitOrder-1 : parent->NumChildren() - 1); @@ -200,7 +200,7 @@ RedistributeNodesEvenly(const TreeType *parent, size_t numChildren = 0; size_t numChildrenPerNode,numRestChildren; - for(size_t i = firstSibling; i <= lastSibling; i++) + for (size_t i = firstSibling; i <= lastSibling; i++) numChildren += parent->Children()[i]->NumChildren(); numChildrenPerNode = numChildren / (lastSibling - firstSibling + 1); @@ -210,9 +210,9 @@ RedistributeNodesEvenly(const TreeType *parent, // Copy children's children in order to redistribute them. size_t iChild = 0; - for(size_t i = firstSibling; i <= lastSibling; i++) + for (size_t i = firstSibling; i <= lastSibling; i++) { - for(size_t j = 0; j < parent->Children()[i]->NumChildren(); j++) + for (size_t j = 0; j < parent->Children()[i]->NumChildren(); j++) { children[iChild] = parent->Children()[i]->Children()[j]; iChild++; @@ -220,20 +220,20 @@ RedistributeNodesEvenly(const TreeType *parent, } iChild = 0; - for(size_t i = firstSibling; i <= lastSibling; i++) + for (size_t i = firstSibling; i <= lastSibling; i++) { // Since we redistribute children of a sibling we should // recalculate the bound. parent->Children()[i]->Bound().Clear(); - for(size_t j = 0; j < numChildrenPerNode; j++) + for (size_t j = 0; j < numChildrenPerNode; j++) { parent->Children()[i]->Bound() |= children[iChild]->Bound(); parent->Children()[i]->Children()[j] = children[iChild]; children[iChild]->Parent() = parent->Children()[i]; iChild++; } - if(numRestChildren > 0) + if (numRestChildren > 0) { parent->Children()[i]->Bound() |= children[iChild]->Bound(); parent->Children()[i]->Children()[numChildrenPerNode] = children[iChild]; @@ -262,7 +262,7 @@ RedistributePointsEvenly(TreeType *parent, size_t numPoints = 0; size_t numPointsPerNode,numRestPoints; - for(size_t i = firstSibling; i <= lastSibling; i++) + for (size_t i = firstSibling; i <= lastSibling; i++) numPoints += parent->Children()[i]->NumPoints(); numPointsPerNode = numPoints / (lastSibling - firstSibling + 1); @@ -274,9 +274,9 @@ RedistributePointsEvenly(TreeType *parent, // Copy children's points in order to redistribute them. size_t iPoint = 0; - for(size_t i = firstSibling; i <= lastSibling; i++) + for (size_t i = firstSibling; i <= lastSibling; i++) { - for(size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) + for (size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) { points[iPoint] = parent->Children()[i]->Points()[j]; tmp.col(iPoint) = parent->Children()[i]->LocalDataset().col(j); @@ -285,21 +285,21 @@ RedistributePointsEvenly(TreeType *parent, } iPoint = 0; - for(size_t i = firstSibling; i <= lastSibling; i++) + for (size_t i = firstSibling; i <= lastSibling; i++) { // Since we redistribute points of a sibling we should // recalculate the bound. parent->Children()[i]->Bound().Clear(); size_t j; - for(j = 0; j < numPointsPerNode; j++) + for (j = 0; j < numPointsPerNode; j++) { parent->Children()[i]->Bound() |= tmp.col(iPoint); parent->Children()[i]->Points()[j] = points[iPoint]; parent->Children()[i]->LocalDataset().col(j) = tmp.col(iPoint); iPoint++; } - if(numRestPoints > 0) + if (numRestPoints > 0) { parent->Children()[i]->Bound() |= tmp.col(iPoint); parent->Children()[i]->Points()[j] = points[iPoint]; @@ -320,7 +320,7 @@ RedistributePointsEvenly(TreeType *parent, TreeType* root = parent; - while(root != NULL) + while (root != NULL) { root->AuxiliaryInfo().HilbertValue().UpdateLargestValue(root); root = root->Parent(); 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 2a5e19be16..aa3171ae9e 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 @@ -53,7 +53,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) // We sort the points by decreasing distance to the centroid of the bound. // We then remove the first p entries and reinsert them at the root. TreeType* root = tree; - while(root->Parent() != NULL) + while (root->Parent() != NULL) root = root->Parent(); size_t p = tree->MaxLeafSize() * 0.3; // The paper says this works the best. if (p == 0) @@ -82,7 +82,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) localDataset.col(i) = tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n); - if(tree->Points()[sorted[sorted.size() - 1 - i].n] < + if (tree->Points()[sorted[sorted.size() - 1 - i].n] < tree->Dataset().n_cols) root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], relevels); @@ -102,7 +102,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < p; i++) { // We reverse the order again to reinsert the closest points first. - if(pointIndices[p - 1 - i] < tree->Dataset().n_cols) + if (pointIndices[p - 1 - i] < tree->Dataset().n_cols) root->InsertPoint(pointIndices[p - 1 - i], relevels); else root->InsertPoint(localDataset[p - 1 - i], relevels); @@ -243,14 +243,14 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) { - if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) treeOne->InsertPoint(tree->Points()[sorted[i].n]); else treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); } else { - if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) treeTwo->InsertPoint(tree->Points()[sorted[i].n]); else treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); @@ -263,14 +263,14 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) { - if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) treeOne->InsertPoint(tree->Points()[sorted[i].n]); else treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); } else { - if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) treeTwo->InsertPoint(tree->Points()[sorted[i].n]); else treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); 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 f76fc6c721..a0a5f88093 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -296,7 +296,7 @@ void RectangleTreecol(count) = dataset->col(point); points[count++] = point; @@ -307,7 +307,7 @@ void RectangleTreeInsertPoint(point, lvls); } @@ -334,7 +334,7 @@ void RectangleTreecol(count) = dataset->col(point); points[count++] = point; @@ -345,7 +345,7 @@ void RectangleTreeInsertPoint(point, relevels); } @@ -374,7 +374,7 @@ void RectangleTreecol(count) = point; points[count++] = dataset->n_cols; @@ -385,7 +385,7 @@ void RectangleTreeInsertPoint(point, lvls); } @@ -412,7 +412,7 @@ void RectangleTreecol(count) = point; points[count++] = dataset->n_cols; @@ -423,8 +423,8 @@ void RectangleTreeInsertPoint(point, relevels); } @@ -452,7 +452,7 @@ void RectangleTreeBound(); if (level == TreeDepth()) { - if(!auxiliaryInfo.HandleNodeInsertion(this,node,true)) + if (!auxiliaryInfo.HandleNodeInsertion(this, node, true)) { children[numChildren++] = node; node->Parent() = this; @@ -461,7 +461,7 @@ void RectangleTreeInsertNode(node, level, relevels); } @@ -497,7 +497,7 @@ bool RectangleTreecol(i) = localDataset->col(--count); // Decrement count. points[i] = points[count]; @@ -537,7 +537,7 @@ bool RectangleTreecol(i) = localDataset->col(--count); points[i] = points[count]; @@ -587,7 +587,7 @@ bool RectangleTreecol(i) = localDataset->col(--count); // Decrement count. points[i] = points[count]; @@ -627,7 +627,7 @@ bool RectangleTreecol(i) = localDataset->col(--count); points[i] = points[count]; @@ -665,7 +665,7 @@ bool RectangleTreeChildren()[i] == this) { // Decrement numChildren. - if(!auxiliaryInfo.HandleNodeRemoval(parent,i)) + if (!auxiliaryInfo.HandleNodeRemoval(parent, i)) { parent->Children()[i] = parent->Children()[--parent->NumChildren()]; } @@ -990,7 +990,7 @@ void RectangleTreen_cols) + if (points[j] < dataset->n_cols) root->InsertPoint(points[j], relevels); else root->InsertPoint(localDataset[j], relevels); @@ -1016,7 +1016,7 @@ void RectangleTreeChildren()[j] == this) { // Decrement numChildren. - if(!auxiliaryInfo.HandleNodeRemoval(parent,j)) + if (!auxiliaryInfo.HandleNodeRemoval(parent,j)) { parent->Children()[j] = parent->Children()[--parent->NumChildren()]; } @@ -1064,7 +1064,7 @@ void RectangleTreeNumChildren() > maxNumChildren) + if (child->NumChildren() > maxNumChildren) { maxNumChildren = child->MaxNumChildren(); children.resize(maxNumChildren+1); diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp index e8da3e1988..09f0403cb3 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp @@ -70,7 +70,7 @@ class RecursiveHilbertValue invertResult(false), recursionLevel(0) { - for(size_t i = 0; i < dim; i++) + for (size_t i = 0; i < dim; i++) { Lo[i] = std::numeric_limits::lowest(); Hi[i] = std::numeric_limits::max(); diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp index 4c4665cab6..955873b893 100644 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp @@ -28,16 +28,16 @@ RecursiveHilbertValue(const TreeType* tree) : ownsLargestValue(false), hasLargestValue(false) { - if(!tree->Parent()) // This is the root node + if (!tree->Parent()) // This is the root node ownsLargestValue = true; - else if(tree->Parent()->Children()[0]->IsLeaf()) + else if (tree->Parent()->Children()[0]->IsLeaf()) { // This is a leaf node assert(tree->Parent()->NumChildren() > 0); ownsLargestValue = true; } - if(ownsLargestValue) + if (ownsLargestValue) { largestValue = new arma::Col(tree->LocalDataset().n_rows); } @@ -56,7 +56,7 @@ RecursiveHilbertValue(const RecursiveHilbertValue& other) : template RecursiveHilbertValue::~RecursiveHilbertValue() { - if(ownsLargestValue) + if (ownsLargestValue) delete largestValue; } @@ -78,11 +78,11 @@ int RecursiveHilbertValue:: CompareValues(const RecursiveHilbertValue& val1, const RecursiveHilbertValue& val2) { - if(!val1.hasLargestValue && val2.hasLargestValue) + if (!val1.hasLargestValue && val2.hasLargestValue) return -1; - else if(val1.hasLargestValue && !val2.hasLargestValue) + else if (val1.hasLargestValue && !val2.hasLargestValue) return 1; - else if(!val1.hasLargestValue && !val2.hasLargestValue) + else if (!val1.hasLargestValue && !val2.hasLargestValue) return 0; return ComparePoints(*val1.LargestValue(), @@ -93,9 +93,9 @@ template int RecursiveHilbertValue:: CompareWith(const RecursiveHilbertValue& val) const { - if(!hasLargestValue) + if (!hasLargestValue) return -1; - return CompareValues(*this,val); + return CompareValues(*this, val); } template @@ -104,7 +104,7 @@ int RecursiveHilbertValue:: CompareWith(const VecType& point, typename boost::enable_if>* ) const { - if(!hasLargestValue) + if (!hasLargestValue) return -1; return ComparePoints(*largestValue, point); } @@ -132,7 +132,7 @@ ComparePoints(const VecType1& pt1, const VecType2& pt2, comp.center += comp.vec; // Get bits in order to use the Gray code - for(size_t i = 0; i < pt1.n_rows; i++) + for (size_t i = 0; i < pt1.n_rows; i++) { size_t j = comp.permutation[i]; comp.bits[i] = (pt1(j) > comp.center(j) && !comp.inversion[j]) || @@ -143,44 +143,44 @@ ComparePoints(const VecType1& pt1, const VecType2& pt2, } // Gray encode - for(size_t i = 1; i < pt1.n_rows; i++) + for (size_t i = 1; i < pt1.n_rows; i++) { comp.bits[i] ^= comp.bits[i-1]; comp.bits2[i] ^= comp.bits2[i-1]; } - if(comp.invertResult) + if (comp.invertResult) { - for(size_t i = 0; i < pt1.n_rows; i++) + for (size_t i = 0; i < pt1.n_rows; i++) { comp.bits[i] = !comp.bits[i]; comp.bits2[i] = !comp.bits2[i]; } } - for(size_t i = 0; i < pt1.n_rows; i++) + for (size_t i = 0; i < pt1.n_rows; i++) { - if(comp.bits[i] < comp.bits2[i]) + if (comp.bits[i] < comp.bits2[i]) return -1; - if(comp.bits[i] > comp.bits2[i]) + if (comp.bits[i] > comp.bits2[i]) return 1; } - if(comp.recursionLevel >= recursionDepth) + if (comp.recursionLevel >= recursionDepth) return 0; comp.recursionLevel++; - if(comp.bits[pt1.n_rows-1]) + if (comp.bits[pt1.n_rows-1]) comp.invertResult = !comp.invertResult; // Since the Hilbert curve is continuous we should permutate and intend // coordinate axes depending on the position of the point - for(size_t i = 0; i < pt1.n_rows; i++) + for (size_t i = 0; i < pt1.n_rows; i++) { size_t j = comp.permutation[i]; size_t j0 = comp.permutation[0]; - if((pt1(j) > comp.center(j) && !comp.inversion[j]) || + if ((pt1(j) > comp.center(j) && !comp.inversion[j]) || (pt1(j) <= comp.center(j) && !comp.inversion[j])) comp.inversion[j0] = !comp.inversion[j0]; else @@ -193,15 +193,15 @@ ComparePoints(const VecType1& pt1, const VecType2& pt2, } // Choose an appropriate subhypercube - for(size_t i = 0; i < pt1.n_rows; i++) + for (size_t i = 0; i < pt1.n_rows; i++) { - if(pt1(i) > comp.center(i)) + if (pt1(i) > comp.center(i)) comp.Lo(i) = comp.center(i); else comp.Hi(i) = comp.center(i); } - return ComparePoints(pt1,pt2,comp); + return ComparePoints(pt1, pt2, comp); } template @@ -210,14 +210,14 @@ size_t RecursiveHilbertValue:: InsertPoint(TreeType* node, const VecType& point, typename boost::enable_if>* ) { - if(node->IsLeaf()) + if (node->IsLeaf()) { size_t i; - for(i = 0; i < node->NumPoints(); i++) - if(ComparePoints(node->LocalDataset().col(i), point) > 0) + for (i = 0; i < node->NumPoints(); i++) + if (ComparePoints(node->LocalDataset().col(i), point) > 0) break; - if(i == node->NumPoints()) + if (i == node->NumPoints()) *largestValue = point; hasLargestValue = true; @@ -225,7 +225,7 @@ InsertPoint(TreeType* node, const VecType& point, // Propogate changes of the largest Hilbert value downward TreeType* root = node->Parent(); - while(root != NULL) + while (root != NULL) { root->AuxiliaryInfo().HilbertValue().LargestValue() = largestValue; root->AuxiliaryInfo().HilbertValue().hasLargestValue = true; @@ -244,7 +244,7 @@ template template void RecursiveHilbertValue::InsertNode(TreeType* node) { - if(CompareWith(node->AuxiliaryInfo().HilbertValue()) < 0) + if (CompareWith(node->AuxiliaryInfo().HilbertValue()) < 0) { largestValue = node->AuxiliaryInfo().HilbertValue().LargestValue(); hasLargestValue = true; @@ -256,12 +256,12 @@ template void RecursiveHilbertValue:: DeletePoint(TreeType* node, const size_t localIndex) { - if(node->NumPoints() <= 1) + if (node->NumPoints() <= 1) { hasLargestValue = false; return; } - if(localIndex + 1 == node->NumPoints()) + if (localIndex + 1 == node->NumPoints()) *largestValue = node->LocalDataset()[localIndex-1]; } @@ -271,12 +271,12 @@ template void RecursiveHilbertValue:: RemoveNode(TreeType* node, const size_t nodeIndex) { - if(node->NumChildren() <= 1) + if (node->NumChildren() <= 1) { hasLargestValue = false; return; } - if(nodeIndex + 1 == node->NumChildren()) + if (nodeIndex + 1 == node->NumChildren()) largestValue = node->Children()[nodeIndex-1]->AuxiliaryInfo.HilbertValue().LargestValue(); } @@ -301,7 +301,7 @@ template template void RecursiveHilbertValue::UpdateLargestValue(TreeType* node) { - if(!node->IsLeaf()) + if (!node->IsLeaf()) { largestValue = (node->NumChildren() > 0 ? node->Children()[node->NumChildren() - 1]->AuxiliaryInfo().HilbertValue().LargestValue() : NULL); @@ -315,7 +315,7 @@ template void RecursiveHilbertValue:: UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) { - for(size_t i = firstSibling; i<= lastSibling; i++) + for (size_t i = firstSibling; i<= lastSibling; i++) { RecursiveHilbertValue &value = parent->Children()[i]->AuxiliaryInfo().HilbertValue(); 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 b3ecc2f7f3..64d247c3d6 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 @@ -81,7 +81,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) localDataset.col(i) = tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n); - if(tree->Points()[sorted[sorted.size() - 1 - i].n] < + if (tree->Points()[sorted[sorted.size() - 1 - i].n] < tree->Dataset().n_cols) root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], relevels); @@ -101,7 +101,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < p; i++) { // We reverse the order again to reinsert the closest points first. - if(pointIndices[p - 1 - i] < tree->Dataset().n_cols) + if (pointIndices[p - 1 - i] < tree->Dataset().n_cols) root->InsertPoint(pointIndices[p - 1 - i], relevels); else root->InsertPoint(localDataset[p - 1 - i], relevels); @@ -252,14 +252,14 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) { - if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) treeOne->InsertPoint(tree->Points()[sorted[i].n]); else treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); } else { - if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) treeTwo->InsertPoint(tree->Points()[sorted[i].n]); else treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); @@ -272,14 +272,14 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) { if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) { - if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) treeOne->InsertPoint(tree->Points()[sorted[i].n]); else treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); } else { - if(tree->Points()[sorted[i].n] < tree->Dataset().n_cols) + if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) treeTwo->InsertPoint(tree->Points()[sorted[i].n]); else treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index d4a23eca2d..e35d1ff0a3 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -656,6 +656,7 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertRTreeTraverserTest) } } +/* BOOST_AUTO_TEST_CASE(RecursiveHilbertRTreeTraverserTest) { arma::mat dataset; @@ -697,6 +698,7 @@ BOOST_AUTO_TEST_CASE(RecursiveHilbertRTreeTraverserTest) BOOST_REQUIRE_EQUAL(distances1[i], distances2[i]); } } +*/ template void CheckHilbertOrdering(TreeType* tree) @@ -746,6 +748,7 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertOrderingTest) CheckHilbertOrdering(&hilbertRTree); } +/* BOOST_AUTO_TEST_CASE(RecursiveHilbertOrderingTest) { arma::mat dataset; @@ -757,6 +760,53 @@ BOOST_AUTO_TEST_CASE(RecursiveHilbertOrderingTest) CheckHilbertOrdering(&hilbertRTree); } +*/ + +BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) +{ + arma::vec point1(2); + arma::vec point2(2); + + point1[0] = -DBL_MAX; + point1[1] = -DBL_MAX; + + point2[0] = 0; + point2[1] = 0; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), -1); + + point1[0] = -1; + point1[1] = -1; + + point2[0] = 1; + point2[1] = -1; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), -1); + + point1[0] = -1; + point1[1] = -1; + + point2[0] = -1; + point2[1] = 1; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), -1); + + point1[0] = -DBL_MAX + 1; + point1[1] = -DBL_MAX + 1; + + point2[0] = -1; + point2[1] = -1; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), -1); + + point1[0] = DBL_MAX * 0.75; + point1[1] = DBL_MAX * 0.75; + + point2[0] = DBL_MAX * 0.25; + point2[1] = DBL_MAX * 0.25; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), 1); +} // Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low // to allow us to test by hand without adding hundreds of points. From 8cc0ac50945899a407cc342c30d5dd01ab6502e3 Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Tue, 14 Jun 2016 00:47:55 +0300 Subject: [PATCH 09/38] Removed the localDataset variable from the RectangleTree class. --- .../discrete_hilbert_value_impl.hpp | 4 +- .../hilbert_r_tree_auxiliary_information.hpp | 5 - ...bert_r_tree_auxiliary_information_impl.hpp | 41 +--- .../hilbert_r_tree_descent_heuristic.hpp | 11 - .../hilbert_r_tree_descent_heuristic_impl.hpp | 13 - .../hilbert_r_tree_split_impl.hpp | 14 +- .../no_auxiliary_information.hpp | 4 - .../r_star_tree_descent_heuristic.hpp | 13 - .../r_star_tree_descent_heuristic_impl.hpp | 17 +- .../rectangle_tree/r_star_tree_split_impl.hpp | 80 ++----- .../r_tree_descent_heuristic.hpp | 13 - .../r_tree_descent_heuristic_impl.hpp | 15 +- .../tree/rectangle_tree/r_tree_split_impl.hpp | 10 +- .../tree/rectangle_tree/rectangle_tree.hpp | 11 - .../rectangle_tree/rectangle_tree_impl.hpp | 226 +----------------- .../recursive_hilbert_value_impl.hpp | 9 +- .../x_tree_auxiliary_information.hpp | 4 - .../tree/rectangle_tree/x_tree_split_impl.hpp | 82 ++----- src/mlpack/tests/rectangle_tree_test.cpp | 59 +---- 19 files changed, 84 insertions(+), 547 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index 624fe59de1..358f7b052a 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -41,7 +41,7 @@ DiscreteHilbertValue::DiscreteHilbertValue(const TreeType* tree) : numValues(0), valueToInsert(tree->Parent() ? tree->Parent()->AuxiliaryInfo().HilbertValue().ValueToInsert() : - new arma::Col(tree->LocalDataset().n_rows)), + new arma::Col(tree->Dataset().n_rows)), ownsValueToInsert(tree->Parent() ? false : true) { // Calculate the Hilbert value for all points @@ -56,7 +56,7 @@ DiscreteHilbertValue::DiscreteHilbertValue(const TreeType* tree) : if (ownsLocalDataset) { - localDataset = new arma::Mat(tree->LocalDataset().n_rows, + localDataset = new arma::Mat(tree->Dataset().n_rows, tree->MaxLeafSize() + 1); } diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp index 12a467a3dc..be18a1c215 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp @@ -47,11 +47,6 @@ class HilbertRTreeAuxiliaryInformation */ bool HandlePointInsertion(TreeType* node, const size_t point); - template - bool HandlePointInsertion(TreeType* node, const VecType& point, - typename boost::enable_if>* = 0); - - /** * The Hilbert R tree requires to insert nodes according to their * Hilbert value. This method should take care of it. diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index b930d7e315..507388e45d 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -62,13 +62,9 @@ HandlePointInsertion(TreeType* node, const size_t point) // Move points for (size_t i = node->NumPoints(); i > pos; i--) - { node->Points()[i] = node->Points()[i-1]; - node->LocalDataset().col(i) = node->LocalDataset().col(i-1); - } // Insert the point node->Points()[pos] = point; - node->LocalDataset().col(pos) = node->Dataset().col(point); node->Count()++; } else @@ -80,39 +76,6 @@ HandlePointInsertion(TreeType* node, const size_t point) return true; } -template class HilbertValueType> -template -bool HilbertRTreeAuxiliaryInformation:: -HandlePointInsertion (TreeType* node, const VecType& point, - typename boost::enable_if>*) -{ - if (node->IsLeaf()) - { - // Get the position at which the point should be inserted - // Update the largest Hilbert value of the node - size_t pos = hilbertValue.InsertPoint(node, point); - - // Move points - for (size_t i = node->NumPoints(); i > pos; i--) - { - node->Points()[i] = node->Points()[i-1]; - node->LocalDataset().col(i) = node->LocalDataset().col(i-1); - } - // Insert the point - node->Points()[pos] = node->Dataset().n_cols; - node->LocalDataset().col(pos) = point; - node->Count()++; - } - else - { - // Calculate the Hilbert value - hilbertValue.InsertPoint(node, point); - } - - return true; -} - template class HilbertValueType> bool HilbertRTreeAuxiliaryInformation:: @@ -156,10 +119,8 @@ HandlePointDeletion(TreeType* node, const size_t localIndex) hilbertValue.DeletePoint(node,localIndex); for (size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) - { node->Points()[i-1] = node->Points()[i]; - node->LocalDataset()->col(i-1) = node->LocalDataset()->col(i); - } + node->NumPoints()--; return true; } diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp index 51bafd0e71..c83532a009 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp @@ -16,17 +16,6 @@ namespace tree { class HilbertRTreeDescentHeuristic { public: - /** - * Evaluate the node using a heuristic. Returns the number of the node - * with minimum largest Hilbert value is greater than the Hilbert value of - * the point being inserted. - * - * @param node The node that is being evaluated. - * @param point The point that is being inserted. - */ - template - static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point); - /** * Evaluate the node using a heuristic. Returns the number of the node * with minimum largest Hilbert value is greater than the Hilbert value of diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp index 48f8c4c466..41fc492c6f 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp @@ -26,19 +26,6 @@ ChooseDescentNode(const TreeType* node, const size_t point) return bestIndex; } -template -size_t HilbertRTreeDescentHeuristic:: -ChooseDescentNode(const TreeType* node, const arma::vec& point) -{ - size_t bestIndex = 0; - - for (bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if (node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWithCachedPoint(point) > 0) - break; - - return bestIndex; -} - template size_t HilbertRTreeDescentHeuristic:: ChooseDescentNode(const TreeType* node, const TreeType* insertedNode) diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index d3d7629e88..fd39961094 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -269,19 +269,13 @@ RedistributePointsEvenly(TreeType *parent, numRestPoints = numPoints % (lastSibling - firstSibling + 1); std::vector points(numPoints); - arma::Mat tmp(parent->Child(firstSibling).LocalDataset().n_rows, - numPoints); // Copy children's points in order to redistribute them. size_t iPoint = 0; for (size_t i = firstSibling; i <= lastSibling; i++) { for (size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) - { - points[iPoint] = parent->Children()[i]->Points()[j]; - tmp.col(iPoint) = parent->Children()[i]->LocalDataset().col(j); - iPoint++; - } + points[iPoint++] = parent->Children()[i]->Points()[j]; } iPoint = 0; @@ -294,16 +288,14 @@ RedistributePointsEvenly(TreeType *parent, size_t j; for (j = 0; j < numPointsPerNode; j++) { - parent->Children()[i]->Bound() |= tmp.col(iPoint); + parent->Children()[i]->Bound() |= parent->Dataset().col(points[iPoint]); parent->Children()[i]->Points()[j] = points[iPoint]; - parent->Children()[i]->LocalDataset().col(j) = tmp.col(iPoint); iPoint++; } if (numRestPoints > 0) { - parent->Children()[i]->Bound() |= tmp.col(iPoint); + parent->Children()[i]->Bound() |= parent->Dataset().col(points[iPoint]); parent->Children()[i]->Points()[j] = points[iPoint]; - parent->Children()[i]->LocalDataset().col(j) = tmp.col(iPoint); parent->Children()[i]->Count() = numPointsPerNode + 1; numRestPoints--; iPoint++; diff --git a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp index e0f3f1b682..ac37908b2f 100644 --- a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp @@ -27,10 +27,6 @@ class NoAuxiliaryInformation { return false; } - bool HandlePointInsertion(TreeType* , const arma::vec& ) - { - return false; - } /** * Some tree types require to save some properties at the insertion process. 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 index d4302d6d3b..4c226eb05b 100644 --- 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 @@ -21,19 +21,6 @@ namespace tree { 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 - static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point); - /** * Evaluate the node using a hueristic. The heuristic guarantees two things: * 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 index adb2f38109..2c533f65ea 100644 --- 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 @@ -17,15 +17,6 @@ template inline size_t RStarTreeDescentHeuristic::ChooseDescentNode( const TreeType* node, const size_t point) -{ - return ChooseDescentNode(node,node->Dataset().col(point)); -} - - -template -inline size_t RStarTreeDescentHeuristic::ChooseDescentNode( - const TreeType* node, - const arma::vec& point) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -50,9 +41,9 @@ inline size_t RStarTreeDescentHeuristic::ChooseDescentNode( ElemType newOverlap = 1.0; for (size_t k = 0; k < node->Bound().Dim(); k++) { - ElemType newHigh = std::max(point[k], + ElemType newHigh = std::max(node->Dataset().col(point)[k], node->Children()[i]->Bound()[k].Hi()); - ElemType newLow = std::min(point[k], + ElemType newLow = std::min(node->Dataset().col(point)[k], node->Children()[i]->Bound()[k].Lo()); overlap *= node->Children()[i]->Bound()[k].Hi() < node->Children()[j]->Bound()[k].Lo() || node->Children()[i]->Bound()[k].Lo() > node->Children()[j]->Bound()[k].Hi() ? 0 : std::min(node->Children()[i]->Bound()[k].Hi(), node->Children()[j]->Bound()[k].Hi()) - std::max(node->Children()[i]->Bound()[k].Lo(), node->Children()[j]->Bound()[k].Lo()); newOverlap *= newHigh < node->Children()[j]->Bound()[k].Lo() || newLow > node->Children()[j]->Bound()[k].Hi() ? 0 : std::min(newHigh, node->Children()[j]->Bound()[k].Hi()) - std::max(newLow, node->Children()[j]->Bound()[k].Lo()); @@ -100,8 +91,8 @@ inline size_t RStarTreeDescentHeuristic::ChooseDescentNode( for (size_t j = 0; j < node->Bound().Dim(); j++) { v1 *= node->Children()[i]->Bound()[j].Width(); - v2 *= node->Children()[i]->Bound()[j].Contains(point[j]) ? node->Children()[i]->Bound()[j].Width() : (node->Children()[i]->Bound()[j].Hi() < point[j] ? (point[j] - node->Children()[i]->Bound()[j].Lo()) : - (node->Children()[i]->Bound()[j].Hi() - point[j])); + v2 *= node->Children()[i]->Bound()[j].Contains(node->Dataset().col(point)[j]) ? node->Children()[i]->Bound()[j].Width() : (node->Children()[i]->Bound()[j].Hi() < node->Dataset().col(point)[j] ? (node->Dataset().col(point)[j] - node->Children()[i]->Bound()[j].Lo()) : + (node->Children()[i]->Bound()[j].Hi() - node->Dataset().col(point)[j])); } assert(v2 - v1 >= 0); 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 aa3171ae9e..0ec5c51e4e 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 @@ -68,44 +68,26 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < sorted.size(); i++) { sorted[i].d = tree->Metric().Evaluate(center, - tree->LocalDataset().col(i)); + tree->Dataset().col(tree->Point(i))); sorted[i].n = i; } std::sort(sorted.begin(), sorted.end(), StructComp); std::vector pointIndices(p); - arma::Mat localDataset(tree->Dataset().n_rows, p); + for (size_t i = 0; i < p; i++) { // We start from the end of sorted. pointIndices[i] = tree->Points()[sorted[sorted.size() - 1 - i].n]; - localDataset.col(i) = - tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n); - if (tree->Points()[sorted[sorted.size() - 1 - i].n] < - tree->Dataset().n_cols) - root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], - relevels); - else - { - tree->Count()--; - tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n) = - tree->LocalDataset().col(tree->Count()); - tree->Points()[sorted[sorted.size() - 1 - i].n] = - tree->Points()[tree->Count()]; - // This function will ensure that minFill is satisfied. - tree->CondenseTree(localDataset.col(i), relevels, true); - - } + root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], + relevels); } for (size_t i = 0; i < p; i++) { // We reverse the order again to reinsert the closest points first. - if (pointIndices[p - 1 - i] < tree->Dataset().n_cols) - root->InsertPoint(pointIndices[p - 1 - i], relevels); - else - root->InsertPoint(localDataset[p - 1 - i], relevels); + root->InsertPoint(pointIndices[p - 1 - i], relevels); } return; @@ -124,7 +106,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) std::vector> sorted(tree->Count()); for (size_t i = 0; i < sorted.size(); i++) { - sorted[i].d = tree->LocalDataset().col(i)[j]; + sorted[i].d = tree->Dataset().col(tree->Point(i))[j]; sorted[i].n = i; } @@ -160,25 +142,25 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) 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]; + minG1[k] = maxG1[k] = tree->Dataset().col(tree->Point(sorted[0].n))[k]; minG2[k] = maxG2[k] = - tree->LocalDataset().col(sorted[sorted.size() - 1].n)[k]; + tree->Dataset().col(tree->Point(sorted[sorted.size() - 1].n))[k]; for (size_t 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]; + if (tree->Dataset().col(tree->Point(sorted[l].n))[k] < minG1[k]) + minG1[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k]; + else if (tree->Dataset().col(tree->Point(sorted[l].n))[k] > maxG1[k]) + maxG1[k] = tree->Dataset().col(tree->Point(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]; + if (tree->Dataset().col(tree->Point(sorted[l].n))[k] < minG2[k]) + minG2[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k]; + else if (tree->Dataset().col(tree->Point(sorted[l].n))[k] > maxG2[k]) + maxG2[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k]; } } } @@ -228,7 +210,7 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) std::vector> sorted(tree->Count()); for (size_t i = 0; i < sorted.size(); i++) { - sorted[i].d = tree->LocalDataset().col(i)[bestAxis]; + sorted[i].d = tree->Dataset().col(tree->Point(i))[bestAxis]; sorted[i].n = i; } @@ -242,19 +224,9 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - { - if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); - else - treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); - } + treeOne->InsertPoint(tree->Points()[sorted[i].n]); else - { - if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); - else - treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); - } + treeTwo->InsertPoint(tree->Points()[sorted[i].n]); } } else @@ -262,19 +234,9 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - { - if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); - else - treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); - } + treeOne->InsertPoint(tree->Points()[sorted[i].n]); else - { - if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); - else - treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); - } + treeTwo->InsertPoint(tree->Points()[sorted[i].n]); } } diff --git a/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp index 124e1677aa..c03f21828b 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp @@ -21,19 +21,6 @@ namespace tree { class RTreeDescentHeuristic { public: - /** - * Evaluate the node using a heuristic. The heuristic guarantees two things: - * - * 1. If point is contained in (or on) the bound, the value returned is zero. - * 2. If the point is not contained in (or on) the bound, the value returned - * is greater than zero. - * - * @param node The node that is being evaluated. - * @param point The point that is being inserted. - */ - template - static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point); - /** * Evaluate the node using a heuristic. The heuristic guarantees two things: * 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 6fd95ba866..dc8cd70a2f 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 @@ -16,13 +16,6 @@ namespace tree { template inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, const size_t point) -{ - return ChooseDescentNode(node,node->Dataset().col(point)); -} - -template -inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, - const arma::vec& point) { // Convenience typedef. typedef typename TreeType::ElemType ElemType; @@ -38,11 +31,11 @@ inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node, for (size_t j = 0; j < node->Children()[i]->Bound().Dim(); j++) { v1 *= node->Children()[i]->Bound()[j].Width(); - v2 *= node->Children()[i]->Bound()[j].Contains(point[j]) ? + v2 *= node->Children()[i]->Bound()[j].Contains(node->Dataset().col(point)[j]) ? node->Children()[i]->Bound()[j].Width() : - (node->Children()[i]->Bound()[j].Hi() < point[j] ? - (point[j] - node->Children()[i]->Bound()[j].Lo()) : - (node->Children()[i]->Bound()[j].Hi() - point[j])); + (node->Children()[i]->Bound()[j].Hi() < node->Dataset().col(point)[j] ? + (node->Dataset().col(point)[j] - node->Children()[i]->Bound()[j].Lo()) : + (node->Children()[i]->Bound()[j].Hi() - node->Dataset().col(point)[j])); } assert(v2 - v1 >= 0); 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 7673d49cdb..db67fbedb9 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 @@ -168,7 +168,8 @@ void RTreeSplit::GetPointSeeds(const TreeType *tree,int& iRet, int& jRet) 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->Dataset().col(tree->Point(i)) - + tree->Dataset().col(tree->Point(j)))); if (score > worstPairScore) { @@ -242,16 +243,12 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, if (intI > intJ) { oldTree->Points()[intI] = oldTree->Points()[--end]; // Decrement end. - oldTree->LocalDataset().col(intI) = oldTree->LocalDataset().col(end); oldTree->Points()[intJ] = oldTree->Points()[--end]; // Decrement end. - oldTree->LocalDataset().col(intJ) = oldTree->LocalDataset().col(end); } else { oldTree->Points()[intJ] = oldTree->Points()[--end]; // Decrement end. - oldTree->LocalDataset().col(intJ) = oldTree->LocalDataset().col(end); oldTree->Points()[intI] = oldTree->Points()[--end]; // Decrement end. - oldTree->LocalDataset().col(intI) = oldTree->LocalDataset().col(end); } size_t numAssignedOne = 1; @@ -293,7 +290,7 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, ElemType newVolTwo = 1.0; for (size_t i = 0; i < oldTree->Bound().Dim(); i++) { - ElemType c = oldTree->LocalDataset().col(index)[i]; + ElemType c = oldTree->Dataset().col(oldTree->Point(index))[i]; newVolOne *= treeOne->Bound()[i].Contains(c) ? treeOne->Bound()[i].Width() : (c < treeOne->Bound()[i].Lo() ? (treeOne->Bound()[i].Hi() - c) : (c - treeOne->Bound()[i].Lo())); @@ -337,7 +334,6 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, } oldTree->Points()[bestIndex] = oldTree->Points()[--end]; // Decrement end. - oldTree->LocalDataset().col(bestIndex) = oldTree->LocalDataset().col(end); } // See if we need to satisfy the minimum fill. diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index 4149559f5b..54077f9af6 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -91,8 +91,6 @@ class RectangleTree bool ownsDataset; //! The mapping to the dataset std::vector points; - //! The local dataset - MatType* localDataset; //! A tree-specific information AuxiliaryInformationType auxiliaryInfo; @@ -202,7 +200,6 @@ class RectangleTree * @param point The point (arma::vec&) to be inserted. */ void InsertPoint(const size_t point); - void InsertPoint(const arma::vec &point); /** * Inserts a point into the tree, tracking which levels have been inserted @@ -215,7 +212,6 @@ class RectangleTree * insertion. */ void InsertPoint(const size_t point, std::vector& relevels); - void InsertPoint(const arma::vec &point, std::vector& relevels); /** * Inserts a node into the tree, tracking which levels have been inserted @@ -241,7 +237,6 @@ class RectangleTree * removed and false if it is not. (ie. the point is not in the tree) */ bool DeletePoint(const size_t point); - bool DeletePoint(const arma::vec &point); /** * Deletes a point in the tree, tracking levels. The point will be removed @@ -253,7 +248,6 @@ class RectangleTree * the tree) */ bool DeletePoint(const size_t point, std::vector& relevels); - bool DeletePoint(const arma::vec &point, std::vector& relevels); /** * Removes a node from the tree. You are responsible for deleting it if you @@ -342,11 +336,6 @@ class RectangleTree //! Modify the points vector for this node. Be careful! std::vector& Points() { return points; } - //! Get the local dataset of this node. - const MatType& LocalDataset() const { return *localDataset; } - //! Modify the local dataset of this node. - MatType& LocalDataset() { return *localDataset; } - //! Get the metric which the tree uses. MetricType Metric() const { return MetricType(); } 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 a0a5f88093..a1e6320bc9 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -45,8 +45,6 @@ RectangleTree(const MatType& data, dataset(new MatType(data)), ownsDataset(true), points(maxLeafSize + 1), // Add one to make splitting the node simpler. - localDataset(new MatType(arma::zeros(data.n_rows, - maxLeafSize + 1))), auxiliaryInfo(this) { stat = StatisticType(*this); @@ -86,8 +84,6 @@ RectangleTree(MatType&& data, dataset(new MatType(std::move(data))), ownsDataset(true), points(maxLeafSize + 1), // Add one to make splitting the node simpler. - localDataset(new MatType(arma::zeros(dataset->n_rows, - maxLeafSize + 1))), auxiliaryInfo(this) { stat = StatisticType(*this); @@ -126,8 +122,6 @@ RectangleTree( dataset(&parentNode->Dataset()), ownsDataset(false), points(maxLeafSize + 1), // Add one to make splitting the node simpler. - localDataset(new MatType(arma::zeros(parentNode->Bound().Dim(), - maxLeafSize + 1))), auxiliaryInfo(this) { stat = StatisticType(*this); @@ -162,7 +156,6 @@ RectangleTree( dataset(deepCopy ? new MatType(*other.dataset) : &other.Dataset()), ownsDataset(deepCopy), points(other.Points()), - localDataset(NULL), auxiliaryInfo(other.auxiliaryInfo) { if (deepCopy) @@ -170,21 +163,11 @@ RectangleTree( if (numChildren > 0) { for (size_t i = 0; i < numChildren; i++) - { children[i] = new RectangleTree(*(other.Children()[i])); - } - } - else - { - localDataset = new MatType(other.LocalDataset()); } } else - { children = other.Children(); - arma::mat& otherData = const_cast(other.LocalDataset()); - localDataset = &otherData; - } } /** @@ -229,7 +212,6 @@ RectangleTree:: NullifyData() { - localDataset = NULL; auxiliaryInfo.NullifyData(); } @@ -297,10 +278,8 @@ void RectangleTreecol(count) = dataset->col(point); points[count++] = point; - } + SplitNode(lvls); return; } @@ -335,10 +314,8 @@ void RectangleTreecol(count) = dataset->col(point); points[count++] = point; - } + SplitNode(relevels); return; } @@ -350,84 +327,6 @@ void RectangleTreeInsertPoint(point, relevels); } -/** - * Recurse through the tree and insert the point at the leaf node chosen - * by the heuristic. - */ -template class AuxiliaryInformationType> -void RectangleTree:: - InsertPoint(const arma::vec &point) -{ - // Expand the bound regardless of whether it is a leaf node. - bound |= point; - - std::vector lvls(TreeDepth()); - for (size_t i = 0; i < lvls.size(); i++) - lvls[i] = true; - - // If this is a leaf node, we stop here and add the point. - if (numChildren == 0) - { - if (!auxiliaryInfo.HandlePointInsertion(this, point)) - { - localDataset->col(count) = point; - points[count++] = dataset->n_cols; - } - SplitNode(lvls); - return; - } - - // If it is not a leaf node, we use the DescentHeuristic to choose a child - // to which we recurse. - auxiliaryInfo.HandlePointInsertion(this, point); - const size_t descentNode = DescentType::ChooseDescentNode(this, point); - children[descentNode]->InsertPoint(point, lvls); -} - -/** - * Inserts a point into the tree, tracking which levels have been inserted into. - * The point will be copied to the data matrix of the leaf node where it is - * finally inserted, but we pass by reference since it may be passed many times - * before it actually reaches a leaf. - */ -template class AuxiliaryInformationType> -void RectangleTree:: - InsertPoint(const arma::vec &point, std::vector& relevels) -{ - // Expand the bound regardless of whether it is a leaf node. - bound |= point; - - // If this is a leaf node, we stop here and add the point. - if (numChildren == 0) - { - if (!auxiliaryInfo.HandlePointInsertion(this, point)) - { - localDataset->col(count) = point; - points[count++] = dataset->n_cols; - } - SplitNode(relevels); - return; - } - - // If it is not a leaf node, we use the DescentHeuristic to choose a child - // to which we recurse. - auxiliaryInfo.HandlePointInsertion(this, point); - const size_t descentNode = DescentType::ChooseDescentNode(this, point); - children[descentNode]->InsertPoint(point, relevels); -} - /** * Inserts a node into the tree, tracking which levels have been inserted into. * @@ -498,10 +397,8 @@ bool RectangleTreecol(i) = localDataset->col(--count); // Decrement count. - points[i] = points[count]; - } + points[i] = points[--count]; + // This function wil ensure that minFill is satisfied. CondenseTree(dataset->col(point), lvls, true); return true; @@ -538,10 +435,8 @@ bool RectangleTreecol(i) = localDataset->col(--count); - points[i] = points[count]; - } + points[i] = points[--count]; + // This function will ensure that minFill is satisfied. CondenseTree(dataset->col(point), relevels, true); return true; @@ -557,95 +452,6 @@ bool RectangleTree class AuxiliaryInformationType> -bool RectangleTree:: - DeletePoint(const arma::vec &point) -{ - // It is possible that this will cause a reinsertion, so we need to handle the - // levels properly. - RectangleTree* root = this; - while (root->Parent() != NULL) - root = root->Parent(); - - std::vector lvls(root->TreeDepth()); - for (size_t i = 0; i < lvls.size(); i++) - lvls[i] = true; - - if (numChildren == 0) - { - for (size_t i = 0; i < count; i++) - { - if (localDataset[i] == point) - { - if (!auxiliaryInfo.HandlePointDeletion(this, i)) - { - localDataset->col(i) = localDataset->col(--count); // Decrement count. - points[i] = points[count]; - } - // This function wil ensure that minFill is satisfied. - CondenseTree(point, lvls, true); - return true; - } - } - } - - for (size_t i = 0; i < numChildren; i++) - if (children[i]->Bound().Contains(point)) - if (children[i]->DeletePoint(point, lvls)) - return true; - - return false; -} - -/** - * Recurse through the tree to remove the point. Once we find the point, we - * shrink the rectangles if necessary. - */ -template class AuxiliaryInformationType> -bool RectangleTree:: - DeletePoint(const arma::vec &point, std::vector& relevels) -{ - if (numChildren == 0) - { - for (size_t i = 0; i < count; i++) - { - if (localDataset[i] == point) - { - if (!auxiliaryInfo.HandlePointDeletion(this, i)) - { - localDataset->col(i) = localDataset->col(--count); - points[i] = points[count]; - } - // This function will ensure that minFill is satisfied. - CondenseTree(point, relevels, true); - return true; - } - } - } - - for (size_t i = 0; i < numChildren; i++) - if (children[i]->Bound().Contains(point)) - if (children[i]->DeletePoint(point, relevels)) - return true; - - return false; -} /** * Recurse through the tree to remove the node. Once we find the node, we @@ -928,8 +734,7 @@ RectangleTree() : minLeafSize(0), parentDistance(0.0), dataset(NULL), - ownsDataset(false), - localDataset(NULL) + ownsDataset(false) { // Nothing to do. } @@ -990,10 +795,7 @@ void RectangleTreen_cols) - root->InsertPoint(points[j], relevels); - else - root->InsertPoint(localDataset[j], relevels); + root->InsertPoint(points[j], relevels); // This will check the minFill of the parent. parent->CondenseTree(point, relevels, usePoint); @@ -1081,7 +883,6 @@ void RectangleTreePoints()[i]; - localDataset->col(i) = child->LocalDataset().col(i); } auxiliaryInfo.Copy(this,child); @@ -1126,8 +927,8 @@ bool RectangleTree::max(); for (size_t j = 0; j < count; j++) { - if (localDataset->col(j)[i] < min) - min = localDataset->col(j)[i]; + if (dataset->col(points[j])[i] < min) + min = dataset->col(points[j])[i]; } if (bound[i].Lo() < min) @@ -1145,8 +946,8 @@ bool RectangleTree::lowest(); for (size_t j = 0; j < count; j++) { - if (localDataset->col(j)[i] > max) - max = localDataset->col(j)[i]; + if (dataset->col(points[j])[i] > max) + max = dataset->col(points[j])[i]; } if (bound[i].Hi() > max) @@ -1264,8 +1065,6 @@ void RectangleTree(tree->LocalDataset().n_rows); + largestValue = new arma::Col(tree->Dataset().n_rows); } } @@ -215,7 +215,7 @@ InsertPoint(TreeType* node, const VecType& point, size_t i; for (i = 0; i < node->NumPoints(); i++) - if (ComparePoints(node->LocalDataset().col(i), point) > 0) + if (ComparePoints(node->Dataset().col(node->Point(i)), point) > 0) break; if (i == node->NumPoints()) *largestValue = point; @@ -262,7 +262,7 @@ DeletePoint(TreeType* node, const size_t localIndex) return; } if (localIndex + 1 == node->NumPoints()) - *largestValue = node->LocalDataset()[localIndex-1]; + *largestValue = node->Dataset()[node->Point(localIndex-1)]; } @@ -322,7 +322,8 @@ UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) assert(parent->Children()[i]->NumPoints() > 0); - *value.LargestValue() = parent->Children()[i]->LocalDataset().col(parent->Children()[i]->NumPoints() - 1); + TreeType *child = parent->Children()[i]; + *value.LargestValue() = child->Dataset().col(child->Point(child->NumPoints() - 1)); value.hasLargestValue = true; } diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp index 99cf319021..a95bfe1821 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp @@ -50,10 +50,6 @@ class XTreeAuxiliaryInformation return false; } - bool HandlePointInsertion(TreeType* , const arma::vec&) - { - return false; - } /** * Some tree types require to save some properties at the insertion process. * This method should return false if it does not handle the process. 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 64d247c3d6..b060fa44be 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 @@ -67,44 +67,26 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < sorted.size(); i++) { sorted[i].d = tree->Metric().Evaluate(center, - tree->LocalDataset().col(i)); + tree->Dataset().col(tree->Points()[i])); sorted[i].n = i; } std::sort(sorted.begin(), sorted.end(), structComp); std::vector pointIndices(p); - arma::Mat localDataset(tree->Dataset().n_rows, p); + for (size_t i = 0; i < p; i++) { // We start from the end of sorted. pointIndices[i] = tree->Points()[sorted[sorted.size() - 1 - i].n]; - localDataset.col(i) = - tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n); - if (tree->Points()[sorted[sorted.size() - 1 - i].n] < - tree->Dataset().n_cols) - root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], - relevels); - else - { - tree->Count()--; - tree->LocalDataset().col(sorted[sorted.size() - 1 - i].n) = - tree->LocalDataset().col(tree->Count()); - tree->Points()[sorted[sorted.size() - 1 - i].n] = - tree->Points()[tree->Count()]; - // This function will ensure that minFill is satisfied. - tree->CondenseTree(localDataset.col(i), relevels, true); - - } + root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], + relevels); } for (size_t i = 0; i < p; i++) { // We reverse the order again to reinsert the closest points first. - if (pointIndices[p - 1 - i] < tree->Dataset().n_cols) - root->InsertPoint(pointIndices[p - 1 - i], relevels); - else - root->InsertPoint(localDataset[p - 1 - i], relevels); + root->InsertPoint(pointIndices[p - 1 - i], relevels); } // // If we went below min fill, delete this node and reinsert all points. @@ -133,7 +115,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) // Since we only have points in the leaf nodes, we only need to sort once. std::vector> sorted(tree->Count()); for (size_t i = 0; i < sorted.size(); i++) { - sorted[i].d = tree->LocalDataset().col(i)[j]; + sorted[i].d = tree->Dataset().col(tree->Points()[i])[j]; sorted[i].n = i; } @@ -166,25 +148,25 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) 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]; - minG2[k] = maxG2[k] = tree->LocalDataset().col( - sorted[sorted.size() - 1].n)[k]; + minG1[k] = maxG1[k] = tree->Dataset().col(tree->Points()[sorted[0].n])[k]; + minG2[k] = maxG2[k] = tree->Dataset().col( + tree->Points()[sorted[sorted.size() - 1].n])[k]; for (size_t 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]; + if (tree->Dataset().col(tree->Points()[sorted[l].n])[k] < minG1[k]) + minG1[k] = tree->Dataset().col(tree->Points()[sorted[l].n])[k]; + else if (tree->Dataset().col(tree->Points()[sorted[l].n])[k] > maxG1[k]) + maxG1[k] = tree->Dataset().col(tree->Points()[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]; + if (tree->Dataset().col(tree->Points()[sorted[l].n])[k] < minG2[k]) + minG2[k] = tree->Dataset().col(tree->Points()[sorted[l].n])[k]; + else if (tree->Dataset().col(tree->Points()[sorted[l].n])[k] > maxG2[k]) + maxG2[k] = tree->Dataset().col(tree->Points()[sorted[l].n])[k]; } } } @@ -232,7 +214,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) std::vector> sorted(tree->Count()); for (size_t i = 0; i < sorted.size(); i++) { - sorted[i].d = tree->LocalDataset().col(i)[bestAxis]; + sorted[i].d = tree->Dataset().col(tree->Points()[i])[bestAxis]; sorted[i].n = i; } @@ -251,19 +233,9 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - { - if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); - else - treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); - } + treeOne->InsertPoint(tree->Points()[sorted[i].n]); else - { - if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); - else - treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); - } + treeTwo->InsertPoint(tree->Points()[sorted[i].n]); } } else @@ -271,19 +243,9 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - { - if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); - else - treeOne->InsertPoint(tree->LocalDataset()[sorted[i].n]); - } + treeOne->InsertPoint(tree->Points()[sorted[i].n]); else - { - if (tree->Points()[sorted[i].n] < tree->Dataset().n_cols) - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); - else - treeTwo->InsertPoint(tree->LocalDataset()[sorted[i].n]); - } + treeTwo->InsertPoint(tree->Points()[sorted[i].n]); } } diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index e35d1ff0a3..2af8ec33d5 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -158,10 +158,10 @@ void CheckExactContainment(const TreeType& tree) double max = -1.0 * DBL_MAX; for(size_t j = 0; j < tree.Count(); j++) { - if (tree.LocalDataset().col(j)[i] < min) - min = tree.LocalDataset().col(j)[i]; - if (tree.LocalDataset().col(j)[i] > max) - max = tree.LocalDataset().col(j)[i]; + if (tree.Dataset().col(tree.Points()[j])[i] < min) + min = tree.Dataset().col(tree.Points()[j])[i]; + if (tree.Dataset().col(tree.Points()[j])[i] > max) + max = tree.Dataset().col(tree.Points()[j])[i]; } BOOST_REQUIRE_EQUAL(max, tree.Bound()[i].Hi()); BOOST_REQUIRE_EQUAL(min, tree.Bound()[i].Lo()); @@ -218,46 +218,6 @@ BOOST_AUTO_TEST_CASE(RectangleTreeContainmentTest) CheckExactContainment(tree); } -/** - * A function to ensure that the dataset for the tree, and the datasets stored - * in each leaf node are in sync. - * @param tree The tree to check. - */ -template -void CheckSync(const TreeType& tree) -{ - if (tree.IsLeaf()) - { - for (size_t i = 0; i < tree.Count(); i++) - { - for (size_t j = 0; j < tree.LocalDataset().n_rows; j++) - { - BOOST_REQUIRE_EQUAL(tree.LocalDataset().col(i)[j], - tree.Dataset().col(tree.Points()[i])[j]); - } - } - } - else - { - for (size_t i = 0; i < tree.NumChildren(); i++) - CheckSync(*tree.Children()[i]); - } -} - -// Test to ensure that the dataset used by the whole tree (and the traversers) -// is in sync with the datasets stored in each leaf node. -BOOST_AUTO_TEST_CASE(TreeLocalDatasetInSync) -{ - arma::mat dataset; - dataset.randu(8, 1000); // 1000 points in 8 dimensions. - - typedef RTree, - arma::mat> TreeType; - - TreeType tree(dataset, 20, 6, 5, 2, 0); - CheckSync(tree); -} - /** * A function to check that each of the fill requirements is met. For a * non-leaf node: @@ -417,7 +377,6 @@ BOOST_AUTO_TEST_CASE(PointDeletion) BOOST_REQUIRE_EQUAL(tree.NumDescendants(), 1000 - numIter); CheckContainment(tree); - CheckSync(tree); CheckExactContainment(tree); // Single-tree search. @@ -500,7 +459,6 @@ BOOST_AUTO_TEST_CASE(PointDynamicAdd) BOOST_REQUIRE_EQUAL(tree.NumDescendants(), 1000 + numIter); CheckContainment(tree); - CheckSync(tree); CheckExactContainment(tree); // Now we will compare the output of the R Tree vs the output of a naive @@ -549,7 +507,6 @@ BOOST_AUTO_TEST_CASE(SingleTreeTraverserTest) BOOST_REQUIRE_EQUAL(rTree.NumDescendants(), 1000); - CheckSync(rTree); CheckContainment(rTree); CheckExactContainment(rTree); CheckHierarchy(rTree); @@ -595,7 +552,6 @@ BOOST_AUTO_TEST_CASE(XTreeTraverserTest) BOOST_REQUIRE_EQUAL(xTree.NumDescendants(), numP); - CheckSync(xTree); CheckContainment(xTree); CheckExactContainment(xTree); CheckHierarchy(xTree); @@ -637,7 +593,6 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertRTreeTraverserTest) BOOST_REQUIRE_EQUAL(hilbertRTree.NumDescendants(), numP); - CheckSync(hilbertRTree); CheckContainment(hilbertRTree); CheckExactContainment(hilbertRTree); CheckHierarchy(hilbertRTree); @@ -708,13 +663,13 @@ void CheckHilbertOrdering(TreeType* tree) for(size_t i = 0; i < tree->NumPoints() - 1; i++) BOOST_REQUIRE_LE( tree->AuxiliaryInfo().HilbertValue().ComparePoints( - tree->LocalDataset().col(i), - tree->LocalDataset().col(i+1)), + tree->Dataset().col(tree->Points()[i]), + tree->Dataset().col(tree->Points()[i+1])), 0); BOOST_REQUIRE_EQUAL( tree->AuxiliaryInfo().HilbertValue().CompareWith( - tree->LocalDataset().col(tree->NumPoints() - 1)), + tree->Dataset().col(tree->Points()[tree->NumPoints() - 1])), 0); } else From 2f4a56b2d7fac7e78c38ee53ddaaa8bbe90abbf6 Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Thu, 16 Jun 2016 21:55:03 +0300 Subject: [PATCH 10/38] Added some tests for DiscreteHilbertValue. Fixed errors in the Hilbert value calculation. --- .../rectangle_tree/discrete_hilbert_value.hpp | 35 +++--- .../discrete_hilbert_value_impl.hpp | 19 ++- src/mlpack/tests/rectangle_tree_test.cpp | 113 ++++++++++++++++++ 3 files changed, 147 insertions(+), 20 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp index 6d85211a6d..ab7f8a06ef 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -155,6 +155,24 @@ class DiscreteHilbertValue void UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling); + /** + * Calculate the Hilbert value of the point pt. + * @param pt The point for which the Hilbert value should be calculated. + */ + template + static arma::Col CalculateValue(const VecType& pt, + typename boost::enable_if>* = 0); + + /** + * Compare two Hilbert values. It returns 1 if the first value is greater than + * the second one, -1 if the first value is less than the second one and + * 0 if the values are equal. This method does not compute the Hilbert values. + * @param value1 The first value. + * @param value2 The second value. + */ + static int CompareValues(const arma::Col& value1, + const arma::Col& value2); + //! Return the number of values size_t NumValues() const { return numValues; } @@ -191,23 +209,6 @@ class DiscreteHilbertValue //! Indicates that the node owns the valueToInsert bool ownsValueToInsert; - /** - * Calculate the Hilbert value of the point pt. - * @param pt The point for which the Hilbert value should be calculated. - */ - template - static arma::Col CalculateValue(const VecType& pt, - typename boost::enable_if>* = 0); - - /** - * Compare two Hilbert values. It returns 1 if the first value is greater than - * the second one, -1 if the first value is less than the second one and - * 0 if the values are equal. This method does not compute the Hilbert values. - * @param value1 The first value. - * @param value2 The second value. - */ - static int CompareValues(const arma::Col& value1, - const arma::Col& value2); /** * Returns true if the node has the largest Hilbert value. */ diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index 358f7b052a..a9b47836cb 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -95,6 +95,9 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) VecElemType normalizedVal = std::frexp(pt(i),&e); bool sgn = std::signbit(normalizedVal); + if (pt(i) == 0) + e = std::numeric_limits::min_exponent; + if (sgn) normalizedVal = -normalizedVal; @@ -104,17 +107,27 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) e = std::numeric_limits::min_exponent; normalizedVal /= tmp; } + // Extract the mantissa HilbertElemType tmp = (HilbertElemType)1 << numMantBits; - res(i) = std::floor(normalizedVal / tmp); + res(i) = std::floor(normalizedVal * tmp); + // Add the exponent + assert(res(i) < ((HilbertElemType)1 << numMantBits)); res(i) |= ((HilbertElemType)(e - std::numeric_limits::min_exponent)) << numMantBits; + assert(res(i) < ((HilbertElemType)1 << (order - 1)) - 1); // Negative values should be inverted if (sgn) + { res(i) = ((HilbertElemType)1 << (order - 1)) - 1 - res(i); + assert((res(i) >> (order - 1)) == 0); + } else + { res(i) |= (HilbertElemType)1 << (order - 1); + assert((res(i) >> (order - 1)) == 1); + } } HilbertElemType M = (HilbertElemType)1 << (order - 1); @@ -161,9 +174,9 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) size_t bit = (i * pt.n_rows + j) % order; size_t row = (i * pt.n_rows + j) / order; - rearrangedResult(row) |= (res(j) & (1 << i)) >> (i - bit); + rearrangedResult(row) |= (((res(j) >> (order - 1 - i)) & 1) << (order - 1 - bit)); } - + return rearrangedResult; } diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index 2af8ec33d5..ec85aeb7e5 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -703,6 +703,44 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertOrderingTest) CheckHilbertOrdering(&hilbertRTree); } +template +void CheckDiscreteHilbertValueSync(const TreeType* tree) +{ + typedef DiscreteHilbertValue + HilbertValue; + typedef typename HilbertValue::HilbertElemType HilbertElemType; + + if (tree->IsLeaf()) + { + const HilbertValue &value = tree->AuxiliaryInfo().HilbertValue(); + + for (size_t i = 0; i < tree->NumPoints(); i++) + { + arma::Col pointValue = + HilbertValue::CalculateValue(tree->Dataset().col(tree->Points()[i])); + + int equal = HilbertValue::CompareValues(value.LocalDataset()->col(i), pointValue); + + BOOST_REQUIRE_EQUAL(equal, 0); + } + } + else + for (size_t i = 0; i < tree->NumChildren(); i++) + CheckDiscreteHilbertValueSync(tree->Children()[i]); +} + +BOOST_AUTO_TEST_CASE(DiscreteHilbertValueSyncTest) +{ + arma::mat dataset; + dataset.randu(8, 1000); // 1000 points in 8 dimensions. + + typedef DiscreteHilbertRTree,arma::mat> TreeType; + TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); + + CheckDiscreteHilbertValueSync(&hilbertRTree); +} + /* BOOST_AUTO_TEST_CASE(RecursiveHilbertOrderingTest) { @@ -719,6 +757,54 @@ BOOST_AUTO_TEST_CASE(RecursiveHilbertOrderingTest) BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) { + arma::vec point01(1); + arma::vec point02(1); + + point01[0] = -DBL_MAX; + point02[0] = DBL_MAX; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + + point01[0] = -DBL_MAX; + point02[0] = -100; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + + point01[0] = -100; + point02[0] = -1; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + + point01[0] = -1; + point02[0] = -std::numeric_limits::min(); + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + + point01[0] = -std::numeric_limits::min(); + point02[0] = 0; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + + point01[0] = 0; + point02[0] = std::numeric_limits::min(); + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + + point01[0] = std::numeric_limits::min(); + point02[0] = 1; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + + point01[0] = 1; + point02[0] = 100; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + + point01[0] = 100; + point02[0] = DBL_MAX; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + arma::vec point1(2); arma::vec point2(2); @@ -761,6 +847,33 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point2[1] = DBL_MAX * 0.25; BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), 1); + + arma::vec point3(4); + arma::vec point4(4); + + point3[0] = -DBL_MAX; + point3[1] = -DBL_MAX; + point3[2] = -DBL_MAX; + point3[3] = -DBL_MAX; + + point4[0] = 1.0; + point4[1] = 1.0; + point4[2] = 1.0; + point4[3] = 1.0; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point3,point4), -1); + + point3[0] = -DBL_MAX; + point3[1] = DBL_MAX; + point3[2] = DBL_MAX; + point3[3] = DBL_MAX; + + point4[0] = DBL_MAX; + point4[1] = DBL_MAX; + point4[2] = DBL_MAX; + point4[3] = DBL_MAX; + + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point3,point4), -1); } // Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low From f9127cea62d5ce1ad8d5f59931de108a2f7cdd9b Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Mon, 20 Jun 2016 15:29:11 +0300 Subject: [PATCH 11/38] Fixed comments. Added some bibtex information for the Hilbert R tree. --- .../tree/rectangle_tree/rectangle_tree.hpp | 29 +++++++++---------- .../rectangle_tree/rectangle_tree_impl.hpp | 5 +--- .../core/tree/rectangle_tree/typedef.hpp | 23 +++++++++++++-- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index 8c235224ce..0f2d9cc6e3 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -35,6 +35,8 @@ namespace tree /** Trees and tree-building procedures. */ { * @tparam SplitType The type of split to use when inserting points. * @tparam DescentType The heuristic to use when descending the tree to insert * points. + * @tparam AuxiliaryInformationType An auxiliary information contained + * in the node. This information depends on the type of the RectangleTree. */ template& relevels); /** - * Deletes a point in the tree. The point will be removed from the data - * matrix of the leaf node where it is store and the bounding rectangles will - * be updated. However, the point will be kept in the centeral dataset. (The + * Deletes a point from the treeand, updates the bounding rectangle. + * However, the point will be kept in the centeral dataset. (The * user may remove it from there if he wants, but he must not change the * indices of the other points.) Returns true if the point is successfully * removed and false if it is not. (ie. the point is not in the tree) @@ -239,10 +237,9 @@ class RectangleTree bool DeletePoint(const size_t point); /** - * Deletes a point in the tree, tracking levels. The point will be removed - * from the data matrix of the leaf node where it is store and the bounding - * rectangles will be updated. However, the point will be kept in the - * centeral dataset. (The user may remove it from there if he wants, but he + * Deletes a point from the tree, updates the bounding rectangle, + * tracking levels. However, the point will be kept in the centeral dataset. + * (The user may remove it from there if he wants, but he * must not change the indices of the other points.) Returns true if the point * is successfully removed and false if it is not. (ie. the point is not in * the tree) 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 b087f794e2..c11d62c136 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -237,7 +237,7 @@ void RectangleTree; /** - * The Hilbert R-tree, a variant of the R tree with an ordering along the Hilbert curve. This template typedef - * satisfies the TreeType policy API. + * The Hilbert R-tree, a variant of the R tree with an ordering along + * the Hilbert curve. This template typedef satisfies the TreeType policy API. * + * @code + * @inproceedings{kamel1994r, + * author = {Kamel, Ibrahim and Faloutsos, Christos}, + * title = {Hilbert R-tree: An Improved R-tree Using Fractals}, + * booktitle = {Proceedings of the 20th International Conference on Very Large Data Bases}, + * series = {VLDB '94}, + * year = {1994}, + * isbn = {1-55860-153-8}, + * pages = {500--509}, + * numpages = {10}, + * url = {http://dl.acm.org/citation.cfm?id=645920.673001}, + * acmid = {673001}, + * publisher = {Morgan Kaufmann Publishers Inc.}, + * address = {San Francisco, CA, USA} + * } + * @endcode + * + * @see @ref trees, RTree, DiscreteHilbertRTree */ - template using RecursiveHilbertRTreeAuxiliaryInformation = HilbertRTreeAuxiliaryInformation; From 6db6de598389d780d51fe19a1d8484f9f1071920 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Thu, 2 Jun 2016 15:53:45 -0300 Subject: [PATCH 12/38] Modify KNN/KFN to include Approximate Neighbor Search. --- .../methods/neighbor_search/kfn_main.cpp | 12 ++++- .../methods/neighbor_search/knn_main.cpp | 14 ++++-- .../neighbor_search/neighbor_search.hpp | 15 +++++++ .../neighbor_search/neighbor_search_impl.hpp | 32 +++++++++---- .../neighbor_search/neighbor_search_rules.hpp | 4 ++ .../neighbor_search_rules_impl.hpp | 10 ++++- .../methods/neighbor_search/ns_model.hpp | 17 ++++++- .../methods/neighbor_search/ns_model_impl.hpp | 45 +++++++++++++++---- .../sort_policies/furthest_neighbor_sort.hpp | 17 +++++++ .../sort_policies/nearest_neighbor_sort.hpp | 15 +++++++ 10 files changed, 158 insertions(+), 23 deletions(-) diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp index d6807400e7..a2fbf2eba9 100644 --- a/src/mlpack/methods/neighbor_search/kfn_main.cpp +++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp @@ -72,6 +72,8 @@ PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0); PARAM_FLAG("naive", "If true, O(n^2) naive mode is used for computation.", "N"); PARAM_FLAG("single_mode", "If true, single-tree search is used (as opposed to " "dual-tree search).", "s"); +PARAM_DOUBLE("epsilon", "If specified, will do approximate furthest neighbor " + "search with given relative error.", "e", 0); // Convenience typedef. typedef NSModel KFNModel; @@ -138,6 +140,12 @@ int main(int argc, char *argv[]) Log::Fatal << "Invalid leaf size: " << lsInt << ". Must be greater than 0." << endl; + // Sanity check on epsilon. + const double epsilon = CLI::GetParam("epsilon"); + if (epsilon < 0) + Log::Fatal << "Invalid epsilon: " << epsilon << ". Must be non-negative. " + << endl; + // We either have to load the reference data, or we have to load the model. NSModel kfn; const bool naive = CLI::HasParam("naive"); @@ -175,7 +183,8 @@ int main(int argc, char *argv[]) Log::Info << "Loaded reference data from '" << referenceFile << "' (" << referenceSet.n_rows << "x" << referenceSet.n_cols << ")." << endl; - kfn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode); + kfn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode, + epsilon); } else { @@ -191,6 +200,7 @@ int main(int argc, char *argv[]) kfn.SingleMode() = CLI::HasParam("single_mode"); kfn.Naive() = CLI::HasParam("naive"); kfn.LeafSize() = size_t(lsInt); + kfn.Epsilon() = epsilon; } // Perform search, if desired. diff --git a/src/mlpack/methods/neighbor_search/knn_main.cpp b/src/mlpack/methods/neighbor_search/knn_main.cpp index 4957e88ebe..880f5db90f 100644 --- a/src/mlpack/methods/neighbor_search/knn_main.cpp +++ b/src/mlpack/methods/neighbor_search/knn_main.cpp @@ -74,6 +74,8 @@ PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0); PARAM_FLAG("naive", "If true, O(n^2) naive mode is used for computation.", "N"); PARAM_FLAG("single_mode", "If true, single-tree search is used (as opposed to " "dual-tree search).", "S"); +PARAM_DOUBLE("epsilon", "If specified, will do approximate nearest neighbor " + "search with given relative error.", "e", 0); // Convenience typedef. typedef NSModel KNNModel; @@ -137,10 +139,14 @@ int main(int argc, char *argv[]) // Sanity check on leaf size. const int lsInt = CLI::GetParam("leaf_size"); if (lsInt < 1) - { Log::Fatal << "Invalid leaf size: " << lsInt << ". Must be greater " "than 0." << endl; - } + + // Sanity check on epsilon. + const double epsilon = CLI::GetParam("epsilon"); + if (epsilon < 0) + Log::Fatal << "Invalid epsilon: " << epsilon << ". Must be non-negative. " + << endl; // We either have to load the reference data, or we have to load the model. NSModel knn; @@ -180,7 +186,8 @@ int main(int argc, char *argv[]) << referenceSet.n_rows << " x " << referenceSet.n_cols << ")." << endl; - knn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode); + knn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode, + epsilon); } else { @@ -196,6 +203,7 @@ int main(int argc, char *argv[]) knn.SingleMode() = CLI::HasParam("single_mode"); knn.Naive() = CLI::HasParam("naive"); knn.LeafSize() = size_t(lsInt); + knn.Epsilon() = epsilon; } // Perform search, if desired. diff --git a/src/mlpack/methods/neighbor_search/neighbor_search.hpp b/src/mlpack/methods/neighbor_search/neighbor_search.hpp index 999f261c8f..f1acea458d 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search.hpp @@ -84,11 +84,13 @@ class NeighborSearch * dual-tree search). This overrides singleMode (if it is set to true). * @param singleMode If true, single-tree search will be used (as opposed to * dual-tree search). + * @param epsilon Relative approximate error (non-negative). * @param metric An optional instance of the MetricType class. */ NeighborSearch(const MatType& referenceSet, const bool naive = false, const bool singleMode = false, + const double epsilon = 0, const MetricType metric = MetricType()); /** @@ -108,11 +110,13 @@ class NeighborSearch * dual-tree search). This overrides singleMode (if it is set to true). * @param singleMode If true, single-tree search will be used (as opposed to * dual-tree search). + * @param epsilon Relative approximate error (non-negative). * @param metric An optional instance of the MetricType class. */ NeighborSearch(MatType&& referenceSet, const bool naive = false, const bool singleMode = false, + const double epsilon = 0, const MetricType metric = MetricType()); /** @@ -138,10 +142,12 @@ class NeighborSearch * @param referenceSet Set of reference points corresponding to referenceTree. * @param singleMode Whether single-tree computation should be used (as * opposed to dual-tree computation). + * @param epsilon Relative approximate error (non-negative). * @param metric Instantiated distance metric. */ NeighborSearch(Tree* referenceTree, const bool singleMode = false, + const double epsilon = 0, const MetricType metric = MetricType()); /** @@ -152,10 +158,12 @@ class NeighborSearch * @param naive Whether to use naive search. * @param singleMode Whether single-tree computation should be used (as * opposed to dual-tree computation). + * @param epsilon Relative approximate error (non-negative). * @param metric Instantiated metric. */ NeighborSearch(const bool naive = false, const bool singleMode = false, + const double epsilon = 0, const MetricType metric = MetricType()); @@ -270,6 +278,11 @@ class NeighborSearch //! Modify whether or not search is done in single-tree mode. bool& SingleMode() { return singleMode; } + //! Access the relative error to be considered in approximate search. + double Epsilon() const { return epsilon; } + //! Modify the relative error to be considered in approximate search. + double& Epsilon() { return epsilon; } + //! Access the reference dataset. const MatType& ReferenceSet() const { return *referenceSet; } @@ -294,6 +307,8 @@ class NeighborSearch bool naive; //! Indicates if single-tree search is being used (as opposed to dual-tree). bool singleMode; + //! Indicates the relative error to be considered in approximate search. + double epsilon; //! Instantiation of metric. MetricType metric; diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp index d86f5146e1..2d7468bbf7 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_impl.hpp @@ -75,6 +75,7 @@ NeighborSearch:: NeighborSearch(const MatType& referenceSetIn, const bool naive, const bool singleMode, + const double epsilon, const MetricType metric) : referenceTree(naive ? NULL : BuildTree(referenceSetIn, oldFromNewReferences)), @@ -83,12 +84,14 @@ NeighborSearch(const MatType& referenceSetIn, setOwner(false), naive(naive), singleMode(!naive && singleMode), // No single mode if naive. + epsilon(epsilon), metric(metric), baseCases(0), scores(0), treeNeedsReset(false) { - // Nothing to do. + if (epsilon < 0) + throw std::invalid_argument("epsilon must be non-negative"); } // Construct the object. @@ -103,6 +106,7 @@ NeighborSearch:: NeighborSearch(MatType&& referenceSetIn, const bool naive, const bool singleMode, + const double epsilon, const MetricType metric) : referenceTree(naive ? NULL : BuildTree(std::move(referenceSetIn), @@ -113,12 +117,14 @@ NeighborSearch(MatType&& referenceSetIn, setOwner(naive), naive(naive), singleMode(!naive && singleMode), + epsilon(epsilon), metric(metric), baseCases(0), scores(0), treeNeedsReset(false) { - // Nothing to do. + if (epsilon < 0) + throw std::invalid_argument("epsilon must be non-negative"); } // Construct the object. @@ -132,6 +138,7 @@ template:: NeighborSearch(Tree* referenceTree, const bool singleMode, + const double epsilon, const MetricType metric) : referenceTree(referenceTree), referenceSet(&referenceTree->Dataset()), @@ -139,12 +146,14 @@ NeighborSearch(Tree* referenceTree, setOwner(false), naive(false), singleMode(singleMode), + epsilon(epsilon), metric(metric), baseCases(0), scores(0), treeNeedsReset(false) { - // Nothing else to initialize. + if (epsilon < 0) + throw std::invalid_argument("epsilon must be non-negative"); } // Construct the object without a reference dataset. @@ -158,6 +167,7 @@ template:: NeighborSearch(const bool naive, const bool singleMode, + const double epsilon, const MetricType metric) : referenceTree(NULL), referenceSet(new MatType()), // Empty matrix. @@ -165,11 +175,14 @@ NeighborSearch:: setOwner(true), naive(naive), singleMode(singleMode), + epsilon(epsilon), metric(metric), baseCases(0), scores(0), treeNeedsReset(false) { + if (epsilon < 0) + throw std::invalid_argument("epsilon must be non-negative"); // Build the tree on the empty dataset, if necessary. if (!naive) { @@ -364,7 +377,8 @@ Search(const MatType& querySet, if (naive) { // Create the helper object for the tree traversal. - RuleType rules(*referenceSet, querySet, *neighborPtr, *distancePtr, metric); + RuleType rules(*referenceSet, querySet, *neighborPtr, *distancePtr, metric, + epsilon); // The naive brute-force traversal. for (size_t i = 0; i < querySet.n_cols; ++i) @@ -376,7 +390,8 @@ Search(const MatType& querySet, else if (singleMode) { // Create the helper object for the tree traversal. - RuleType rules(*referenceSet, querySet, *neighborPtr, *distancePtr, metric); + RuleType rules(*referenceSet, querySet, *neighborPtr, *distancePtr, metric, + epsilon); // Create the traverser. typename Tree::template SingleTreeTraverser traverser(rules); @@ -402,7 +417,7 @@ Search(const MatType& querySet, // Create the helper object for the tree traversal. RuleType rules(*referenceSet, queryTree->Dataset(), *neighborPtr, - *distancePtr, metric); + *distancePtr, metric, epsilon); // Create the traverser. TraversalType traverser(rules); @@ -527,7 +542,8 @@ Search(Tree* queryTree, // Create the helper object for the traversal. typedef NeighborSearchRules RuleType; - RuleType rules(*referenceSet, querySet, *neighborPtr, distances, metric); + RuleType rules(*referenceSet, querySet, *neighborPtr, distances, metric, + epsilon); // Create the traverser. TraversalType traverser(rules); @@ -598,7 +614,7 @@ Search(const size_t k, // Create the helper object for the traversal. typedef NeighborSearchRules RuleType; RuleType rules(*referenceSet, *referenceSet, *neighborPtr, *distancePtr, - metric, true /* don't return the same point as nearest neighbor */); + metric, epsilon, true /* don't return the same point as nearest neighbor */); if (naive) { diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_rules.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_rules.hpp index 474d22b005..47a7933dd0 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_rules.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_rules.hpp @@ -22,6 +22,7 @@ class NeighborSearchRules arma::Mat& neighbors, arma::mat& distances, MetricType& metric, + const double epsilon = 0, const bool sameSet = false); /** * Get the distance from the query point to the reference point. @@ -120,6 +121,9 @@ class NeighborSearchRules //! Denotes whether or not the reference and query sets are the same. bool sameSet; + //! Relative error to be considered in approximate search. + const double epsilon; + //! The last query point BaseCase() was called with. size_t lastQueryIndex; //! The last reference point BaseCase() was called with. diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp index cc2b957491..6edf103136 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp @@ -20,6 +20,7 @@ NeighborSearchRules::NeighborSearchRules( arma::Mat& neighbors, arma::mat& distances, MetricType& metric, + const double epsilon, const bool sameSet) : referenceSet(referenceSet), querySet(querySet), @@ -27,6 +28,7 @@ NeighborSearchRules::NeighborSearchRules( distances(distances), metric(metric), sameSet(sameSet), + epsilon(epsilon), lastQueryIndex(querySet.n_cols), lastReferenceIndex(referenceSet.n_cols), baseCases(0), @@ -112,7 +114,8 @@ inline double NeighborSearchRules::Score( } // Compare against the best k'th distance for this query point so far. - const double bestDistance = distances(distances.n_rows - 1, queryIndex); + double bestDistance = distances(distances.n_rows - 1, queryIndex); + bestDistance = SortPolicy::Relax(bestDistance, epsilon); return (SortPolicy::IsBetter(distance, bestDistance)) ? distance : DBL_MAX; } @@ -128,7 +131,8 @@ inline double NeighborSearchRules::Rescore( return oldScore; // Just check the score again against the distances. - const double bestDistance = distances(distances.n_rows - 1, queryIndex); + double bestDistance = distances(distances.n_rows - 1, queryIndex); + bestDistance = SortPolicy::Relax(bestDistance, epsilon); return (SortPolicy::IsBetter(oldScore, bestDistance)) ? oldScore : DBL_MAX; } @@ -419,6 +423,8 @@ inline double NeighborSearchRules:: queryNode.Stat().SecondBound() = bestDistance; queryNode.Stat().AuxBound() = auxDistance; + worstDistance = SortPolicy::Relax(worstDistance, epsilon); + if (SortPolicy::IsBetter(worstDistance, bestDistance)) return worstDistance; else diff --git a/src/mlpack/methods/neighbor_search/ns_model.hpp b/src/mlpack/methods/neighbor_search/ns_model.hpp index d87549e920..db3331a3e4 100644 --- a/src/mlpack/methods/neighbor_search/ns_model.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model.hpp @@ -177,6 +177,16 @@ class NaiveVisitor : public boost::static_visitor bool& operator()(NSType *ns) const; }; +/** + * EpsilonVisitor exposes the Epsilon method of the given NSType. + */ +class EpsilonVisitor : public boost::static_visitor +{ + public: + template + double& operator()(NSType *ns) const; +}; + /** * ReferenceSetVisitor exposes the referenceSet of the given NSType. */ @@ -266,6 +276,10 @@ class NSModel bool Naive() const; bool& Naive(); + //! Expose Epsilon. + double Epsilon() const; + double& Epsilon(); + //! Expose leafSize. size_t LeafSize() const { return leafSize; } size_t& LeafSize() { return leafSize; } @@ -282,7 +296,8 @@ class NSModel void BuildModel(arma::mat&& referenceSet, const size_t leafSize, const bool naive, - const bool singleMode); + const bool singleMode, + const double epsilon = 0); //! Perform neighbor search. The query set will be reordered. void Search(arma::mat&& querySet, diff --git a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp index 5ed97721cd..bbca3d2a3b 100644 --- a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp @@ -185,6 +185,15 @@ bool& NaiveVisitor::operator()(NSType* ns) const throw std::runtime_error("no neighbor search model initialized"); } +//! Expose the Epsilon method of the given NSType. +template +double& EpsilonVisitor::operator()(NSType* ns) const +{ + if (ns) + return ns->Epsilon(); + throw std::runtime_error("no neighbor search model initialized"); +} + //! Expose the referenceSet of the given NSType. template const arma::mat& ReferenceSetVisitor::operator()(NSType* ns) const @@ -293,12 +302,25 @@ bool& NSModel::Naive() return boost::apply_visitor(NaiveVisitor(), nSearch); } +template +double NSModel::Epsilon() const +{ + return boost::apply_visitor(EpsilonVisitor(), nSearch); +} + +template +double& NSModel::Epsilon() +{ + return boost::apply_visitor(EpsilonVisitor(), nSearch); +} + //! Build the reference tree. template void NSModel::BuildModel(arma::mat&& referenceSet, const size_t leafSize, const bool naive, - const bool singleMode) + const bool singleMode, + const double epsilon) { // Initialize random basis if necessary. if (randomBasis) @@ -348,23 +370,26 @@ void NSModel::BuildModel(arma::mat&& referenceSet, switch (treeType) { case KD_TREE: - nSearch = new NSType(naive, singleMode); + nSearch = new NSType(naive, singleMode, + epsilon); break; case COVER_TREE: nSearch = new NSType(naive, - singleMode); + singleMode, epsilon); break; case R_TREE: - nSearch = new NSType(naive, singleMode); + nSearch = new NSType(naive, singleMode, epsilon); break; case R_STAR_TREE: - nSearch = new NSType(naive, singleMode); + nSearch = new NSType(naive, singleMode, + epsilon); break; case BALL_TREE: - nSearch = new NSType(naive, singleMode); + nSearch = new NSType(naive, singleMode, + epsilon); break; case X_TREE: - nSearch = new NSType(naive, singleMode); + nSearch = new NSType(naive, singleMode, epsilon); break; } @@ -389,7 +414,11 @@ void NSModel::Search(arma::mat&& querySet, if (randomBasis) querySet = q * querySet; - Log::Info << "Searching for " << k << " nearest neighbors with "; + Log::Info << "Searching for " << k; + if (Epsilon() != 0) + Log::Info << " approximate nearest neighbors (e=" << Epsilon() << ") with "; + else + Log::Info << " nearest neighbors with "; if (!Naive() && !SingleMode()) Log::Info << "dual-tree " << TreeName() << " search..." << std::endl; else if (!Naive()) diff --git a/src/mlpack/methods/neighbor_search/sort_policies/furthest_neighbor_sort.hpp b/src/mlpack/methods/neighbor_search/sort_policies/furthest_neighbor_sort.hpp index 87a72622e8..a69c167921 100644 --- a/src/mlpack/methods/neighbor_search/sort_policies/furthest_neighbor_sort.hpp +++ b/src/mlpack/methods/neighbor_search/sort_policies/furthest_neighbor_sort.hpp @@ -145,6 +145,23 @@ class FurthestNeighborSort */ static inline double CombineWorst(const double a, const double b) { return std::max(a - b, 0.0); } + + /** + * Return the given value relaxed. + * + * @param value Value to relax. + * @param epsilon Relative error (non-negative). + * + * @return double Value relaxed. + */ + static inline double Relax(const double value, const double epsilon) + { + if (value == 0) + return 0; + if (value == DBL_MAX || epsilon >= 1) + return DBL_MAX; + return (1 / (1 - epsilon)) * value; + } }; } // namespace neighbor diff --git a/src/mlpack/methods/neighbor_search/sort_policies/nearest_neighbor_sort.hpp b/src/mlpack/methods/neighbor_search/sort_policies/nearest_neighbor_sort.hpp index f57635a2a5..42a08b0641 100644 --- a/src/mlpack/methods/neighbor_search/sort_policies/nearest_neighbor_sort.hpp +++ b/src/mlpack/methods/neighbor_search/sort_policies/nearest_neighbor_sort.hpp @@ -150,6 +150,21 @@ class NearestNeighborSort return DBL_MAX; return a + b; } + + /** + * Return the given value relaxed. + * + * @param value Value to relax. + * @param epsilon Relative error (non-negative). + * + * @return double Value relaxed. + */ + static inline double Relax(const double value, const double epsilon) + { + if (value == DBL_MAX) + return DBL_MAX; + return (1 / (1 + epsilon)) * value; + } }; } // namespace neighbor From 0f65abf878cf37fa0fb6e8c04bd2b85524355320 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Mon, 6 Jun 2016 13:35:52 -0300 Subject: [PATCH 13/38] Add tests for approximate Nearest Neighbor Search. --- src/mlpack/tests/CMakeLists.txt | 1 + src/mlpack/tests/aknn_test.cpp | 397 ++++++++++++++++++++++++++++++++ 2 files changed, 398 insertions(+) create mode 100644 src/mlpack/tests/aknn_test.cpp diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index bd204f7e0d..1d5f61bb25 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(mlpack_test kmeans_test.cpp knn_test.cpp krann_search_test.cpp + aknn_test.cpp lars_test.cpp lbfgs_test.cpp lin_alg_test.cpp diff --git a/src/mlpack/tests/aknn_test.cpp b/src/mlpack/tests/aknn_test.cpp new file mode 100644 index 0000000000..0fab49b72c --- /dev/null +++ b/src/mlpack/tests/aknn_test.cpp @@ -0,0 +1,397 @@ +/** + * @file aknn_test.cpp + * + * Test file for KNN class with different values of epsilon. + */ +#include +#include +#include +#include +#include +#include +#include +#include "old_boost_test_definitions.hpp" + +using namespace mlpack; +using namespace mlpack::neighbor; +using namespace mlpack::tree; +using namespace mlpack::metric; +using namespace mlpack::bound; + +BOOST_AUTO_TEST_SUITE(AKNNTest); + +/** + * Test the dual-tree nearest-neighbors method with different values for + * epsilon. This uses both a query and reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(DualTreeVsNaive1) +{ + arma::mat dataset; + + if (!data::Load("test_data_3_1000.csv", dataset)) + BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); + + KNN naive(dataset, true); + arma::Mat neighborsNaive; + arma::mat distancesNaive; + naive.Search(dataset, 15, neighborsNaive, distancesNaive); + + for (size_t c = 0; c < 4; c++) + { + KNN* knn; + double epsilon; + + switch (c) + { + case 0: // Use the dual-tree method with e=0.02. + epsilon = 0.02; + break; + case 1: // Use the dual-tree method with e=0.05. + epsilon = 0.05; + break; + case 2: // Use the dual-tree method with e=0.10. + epsilon = 0.10; + break; + case 3: // Use the dual-tree method with e=0.20. + epsilon = 0.20; + break; + } + + knn = new KNN(dataset, false, false, epsilon); + + // Now perform the actual calculation. + arma::Mat neighborsTree; + arma::mat distancesTree; + knn->Search(dataset, 15, neighborsTree, distancesTree); + + for (size_t i = 0; i < neighborsTree.n_elem; i++) + BOOST_REQUIRE_CLOSE(distancesTree(i), distancesNaive(i), epsilon * 100); + + // Clean the memory. + delete knn; + } +} + +/** + * Test the dual-tree nearest-neighbors method with the naive method. This uses + * only a reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(DualTreeVsNaive2) +{ + arma::mat dataset; + + if (!data::Load("test_data_3_1000.csv", dataset)) + BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); + + KNN naive(dataset, true); + arma::Mat neighborsNaive; + arma::mat distancesNaive; + naive.Search(15, neighborsNaive, distancesNaive); + + KNN knn(dataset, false, false, 0.05); + arma::Mat neighborsTree; + arma::mat distancesTree; + knn.Search(15, neighborsTree, distancesTree); + + for (size_t i = 0; i < neighborsTree.n_elem; i++) + BOOST_REQUIRE_CLOSE(distancesTree(i), distancesNaive(i), 5); +} + +/** + * Test the single-tree nearest-neighbors method with the naive method. This + * uses only a reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(SingleTreeVsNaive) +{ + arma::mat dataset; + + if (!data::Load("test_data_3_1000.csv", dataset)) + BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); + + KNN naive(dataset, true); + arma::Mat neighborsNaive; + arma::mat distancesNaive; + naive.Search(15, neighborsNaive, distancesNaive); + + KNN knn(dataset, false, true, 0.05); + arma::Mat neighborsTree; + arma::mat distancesTree; + knn.Search(15, neighborsTree, distancesTree); + + for (size_t i = 0; i < neighborsTree.n_elem; i++) + BOOST_REQUIRE_CLOSE(distancesTree[i], distancesNaive[i], 5); +} + +/** + * Test the cover tree single-tree nearest-neighbors method against the naive + * method. This uses only a random reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(SingleCoverTreeTest) +{ + arma::mat data; + data.randu(75, 1000); // 75 dimensional, 1000 points. + + KNN naive(data, true); + arma::Mat naiveNeighbors; + arma::mat naiveDistances; + naive.Search(data, 15, naiveNeighbors, naiveDistances); + + StandardCoverTree, + arma::mat> tree(data); + + NeighborSearch, arma::mat, StandardCoverTree> + coverTreeSearch(&tree, true, 0.05); + + arma::Mat coverTreeNeighbors; + arma::mat coverTreeDistances; + coverTreeSearch.Search(data, 15, coverTreeNeighbors, coverTreeDistances); + + for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) + BOOST_REQUIRE_CLOSE(coverTreeDistances[i], naiveDistances[i], 5); +} + +/** + * Test the cover tree dual-tree nearest neighbors method against the naive + * method. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(DualCoverTreeTest) +{ + arma::mat dataset; + data::Load("test_data_3_1000.csv", dataset); + + KNN naive(dataset, true); + arma::Mat naiveNeighbors; + arma::mat naiveDistances; + naive.Search(dataset, 15, naiveNeighbors, naiveDistances); + + StandardCoverTree, + arma::mat> referenceTree(dataset); + + NeighborSearch coverTreeSearch(&referenceTree, false, 0.05); + + arma::Mat coverNeighbors; + arma::mat coverDistances; + coverTreeSearch.Search(&referenceTree, 15, coverNeighbors, coverDistances); + + for (size_t i = 0; i < coverNeighbors.n_elem; ++i) + BOOST_REQUIRE_CLOSE(coverDistances[i], naiveDistances[i], 5); +} + +/** + * Test the ball tree single-tree nearest-neighbors method against the naive + * method. This uses only a random reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(SingleBallTreeTest) +{ + arma::mat data; + data.randu(50, 300); // 50 dimensional, 300 points. + + KNN naive(data, true); + arma::Mat naiveNeighbors; + arma::mat naiveDistances; + naive.Search(data, 15, naiveNeighbors, naiveDistances); + + NeighborSearch + ballTreeSearch(data, false, true, 0.05); + + arma::Mat ballNeighbors; + arma::mat ballDistances; + ballTreeSearch.Search(data, 15, ballNeighbors, ballDistances); + + for (size_t i = 0; i < ballNeighbors.n_elem; ++i) + BOOST_REQUIRE_CLOSE(ballDistances(i), naiveDistances(i), 5); +} + +/** + * Test the ball tree dual-tree nearest neighbors method against the naive + * method. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(DualBallTreeTest) +{ + arma::mat dataset; + data::Load("test_data_3_1000.csv", dataset); + + KNN naive(dataset, true); + arma::Mat naiveNeighbors; + arma::mat naiveDistances; + naive.Search(15, naiveNeighbors, naiveDistances); + + NeighborSearch + ballTreeSearch(dataset, false, false, 0.05); + arma::Mat ballNeighbors; + arma::mat ballDistances; + ballTreeSearch.Search(15, ballNeighbors, ballDistances); + + for (size_t i = 0; i < ballNeighbors.n_elem; ++i) + BOOST_REQUIRE_CLOSE(ballDistances(i), naiveDistances(i), 5); +} + +// Make sure sparse nearest neighbors works with kd trees. +BOOST_AUTO_TEST_CASE(SparseKNNKDTreeTest) +{ + // The dimensionality of these datasets must be high so that the probability + // of a completely empty point is very low. In this case, with dimensionality + // 70, the probability of all 70 dimensions being zero is 0.8^70 = 1.65e-7 in + // the reference set and 0.9^70 = 6.27e-4 in the query set. + arma::sp_mat queryDataset; + queryDataset.sprandu(70, 200, 0.2); + arma::sp_mat referenceDataset; + referenceDataset.sprandu(70, 500, 0.1); + arma::mat denseQuery(queryDataset); + arma::mat denseReference(referenceDataset); + + typedef NeighborSearch SparseKNN; + + SparseKNN a(referenceDataset, false, false, 0.05); + KNN naive(denseReference, true); + + arma::mat sparseDistances; + arma::Mat sparseNeighbors; + a.Search(queryDataset, 10, sparseNeighbors, sparseDistances); + + arma::mat naiveDistances; + arma::Mat naiveNeighbors; + naive.Search(denseQuery, 10, naiveNeighbors, naiveDistances); + + for (size_t i = 0; i < naiveNeighbors.n_cols; ++i) + for (size_t j = 0; j < naiveNeighbors.n_rows; ++j) + BOOST_REQUIRE_CLOSE(naiveDistances(j, i), sparseDistances(j, i), 5); +} + +// Ensure that we can build an NSModel and get correct +// results. +BOOST_AUTO_TEST_CASE(KNNModelTest) +{ + typedef NSModel KNNModel; + + arma::mat queryData = arma::randu(10, 50); + arma::mat referenceData = arma::randu(10, 200); + + // Build all the possible models. + 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); + models[3] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false); + models[4] = KNNModel(KNNModel::TreeTypes::R_TREE, true); + 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::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 < 3; ++j) + { + // Get a baseline. + KNN knn(referenceData); + arma::Mat baselineNeighbors; + arma::mat baselineDistances; + knn.Search(queryData, 3, baselineNeighbors, baselineDistances); + + 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); + arma::mat queryCopy(queryData); + if (j == 0) + models[i].BuildModel(std::move(referenceCopy), 20, false, false, 0.05); + if (j == 1) + models[i].BuildModel(std::move(referenceCopy), 20, false, true, 0.05); + if (j == 2) + models[i].BuildModel(std::move(referenceCopy), 20, true, false); + + arma::Mat neighbors; + arma::mat distances; + + models[i].Search(std::move(queryCopy), 3, neighbors, distances); + + BOOST_REQUIRE_EQUAL(neighbors.n_rows, baselineNeighbors.n_rows); + BOOST_REQUIRE_EQUAL(neighbors.n_cols, baselineNeighbors.n_cols); + BOOST_REQUIRE_EQUAL(neighbors.n_elem, baselineNeighbors.n_elem); + BOOST_REQUIRE_EQUAL(distances.n_rows, baselineDistances.n_rows); + BOOST_REQUIRE_EQUAL(distances.n_cols, baselineDistances.n_cols); + BOOST_REQUIRE_EQUAL(distances.n_elem, baselineDistances.n_elem); + for (size_t k = 0; k < distances.n_elem; ++k) + BOOST_REQUIRE_CLOSE(distances[k], baselineDistances[k], 5); + } + } +} + +// Ensure that we can build an NSModel and get correct +// results, in the case where the reference set is the same as the query set. +BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) +{ + typedef NSModel KNNModel; + + arma::mat referenceData = arma::randu(10, 200); + + // Build all the possible models. + 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); + models[3] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false); + models[4] = KNNModel(KNNModel::TreeTypes::R_TREE, true); + 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::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 < 3; ++j) + { + // Get a baseline. + KNN knn(referenceData); + arma::Mat baselineNeighbors; + arma::mat baselineDistances; + knn.Search(3, baselineNeighbors, baselineDistances); + + for (size_t i = 0; i < 12; ++i) + { + // We only have a std::move() constructor... so copy the data. + arma::mat referenceCopy(referenceData); + if (j == 0) + models[i].BuildModel(std::move(referenceCopy), 20, false, false, 0.05); + if (j == 1) + models[i].BuildModel(std::move(referenceCopy), 20, false, true, 0.05); + if (j == 2) + models[i].BuildModel(std::move(referenceCopy), 20, true, false); + + arma::Mat neighbors; + arma::mat distances; + + models[i].Search(3, neighbors, distances); + + BOOST_REQUIRE_EQUAL(neighbors.n_rows, baselineNeighbors.n_rows); + BOOST_REQUIRE_EQUAL(neighbors.n_cols, baselineNeighbors.n_cols); + BOOST_REQUIRE_EQUAL(neighbors.n_elem, baselineNeighbors.n_elem); + BOOST_REQUIRE_EQUAL(distances.n_rows, baselineDistances.n_rows); + BOOST_REQUIRE_EQUAL(distances.n_cols, baselineDistances.n_cols); + BOOST_REQUIRE_EQUAL(distances.n_elem, baselineDistances.n_elem); + for (size_t k = 0; k < distances.n_elem; ++k) + BOOST_REQUIRE_CLOSE(distances[k], baselineDistances[k], 5); + } + } +} + +BOOST_AUTO_TEST_SUITE_END(); From c64bdba5ca5fe578904e6b8ebe54cc9dda1562e3 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Mon, 6 Jun 2016 13:37:32 -0300 Subject: [PATCH 14/38] Add tests for approximate Furthest Neighbor Search. --- src/mlpack/tests/CMakeLists.txt | 1 + src/mlpack/tests/akfn_test.cpp | 241 ++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 src/mlpack/tests/akfn_test.cpp diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index 1d5f61bb25..967edeee4e 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(mlpack_test kernel_pca_test.cpp kernel_traits_test.cpp kfn_test.cpp + akfn_test.cpp kmeans_test.cpp knn_test.cpp krann_search_test.cpp diff --git a/src/mlpack/tests/akfn_test.cpp b/src/mlpack/tests/akfn_test.cpp new file mode 100644 index 0000000000..59178c57da --- /dev/null +++ b/src/mlpack/tests/akfn_test.cpp @@ -0,0 +1,241 @@ +/** + * @file akfn_test.cpp + * + * Tests for KFN (k-furthest-neighbors) with different values of epsilon. + */ +#include +#include +#include +#include +#include "old_boost_test_definitions.hpp" + +using namespace mlpack; +using namespace mlpack::neighbor; +using namespace mlpack::tree; +using namespace mlpack::metric; +using namespace mlpack::bound; + +BOOST_AUTO_TEST_SUITE(AKFNTest); + +/** + * Test the dual-tree furthest-neighbors method with different values for + * epsilon. This uses both a query and reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(DualTreeVsNaive1) +{ + arma::mat dataset; + + if (!data::Load("test_data_3_1000.csv", dataset)) + BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); + + KFN naive(dataset, true); + arma::Mat neighborsNaive; + arma::mat distancesNaive; + naive.Search(dataset, 15, neighborsNaive, distancesNaive); + + for (size_t c = 0; c < 4; c++) + { + KFN* kfn; + double epsilon; + + switch (c) + { + case 0: // Use the dual-tree method with e=0.02. + epsilon = 0.02; + break; + case 1: // Use the dual-tree method with e=0.05. + epsilon = 0.05; + break; + case 2: // Use the dual-tree method with e=0.10. + epsilon = 0.10; + break; + case 3: // Use the dual-tree method with e=0.20. + epsilon = 0.20; + break; + } + + kfn = new KFN(dataset, false, false, epsilon); + + // Now perform the actual calculation. + arma::Mat neighborsTree; + arma::mat distancesTree; + kfn->Search(dataset, 15, neighborsTree, distancesTree); + + for (size_t i = 0; i < neighborsTree.n_elem; i++) + BOOST_REQUIRE_CLOSE(distancesTree(i), distancesNaive(i), epsilon * 100); + + // Clean the memory. + delete kfn; + } +} + +/** + * Test the dual-tree furthest-neighbors method with the naive method. This + * uses only a reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(DualTreeVsNaive2) +{ + arma::mat dataset; + + if (!data::Load("test_data_3_1000.csv", dataset)) + BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); + + KFN naive(dataset, true); + arma::Mat neighborsNaive; + arma::mat distancesNaive; + naive.Search(15, neighborsNaive, distancesNaive); + + KFN kfn(dataset, false, false, 0.05); + arma::Mat neighborsTree; + arma::mat distancesTree; + kfn.Search(15, neighborsTree, distancesTree); + + for (size_t i = 0; i < neighborsTree.n_elem; i++) + BOOST_REQUIRE_CLOSE(distancesTree[i], distancesNaive[i], 5); +} + +/** + * Test the single-tree furthest-neighbors method with the naive method. This + * uses only a reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(SingleTreeVsNaive) +{ + arma::mat dataset; + + if (!data::Load("test_data_3_1000.csv", dataset)) + BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); + + KFN naive(dataset, true); + arma::Mat neighborsNaive; + arma::mat distancesNaive; + naive.Search(15, neighborsNaive, distancesNaive); + + KFN kfn(dataset, false, true, 0.05); + arma::Mat neighborsTree; + arma::mat distancesTree; + kfn.Search(15, neighborsTree, distancesTree); + + for (size_t i = 0; i < neighborsTree.n_elem; i++) + BOOST_REQUIRE_CLOSE(distancesTree[i], distancesNaive[i], 5); +} + +/** + * Test the cover tree single-tree furthest-neighbors method against the naive + * method. This uses only a random reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(SingleCoverTreeTest) +{ + arma::mat data; + data.randu(75, 1000); // 75 dimensional, 1000 points. + + KFN naive(data, true); + arma::Mat naiveNeighbors; + arma::mat naiveDistances; + naive.Search(data, 15, naiveNeighbors, naiveDistances); + + StandardCoverTree, + arma::mat> tree(data); + + NeighborSearch, arma::mat, StandardCoverTree> + coverTreeSearch(&tree, true, 0.05); + + arma::Mat coverTreeNeighbors; + arma::mat coverTreeDistances; + coverTreeSearch.Search(data, 15, coverTreeNeighbors, coverTreeDistances); + + for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) + BOOST_REQUIRE_CLOSE(coverTreeDistances[i], naiveDistances[i], 5); +} + +/** + * Test the cover tree dual-tree furthest neighbors method against the naive + * method. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(DualCoverTreeTest) +{ + arma::mat dataset; + data::Load("test_data_3_1000.csv", dataset); + + KFN naive(dataset, true); + arma::Mat naiveNeighbors; + arma::mat naiveDistances; + naive.Search(dataset, 15, naiveNeighbors, naiveDistances); + + StandardCoverTree, + arma::mat> referenceTree(dataset); + + NeighborSearch, arma::mat, StandardCoverTree> + coverTreeSearch(&referenceTree, false, 0.05); + + arma::Mat coverTreeNeighbors; + arma::mat coverTreeDistances; + coverTreeSearch.Search(dataset, 15, coverTreeNeighbors, coverTreeDistances); + + for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) + BOOST_REQUIRE_CLOSE(coverTreeDistances[i], naiveDistances[i], 5); +} + +/** + * Test the ball tree single-tree furthest-neighbors method against the naive + * method. This uses only a random reference dataset. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(SingleBallTreeTest) +{ + arma::mat data; + data.randu(75, 1000); // 75 dimensional, 1000 points. + + KFN naive(data, true); + arma::Mat naiveNeighbors; + arma::mat naiveDistances; + naive.Search(data, 15, naiveNeighbors, naiveDistances); + + NeighborSearch + ballTreeSearch(data, false, true, 0.05); + + arma::Mat ballNeighbors; + arma::mat ballDistances; + ballTreeSearch.Search(data, 15, ballNeighbors, ballDistances); + + for (size_t i = 0; i < ballNeighbors.n_elem; ++i) + BOOST_REQUIRE_CLOSE(ballDistances(i), naiveDistances(i), 5); +} + +/** + * Test the ball tree dual-tree furthest neighbors method against the naive + * method. + * + * Errors are produced if the results are not according to relative error. + */ +BOOST_AUTO_TEST_CASE(DualBallTreeTest) +{ + arma::mat dataset; + data::Load("test_data_3_1000.csv", dataset); + + KFN naive(dataset, true); + arma::Mat naiveNeighbors; + arma::mat naiveDistances; + naive.Search(15, naiveNeighbors, naiveDistances); + + NeighborSearch + ballTreeSearch(dataset, false, false, 0.05); + arma::Mat ballNeighbors; + arma::mat ballDistances; + ballTreeSearch.Search(15, ballNeighbors, ballDistances); + + for (size_t i = 0; i < ballNeighbors.n_elem; ++i) + BOOST_REQUIRE_CLOSE(ballDistances(i), naiveDistances(i), 5); +} + +BOOST_AUTO_TEST_SUITE_END(); From 5b99eda7868665906459b21640014139831c5ca2 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Mon, 6 Jun 2016 15:04:28 -0300 Subject: [PATCH 15/38] Properly check relative error. BOOST_REQUIRE_CLOSE_FRACTION(VAL, REF, ERR) requires: abs(VAL - REF) <= ERR * REF && abs(VAL - REF) <= ERR * VAL REQUIRE_RELATIVE_ERR(VAL, REF, ERR) only requires: abs(VAL - REF) <= ERR * REF --- src/mlpack/tests/akfn_test.cpp | 14 ++++++------- src/mlpack/tests/aknn_test.cpp | 20 +++++++++---------- .../tests/old_boost_test_definitions.hpp | 5 +++++ 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/mlpack/tests/akfn_test.cpp b/src/mlpack/tests/akfn_test.cpp index 59178c57da..350e41e9bf 100644 --- a/src/mlpack/tests/akfn_test.cpp +++ b/src/mlpack/tests/akfn_test.cpp @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(DualTreeVsNaive1) kfn->Search(dataset, 15, neighborsTree, distancesTree); for (size_t i = 0; i < neighborsTree.n_elem; i++) - BOOST_REQUIRE_CLOSE(distancesTree(i), distancesNaive(i), epsilon * 100); + REQUIRE_RELATIVE_ERR(distancesTree(i), distancesNaive(i), epsilon); // Clean the memory. delete kfn; @@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(DualTreeVsNaive2) kfn.Search(15, neighborsTree, distancesTree); for (size_t i = 0; i < neighborsTree.n_elem; i++) - BOOST_REQUIRE_CLOSE(distancesTree[i], distancesNaive[i], 5); + REQUIRE_RELATIVE_ERR(distancesTree[i], distancesNaive[i], 0.05); } /** @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(SingleTreeVsNaive) kfn.Search(15, neighborsTree, distancesTree); for (size_t i = 0; i < neighborsTree.n_elem; i++) - BOOST_REQUIRE_CLOSE(distancesTree[i], distancesNaive[i], 5); + REQUIRE_RELATIVE_ERR(distancesTree[i], distancesNaive[i], 0.05); } /** @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(SingleCoverTreeTest) coverTreeSearch.Search(data, 15, coverTreeNeighbors, coverTreeDistances); for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) - BOOST_REQUIRE_CLOSE(coverTreeDistances[i], naiveDistances[i], 5); + REQUIRE_RELATIVE_ERR(coverTreeDistances[i], naiveDistances[i], 0.05); } /** @@ -182,7 +182,7 @@ BOOST_AUTO_TEST_CASE(DualCoverTreeTest) coverTreeSearch.Search(dataset, 15, coverTreeNeighbors, coverTreeDistances); for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) - BOOST_REQUIRE_CLOSE(coverTreeDistances[i], naiveDistances[i], 5); + REQUIRE_RELATIVE_ERR(coverTreeDistances[i], naiveDistances[i], 0.05); } /** @@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(SingleBallTreeTest) ballTreeSearch.Search(data, 15, ballNeighbors, ballDistances); for (size_t i = 0; i < ballNeighbors.n_elem; ++i) - BOOST_REQUIRE_CLOSE(ballDistances(i), naiveDistances(i), 5); + REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); } /** @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(DualBallTreeTest) ballTreeSearch.Search(15, ballNeighbors, ballDistances); for (size_t i = 0; i < ballNeighbors.n_elem; ++i) - BOOST_REQUIRE_CLOSE(ballDistances(i), naiveDistances(i), 5); + REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); } BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/mlpack/tests/aknn_test.cpp b/src/mlpack/tests/aknn_test.cpp index 0fab49b72c..be14bf1fce 100644 --- a/src/mlpack/tests/aknn_test.cpp +++ b/src/mlpack/tests/aknn_test.cpp @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(DualTreeVsNaive1) knn->Search(dataset, 15, neighborsTree, distancesTree); for (size_t i = 0; i < neighborsTree.n_elem; i++) - BOOST_REQUIRE_CLOSE(distancesTree(i), distancesNaive(i), epsilon * 100); + REQUIRE_RELATIVE_ERR(distancesTree(i), distancesNaive(i), epsilon); // Clean the memory. delete knn; @@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(DualTreeVsNaive2) knn.Search(15, neighborsTree, distancesTree); for (size_t i = 0; i < neighborsTree.n_elem; i++) - BOOST_REQUIRE_CLOSE(distancesTree(i), distancesNaive(i), 5); + REQUIRE_RELATIVE_ERR(distancesTree(i), distancesNaive(i), 0.05); } /** @@ -125,7 +125,7 @@ BOOST_AUTO_TEST_CASE(SingleTreeVsNaive) knn.Search(15, neighborsTree, distancesTree); for (size_t i = 0; i < neighborsTree.n_elem; i++) - BOOST_REQUIRE_CLOSE(distancesTree[i], distancesNaive[i], 5); + REQUIRE_RELATIVE_ERR(distancesTree[i], distancesNaive[i], 0.05); } /** @@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE(SingleCoverTreeTest) coverTreeSearch.Search(data, 15, coverTreeNeighbors, coverTreeDistances); for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) - BOOST_REQUIRE_CLOSE(coverTreeDistances[i], naiveDistances[i], 5); + REQUIRE_RELATIVE_ERR(coverTreeDistances[i], naiveDistances[i], 0.05); } /** @@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE(DualCoverTreeTest) coverTreeSearch.Search(&referenceTree, 15, coverNeighbors, coverDistances); for (size_t i = 0; i < coverNeighbors.n_elem; ++i) - BOOST_REQUIRE_CLOSE(coverDistances[i], naiveDistances[i], 5); + REQUIRE_RELATIVE_ERR(coverDistances[i], naiveDistances[i], 0.05); } /** @@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(SingleBallTreeTest) ballTreeSearch.Search(data, 15, ballNeighbors, ballDistances); for (size_t i = 0; i < ballNeighbors.n_elem; ++i) - BOOST_REQUIRE_CLOSE(ballDistances(i), naiveDistances(i), 5); + REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); } /** @@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE(DualBallTreeTest) ballTreeSearch.Search(15, ballNeighbors, ballDistances); for (size_t i = 0; i < ballNeighbors.n_elem; ++i) - BOOST_REQUIRE_CLOSE(ballDistances(i), naiveDistances(i), 5); + REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); } // Make sure sparse nearest neighbors works with kd trees. @@ -271,7 +271,7 @@ BOOST_AUTO_TEST_CASE(SparseKNNKDTreeTest) for (size_t i = 0; i < naiveNeighbors.n_cols; ++i) for (size_t j = 0; j < naiveNeighbors.n_rows; ++j) - BOOST_REQUIRE_CLOSE(naiveDistances(j, i), sparseDistances(j, i), 5); + REQUIRE_RELATIVE_ERR(sparseDistances(j, i), naiveDistances(j, i), 0.05); } // Ensure that we can build an NSModel and get correct @@ -330,7 +330,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) BOOST_REQUIRE_EQUAL(distances.n_cols, baselineDistances.n_cols); BOOST_REQUIRE_EQUAL(distances.n_elem, baselineDistances.n_elem); for (size_t k = 0; k < distances.n_elem; ++k) - BOOST_REQUIRE_CLOSE(distances[k], baselineDistances[k], 5); + REQUIRE_RELATIVE_ERR(distances[k], baselineDistances[k], 0.05); } } } @@ -389,7 +389,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) BOOST_REQUIRE_EQUAL(distances.n_cols, baselineDistances.n_cols); BOOST_REQUIRE_EQUAL(distances.n_elem, baselineDistances.n_elem); for (size_t k = 0; k < distances.n_elem; ++k) - BOOST_REQUIRE_CLOSE(distances[k], baselineDistances[k], 5); + REQUIRE_RELATIVE_ERR(distances[k], baselineDistances[k], 0.05); } } } diff --git a/src/mlpack/tests/old_boost_test_definitions.hpp b/src/mlpack/tests/old_boost_test_definitions.hpp index 9d98c0b3ed..1586f6272c 100644 --- a/src/mlpack/tests/old_boost_test_definitions.hpp +++ b/src/mlpack/tests/old_boost_test_definitions.hpp @@ -35,4 +35,9 @@ #endif +// Require the approximation L to be within a relative error of E respect to the +// actual value R. +#define REQUIRE_RELATIVE_ERR( L, R, E ) \ + BOOST_REQUIRE_LE( abs((R) - (L)), (E) * (R)) + #endif From 44b90f2c2565919c73c0870324c3d53ddbf153e0 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Tue, 7 Jun 2016 10:51:51 -0300 Subject: [PATCH 16/38] Update some comments/info. --- .../neighbor_search/neighbor_search_rules_impl.hpp | 4 ++-- src/mlpack/methods/neighbor_search/ns_model_impl.hpp | 10 ++++++---- .../sort_policies/furthest_neighbor_sort.cpp | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp b/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp index 6edf103136..24f94856f5 100644 --- a/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp +++ b/src/mlpack/methods/neighbor_search/neighbor_search_rules_impl.hpp @@ -1,8 +1,8 @@ /** - * @file nearest_neighbor_rules_impl.hpp + * @file neighbor_search_rules_impl.hpp * @author Ryan Curtin * - * Implementation of NearestNeighborRules. + * Implementation of NeighborSearchRules. */ #ifndef MLPACK_METHODS_NEIGHBOR_SEARCH_NEAREST_NEIGHBOR_RULES_IMPL_HPP #define MLPACK_METHODS_NEIGHBOR_SEARCH_NEAREST_NEIGHBOR_RULES_IMPL_HPP diff --git a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp index bbca3d2a3b..0a705626a6 100644 --- a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp @@ -416,9 +416,8 @@ void NSModel::Search(arma::mat&& querySet, Log::Info << "Searching for " << k; if (Epsilon() != 0) - Log::Info << " approximate nearest neighbors (e=" << Epsilon() << ") with "; - else - Log::Info << " nearest neighbors with "; + Log::Info << " approximate (e=" << Epsilon() << ")"; + Log::Info << " neighbors with "; if (!Naive() && !SingleMode()) Log::Info << "dual-tree " << TreeName() << " search..." << std::endl; else if (!Naive()) @@ -437,7 +436,10 @@ void NSModel::Search(const size_t k, arma::Mat& neighbors, arma::mat& distances) { - Log::Info << "Searching for " << k << " nearest neighbors with "; + Log::Info << "Searching for " << k; + if (Epsilon() != 0) + Log::Info << " approximate (e=" << Epsilon() << ")"; + Log::Info << " neighbors with "; if (!Naive() && !SingleMode()) Log::Info << "dual-tree " << TreeName() << " search..." << std::endl; else if (!Naive()) diff --git a/src/mlpack/methods/neighbor_search/sort_policies/furthest_neighbor_sort.cpp b/src/mlpack/methods/neighbor_search/sort_policies/furthest_neighbor_sort.cpp index aee4877398..f58e4d2c22 100644 --- a/src/mlpack/methods/neighbor_search/sort_policies/furthest_neighbor_sort.cpp +++ b/src/mlpack/methods/neighbor_search/sort_policies/furthest_neighbor_sort.cpp @@ -1,5 +1,5 @@ /*** - * @file nearest_neighbor_sort.cpp + * @file furthest_neighbor_sort.cpp * @author Ryan Curtin * * Implementation of the simple FurthestNeighborSort policy class. @@ -12,7 +12,7 @@ size_t FurthestNeighborSort::SortDistance(const arma::vec& list, const arma::Col& indices, double newDistance) { - // The first element in the list is the nearest neighbor. We only want to + // The first element in the list is the furthest neighbor. We only want to // insert if the new distance is greater than the last element in the list. if (newDistance < list[list.n_elem - 1]) return (size_t() - 1); // Do not insert. From d4a0f71f125b9ed09749ad9026f3922d6bff2010 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Thu, 9 Jun 2016 16:33:45 -0300 Subject: [PATCH 17/38] Change file name for test tools. --- src/mlpack/tests/activation_functions_test.cpp | 2 +- src/mlpack/tests/ada_delta_test.cpp | 2 +- src/mlpack/tests/adaboost_test.cpp | 2 +- src/mlpack/tests/adam_test.cpp | 2 +- src/mlpack/tests/akfn_test.cpp | 2 +- src/mlpack/tests/aknn_test.cpp | 2 +- src/mlpack/tests/arma_extend_test.cpp | 2 +- src/mlpack/tests/armadillo_svd_test.cpp | 2 +- src/mlpack/tests/aug_lagrangian_test.cpp | 2 +- src/mlpack/tests/binarize_test.cpp | 2 +- src/mlpack/tests/cf_test.cpp | 2 +- src/mlpack/tests/cli_test.cpp | 2 +- src/mlpack/tests/convolution_test.cpp | 2 +- src/mlpack/tests/convolutional_network_test.cpp | 2 +- src/mlpack/tests/cosine_tree_test.cpp | 2 +- src/mlpack/tests/decision_stump_test.cpp | 2 +- src/mlpack/tests/det_test.cpp | 2 +- src/mlpack/tests/distribution_test.cpp | 2 +- src/mlpack/tests/emst_test.cpp | 2 +- src/mlpack/tests/fastmks_test.cpp | 2 +- src/mlpack/tests/feedforward_network_test.cpp | 2 +- src/mlpack/tests/gmm_test.cpp | 2 +- src/mlpack/tests/hmm_test.cpp | 2 +- src/mlpack/tests/hoeffding_tree_test.cpp | 2 +- src/mlpack/tests/ind2sub_test.cpp | 2 +- src/mlpack/tests/init_rules_test.cpp | 2 +- src/mlpack/tests/kernel_pca_test.cpp | 2 +- src/mlpack/tests/kernel_test.cpp | 2 +- src/mlpack/tests/kernel_traits_test.cpp | 2 +- src/mlpack/tests/kfn_test.cpp | 2 +- src/mlpack/tests/kmeans_test.cpp | 2 +- src/mlpack/tests/knn_test.cpp | 2 +- src/mlpack/tests/krann_search_test.cpp | 2 +- src/mlpack/tests/lars_test.cpp | 2 +- src/mlpack/tests/layer_traits_test.cpp | 2 +- src/mlpack/tests/lbfgs_test.cpp | 2 +- src/mlpack/tests/lin_alg_test.cpp | 2 +- src/mlpack/tests/linear_regression_test.cpp | 2 +- src/mlpack/tests/load_save_test.cpp | 2 +- src/mlpack/tests/local_coordinate_coding_test.cpp | 2 +- src/mlpack/tests/log_test.cpp | 2 +- src/mlpack/tests/logistic_regression_test.cpp | 2 +- src/mlpack/tests/lrsdp_test.cpp | 2 +- src/mlpack/tests/lsh_test.cpp | 2 +- src/mlpack/tests/lstm_peephole_test.cpp | 2 +- src/mlpack/tests/math_test.cpp | 2 +- src/mlpack/tests/matrix_completion_test.cpp | 2 +- src/mlpack/tests/maximal_inputs_test.cpp | 2 +- src/mlpack/tests/mean_shift_test.cpp | 2 +- src/mlpack/tests/metric_test.cpp | 2 +- src/mlpack/tests/minibatch_sgd_test.cpp | 2 +- src/mlpack/tests/mlpack_test.cpp | 2 +- src/mlpack/tests/nbc_test.cpp | 2 +- src/mlpack/tests/nca_test.cpp | 2 +- src/mlpack/tests/network_util_test.cpp | 2 +- src/mlpack/tests/nmf_test.cpp | 2 +- src/mlpack/tests/nystroem_method_test.cpp | 2 +- src/mlpack/tests/pca_test.cpp | 2 +- src/mlpack/tests/perceptron_test.cpp | 2 +- src/mlpack/tests/performance_functions_test.cpp | 2 +- src/mlpack/tests/pooling_rules_test.cpp | 2 +- src/mlpack/tests/quic_svd_test.cpp | 2 +- src/mlpack/tests/radical_test.cpp | 2 +- src/mlpack/tests/range_search_test.cpp | 2 +- src/mlpack/tests/rectangle_tree_test.cpp | 2 +- src/mlpack/tests/recurrent_network_test.cpp | 2 +- src/mlpack/tests/regularized_svd_test.cpp | 2 +- src/mlpack/tests/rmsprop_test.cpp | 2 +- src/mlpack/tests/sa_test.cpp | 2 +- src/mlpack/tests/sdp_primal_dual_test.cpp | 2 +- src/mlpack/tests/serialization.hpp | 2 +- src/mlpack/tests/serialization_test.cpp | 2 +- src/mlpack/tests/sgd_test.cpp | 2 +- src/mlpack/tests/softmax_regression_test.cpp | 2 +- src/mlpack/tests/sort_policy_test.cpp | 2 +- src/mlpack/tests/sparse_autoencoder_test.cpp | 2 +- src/mlpack/tests/sparse_coding_test.cpp | 2 +- src/mlpack/tests/split_data_test.cpp | 2 +- src/mlpack/tests/svd_batch_test.cpp | 2 +- src/mlpack/tests/svd_incremental_test.cpp | 2 +- src/mlpack/tests/termination_policy_test.cpp | 2 +- .../{old_boost_test_definitions.hpp => test_tools.hpp} | 9 ++++----- src/mlpack/tests/tree_test.cpp | 2 +- src/mlpack/tests/tree_traits_test.cpp | 2 +- src/mlpack/tests/union_find_test.cpp | 2 +- 85 files changed, 88 insertions(+), 89 deletions(-) rename src/mlpack/tests/{old_boost_test_definitions.hpp => test_tools.hpp} (79%) diff --git a/src/mlpack/tests/activation_functions_test.cpp b/src/mlpack/tests/activation_functions_test.cpp index 34ec009ddb..9f32f8d5f9 100644 --- a/src/mlpack/tests/activation_functions_test.cpp +++ b/src/mlpack/tests/activation_functions_test.cpp @@ -25,7 +25,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/ada_delta_test.cpp b/src/mlpack/tests/ada_delta_test.cpp index 3471821505..483ce62b2f 100644 --- a/src/mlpack/tests/ada_delta_test.cpp +++ b/src/mlpack/tests/ada_delta_test.cpp @@ -12,7 +12,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace arma; using namespace mlpack::optimization; diff --git a/src/mlpack/tests/adaboost_test.cpp b/src/mlpack/tests/adaboost_test.cpp index fd5680d822..9edc57a606 100644 --- a/src/mlpack/tests/adaboost_test.cpp +++ b/src/mlpack/tests/adaboost_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include "serialization.hpp" using namespace arma; diff --git a/src/mlpack/tests/adam_test.cpp b/src/mlpack/tests/adam_test.cpp index df36980509..6daa5cb19c 100644 --- a/src/mlpack/tests/adam_test.cpp +++ b/src/mlpack/tests/adam_test.cpp @@ -11,7 +11,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace arma; using namespace mlpack::optimization; diff --git a/src/mlpack/tests/akfn_test.cpp b/src/mlpack/tests/akfn_test.cpp index 350e41e9bf..3621916c4b 100644 --- a/src/mlpack/tests/akfn_test.cpp +++ b/src/mlpack/tests/akfn_test.cpp @@ -7,7 +7,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::neighbor; diff --git a/src/mlpack/tests/aknn_test.cpp b/src/mlpack/tests/aknn_test.cpp index be14bf1fce..8d865ba360 100644 --- a/src/mlpack/tests/aknn_test.cpp +++ b/src/mlpack/tests/aknn_test.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::neighbor; diff --git a/src/mlpack/tests/arma_extend_test.cpp b/src/mlpack/tests/arma_extend_test.cpp index 076a02f3e3..79fd0e90d4 100644 --- a/src/mlpack/tests/arma_extend_test.cpp +++ b/src/mlpack/tests/arma_extend_test.cpp @@ -7,7 +7,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace arma; diff --git a/src/mlpack/tests/armadillo_svd_test.cpp b/src/mlpack/tests/armadillo_svd_test.cpp index cb945b3219..5cfc156ee4 100644 --- a/src/mlpack/tests/armadillo_svd_test.cpp +++ b/src/mlpack/tests/armadillo_svd_test.cpp @@ -2,7 +2,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(ArmadilloSVDTest); diff --git a/src/mlpack/tests/aug_lagrangian_test.cpp b/src/mlpack/tests/aug_lagrangian_test.cpp index 9507899dbb..fd6ce6d8d5 100644 --- a/src/mlpack/tests/aug_lagrangian_test.cpp +++ b/src/mlpack/tests/aug_lagrangian_test.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::optimization; diff --git a/src/mlpack/tests/binarize_test.cpp b/src/mlpack/tests/binarize_test.cpp index d0488a2303..ea2638baa0 100644 --- a/src/mlpack/tests/binarize_test.cpp +++ b/src/mlpack/tests/binarize_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace arma; diff --git a/src/mlpack/tests/cf_test.cpp b/src/mlpack/tests/cf_test.cpp index 834e326f76..6a1f1a6620 100644 --- a/src/mlpack/tests/cf_test.cpp +++ b/src/mlpack/tests/cf_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include "serialization.hpp" BOOST_AUTO_TEST_SUITE(CFTest); diff --git a/src/mlpack/tests/cli_test.cpp b/src/mlpack/tests/cli_test.cpp index d0ebbd3a6e..e6cbc64bfe 100644 --- a/src/mlpack/tests/cli_test.cpp +++ b/src/mlpack/tests/cli_test.cpp @@ -22,7 +22,7 @@ #define DEFAULT_INT 42 #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #define BASH_RED "\033[0;31m" #define BASH_GREEN "\033[0;32m" diff --git a/src/mlpack/tests/convolution_test.cpp b/src/mlpack/tests/convolution_test.cpp index 368f32d7a2..b273330ff1 100644 --- a/src/mlpack/tests/convolution_test.cpp +++ b/src/mlpack/tests/convolution_test.cpp @@ -13,7 +13,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/convolutional_network_test.cpp b/src/mlpack/tests/convolutional_network_test.cpp index baa2dff36d..1ca68c2dee 100644 --- a/src/mlpack/tests/convolutional_network_test.cpp +++ b/src/mlpack/tests/convolutional_network_test.cpp @@ -23,7 +23,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/cosine_tree_test.cpp b/src/mlpack/tests/cosine_tree_test.cpp index 925684f496..6a8c586e6d 100644 --- a/src/mlpack/tests/cosine_tree_test.cpp +++ b/src/mlpack/tests/cosine_tree_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(CosineTreeTest); diff --git a/src/mlpack/tests/decision_stump_test.cpp b/src/mlpack/tests/decision_stump_test.cpp index 36c83f7b9e..dae9875dc7 100644 --- a/src/mlpack/tests/decision_stump_test.cpp +++ b/src/mlpack/tests/decision_stump_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::decision_stump; diff --git a/src/mlpack/tests/det_test.cpp b/src/mlpack/tests/det_test.cpp index 9e4e91edfe..7e32defa4d 100644 --- a/src/mlpack/tests/det_test.cpp +++ b/src/mlpack/tests/det_test.cpp @@ -7,7 +7,7 @@ */ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" // This trick does not work on Windows. We will have to comment out the tests // that depend on it. diff --git a/src/mlpack/tests/distribution_test.cpp b/src/mlpack/tests/distribution_test.cpp index 0ca0dfda1e..d0261dd545 100644 --- a/src/mlpack/tests/distribution_test.cpp +++ b/src/mlpack/tests/distribution_test.cpp @@ -7,7 +7,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::distribution; diff --git a/src/mlpack/tests/emst_test.cpp b/src/mlpack/tests/emst_test.cpp index b4776584b1..9e8831aa2e 100644 --- a/src/mlpack/tests/emst_test.cpp +++ b/src/mlpack/tests/emst_test.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include diff --git a/src/mlpack/tests/fastmks_test.cpp b/src/mlpack/tests/fastmks_test.cpp index 973da37965..a7b20e9598 100644 --- a/src/mlpack/tests/fastmks_test.cpp +++ b/src/mlpack/tests/fastmks_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include "serialization.hpp" using namespace mlpack; diff --git a/src/mlpack/tests/feedforward_network_test.cpp b/src/mlpack/tests/feedforward_network_test.cpp index 6ae92d4ab2..e1412b462b 100644 --- a/src/mlpack/tests/feedforward_network_test.cpp +++ b/src/mlpack/tests/feedforward_network_test.cpp @@ -24,7 +24,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/gmm_test.cpp b/src/mlpack/tests/gmm_test.cpp index 83b9fd3f5b..561c261afe 100644 --- a/src/mlpack/tests/gmm_test.cpp +++ b/src/mlpack/tests/gmm_test.cpp @@ -15,7 +15,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::gmm; diff --git a/src/mlpack/tests/hmm_test.cpp b/src/mlpack/tests/hmm_test.cpp index 762090fdf1..7ea6403b8a 100644 --- a/src/mlpack/tests/hmm_test.cpp +++ b/src/mlpack/tests/hmm_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::hmm; diff --git a/src/mlpack/tests/hoeffding_tree_test.cpp b/src/mlpack/tests/hoeffding_tree_test.cpp index c060267cd1..1c6a678cea 100644 --- a/src/mlpack/tests/hoeffding_tree_test.cpp +++ b/src/mlpack/tests/hoeffding_tree_test.cpp @@ -12,7 +12,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include "serialization.hpp" #include diff --git a/src/mlpack/tests/ind2sub_test.cpp b/src/mlpack/tests/ind2sub_test.cpp index ef1014be0c..7f3518f2e9 100644 --- a/src/mlpack/tests/ind2sub_test.cpp +++ b/src/mlpack/tests/ind2sub_test.cpp @@ -6,7 +6,7 @@ */ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(ind2subTest); diff --git a/src/mlpack/tests/init_rules_test.cpp b/src/mlpack/tests/init_rules_test.cpp index 3d09c268a2..5ef4b9929b 100644 --- a/src/mlpack/tests/init_rules_test.cpp +++ b/src/mlpack/tests/init_rules_test.cpp @@ -14,7 +14,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/kernel_pca_test.cpp b/src/mlpack/tests/kernel_pca_test.cpp index dae716ff0b..e154630fbf 100644 --- a/src/mlpack/tests/kernel_pca_test.cpp +++ b/src/mlpack/tests/kernel_pca_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(KernelPCATest); diff --git a/src/mlpack/tests/kernel_test.cpp b/src/mlpack/tests/kernel_test.cpp index f21835c570..1c320190bd 100644 --- a/src/mlpack/tests/kernel_test.cpp +++ b/src/mlpack/tests/kernel_test.cpp @@ -19,7 +19,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::kernel; diff --git a/src/mlpack/tests/kernel_traits_test.cpp b/src/mlpack/tests/kernel_traits_test.cpp index f9eb88d93b..408df4bcaf 100644 --- a/src/mlpack/tests/kernel_traits_test.cpp +++ b/src/mlpack/tests/kernel_traits_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::kernel; diff --git a/src/mlpack/tests/kfn_test.cpp b/src/mlpack/tests/kfn_test.cpp index 2701a6a71f..1fc536a63b 100644 --- a/src/mlpack/tests/kfn_test.cpp +++ b/src/mlpack/tests/kfn_test.cpp @@ -7,7 +7,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::neighbor; diff --git a/src/mlpack/tests/kmeans_test.cpp b/src/mlpack/tests/kmeans_test.cpp index 3089a3e76f..9353557259 100644 --- a/src/mlpack/tests/kmeans_test.cpp +++ b/src/mlpack/tests/kmeans_test.cpp @@ -18,7 +18,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::kmeans; diff --git a/src/mlpack/tests/knn_test.cpp b/src/mlpack/tests/knn_test.cpp index 51a854aafb..aa169b7b8a 100644 --- a/src/mlpack/tests/knn_test.cpp +++ b/src/mlpack/tests/knn_test.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::neighbor; diff --git a/src/mlpack/tests/krann_search_test.cpp b/src/mlpack/tests/krann_search_test.cpp index 37e9b35bcc..805adb39e9 100644 --- a/src/mlpack/tests/krann_search_test.cpp +++ b/src/mlpack/tests/krann_search_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include #include diff --git a/src/mlpack/tests/lars_test.cpp b/src/mlpack/tests/lars_test.cpp index 83bd0a8357..5b410fec3a 100644 --- a/src/mlpack/tests/lars_test.cpp +++ b/src/mlpack/tests/lars_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::regression; diff --git a/src/mlpack/tests/layer_traits_test.cpp b/src/mlpack/tests/layer_traits_test.cpp index 0a0e3cc50b..373d8781c5 100644 --- a/src/mlpack/tests/layer_traits_test.cpp +++ b/src/mlpack/tests/layer_traits_test.cpp @@ -13,7 +13,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/lbfgs_test.cpp b/src/mlpack/tests/lbfgs_test.cpp index ea111f92ac..f16803c924 100644 --- a/src/mlpack/tests/lbfgs_test.cpp +++ b/src/mlpack/tests/lbfgs_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack::optimization; using namespace mlpack::optimization::test; diff --git a/src/mlpack/tests/lin_alg_test.cpp b/src/mlpack/tests/lin_alg_test.cpp index afb8876c30..6d11b36297 100644 --- a/src/mlpack/tests/lin_alg_test.cpp +++ b/src/mlpack/tests/lin_alg_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace arma; using namespace mlpack; diff --git a/src/mlpack/tests/linear_regression_test.cpp b/src/mlpack/tests/linear_regression_test.cpp index 6a0e8686d7..7f175c9544 100644 --- a/src/mlpack/tests/linear_regression_test.cpp +++ b/src/mlpack/tests/linear_regression_test.cpp @@ -7,7 +7,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::regression; diff --git a/src/mlpack/tests/load_save_test.cpp b/src/mlpack/tests/load_save_test.cpp index 3917aead1c..4eb8f12598 100644 --- a/src/mlpack/tests/load_save_test.cpp +++ b/src/mlpack/tests/load_save_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::data; diff --git a/src/mlpack/tests/local_coordinate_coding_test.cpp b/src/mlpack/tests/local_coordinate_coding_test.cpp index 2e51d90861..cc293b3767 100644 --- a/src/mlpack/tests/local_coordinate_coding_test.cpp +++ b/src/mlpack/tests/local_coordinate_coding_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include "serialization.hpp" using namespace arma; diff --git a/src/mlpack/tests/log_test.cpp b/src/mlpack/tests/log_test.cpp index 09a56c26f8..3a5a43d0d3 100644 --- a/src/mlpack/tests/log_test.cpp +++ b/src/mlpack/tests/log_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; diff --git a/src/mlpack/tests/logistic_regression_test.cpp b/src/mlpack/tests/logistic_regression_test.cpp index 7881bb248d..60acb1c0b5 100644 --- a/src/mlpack/tests/logistic_regression_test.cpp +++ b/src/mlpack/tests/logistic_regression_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::regression; diff --git a/src/mlpack/tests/lrsdp_test.cpp b/src/mlpack/tests/lrsdp_test.cpp index 1fe0f77f04..5a7b1cee3b 100644 --- a/src/mlpack/tests/lrsdp_test.cpp +++ b/src/mlpack/tests/lrsdp_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::optimization; diff --git a/src/mlpack/tests/lsh_test.cpp b/src/mlpack/tests/lsh_test.cpp index 65d1d78f66..8844972527 100644 --- a/src/mlpack/tests/lsh_test.cpp +++ b/src/mlpack/tests/lsh_test.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include #include diff --git a/src/mlpack/tests/lstm_peephole_test.cpp b/src/mlpack/tests/lstm_peephole_test.cpp index 6192e64123..fef1196235 100644 --- a/src/mlpack/tests/lstm_peephole_test.cpp +++ b/src/mlpack/tests/lstm_peephole_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/math_test.cpp b/src/mlpack/tests/math_test.cpp index d5f80e8a3f..c68441d8c5 100644 --- a/src/mlpack/tests/math_test.cpp +++ b/src/mlpack/tests/math_test.cpp @@ -8,7 +8,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace math; diff --git a/src/mlpack/tests/matrix_completion_test.cpp b/src/mlpack/tests/matrix_completion_test.cpp index df7020f8ea..697e8098bc 100644 --- a/src/mlpack/tests/matrix_completion_test.cpp +++ b/src/mlpack/tests/matrix_completion_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::matrix_completion; diff --git a/src/mlpack/tests/maximal_inputs_test.cpp b/src/mlpack/tests/maximal_inputs_test.cpp index 62062e1a43..c6e8c109ce 100644 --- a/src/mlpack/tests/maximal_inputs_test.cpp +++ b/src/mlpack/tests/maximal_inputs_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; diff --git a/src/mlpack/tests/mean_shift_test.cpp b/src/mlpack/tests/mean_shift_test.cpp index e777e154aa..e7577d744f 100644 --- a/src/mlpack/tests/mean_shift_test.cpp +++ b/src/mlpack/tests/mean_shift_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::meanshift; diff --git a/src/mlpack/tests/metric_test.cpp b/src/mlpack/tests/metric_test.cpp index 7eff0bbe90..10952fea8e 100644 --- a/src/mlpack/tests/metric_test.cpp +++ b/src/mlpack/tests/metric_test.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace std; using namespace mlpack::metric; diff --git a/src/mlpack/tests/minibatch_sgd_test.cpp b/src/mlpack/tests/minibatch_sgd_test.cpp index 90e5b58577..410d4e3c45 100644 --- a/src/mlpack/tests/minibatch_sgd_test.cpp +++ b/src/mlpack/tests/minibatch_sgd_test.cpp @@ -13,7 +13,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace std; using namespace arma; diff --git a/src/mlpack/tests/mlpack_test.cpp b/src/mlpack/tests/mlpack_test.cpp index 5b9f7a8341..b10486ee0e 100644 --- a/src/mlpack/tests/mlpack_test.cpp +++ b/src/mlpack/tests/mlpack_test.cpp @@ -17,7 +17,7 @@ #endif #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" /** * Provide a global fixture for each test. diff --git a/src/mlpack/tests/nbc_test.cpp b/src/mlpack/tests/nbc_test.cpp index a3e43cdd64..dc96f8d954 100644 --- a/src/mlpack/tests/nbc_test.cpp +++ b/src/mlpack/tests/nbc_test.cpp @@ -7,7 +7,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace naive_bayes; diff --git a/src/mlpack/tests/nca_test.cpp b/src/mlpack/tests/nca_test.cpp index d1c00c5a8e..34b1a39714 100644 --- a/src/mlpack/tests/nca_test.cpp +++ b/src/mlpack/tests/nca_test.cpp @@ -11,7 +11,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::metric; diff --git a/src/mlpack/tests/network_util_test.cpp b/src/mlpack/tests/network_util_test.cpp index 766ed852c5..30d633129d 100644 --- a/src/mlpack/tests/network_util_test.cpp +++ b/src/mlpack/tests/network_util_test.cpp @@ -12,7 +12,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/nmf_test.cpp b/src/mlpack/tests/nmf_test.cpp index 605ce39acc..f50b10a03b 100644 --- a/src/mlpack/tests/nmf_test.cpp +++ b/src/mlpack/tests/nmf_test.cpp @@ -12,7 +12,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(NMFTest); diff --git a/src/mlpack/tests/nystroem_method_test.cpp b/src/mlpack/tests/nystroem_method_test.cpp index f705439b8e..241d9716b5 100644 --- a/src/mlpack/tests/nystroem_method_test.cpp +++ b/src/mlpack/tests/nystroem_method_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include #include diff --git a/src/mlpack/tests/pca_test.cpp b/src/mlpack/tests/pca_test.cpp index d7a78c8830..e4f3b6a311 100644 --- a/src/mlpack/tests/pca_test.cpp +++ b/src/mlpack/tests/pca_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(PCATest); diff --git a/src/mlpack/tests/perceptron_test.cpp b/src/mlpack/tests/perceptron_test.cpp index 8f2de1e9b6..4fc1c8b57c 100644 --- a/src/mlpack/tests/perceptron_test.cpp +++ b/src/mlpack/tests/perceptron_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace arma; diff --git a/src/mlpack/tests/performance_functions_test.cpp b/src/mlpack/tests/performance_functions_test.cpp index 84839a13a7..9ad9342f45 100644 --- a/src/mlpack/tests/performance_functions_test.cpp +++ b/src/mlpack/tests/performance_functions_test.cpp @@ -11,7 +11,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/pooling_rules_test.cpp b/src/mlpack/tests/pooling_rules_test.cpp index b85f3e85c9..9909316aeb 100644 --- a/src/mlpack/tests/pooling_rules_test.cpp +++ b/src/mlpack/tests/pooling_rules_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/quic_svd_test.cpp b/src/mlpack/tests/quic_svd_test.cpp index f859e2116a..218bfff4c3 100644 --- a/src/mlpack/tests/quic_svd_test.cpp +++ b/src/mlpack/tests/quic_svd_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(QUICSVDTest); diff --git a/src/mlpack/tests/radical_test.cpp b/src/mlpack/tests/radical_test.cpp index c5dc323696..c14f449ae7 100644 --- a/src/mlpack/tests/radical_test.cpp +++ b/src/mlpack/tests/radical_test.cpp @@ -7,7 +7,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(RadicalTest); diff --git a/src/mlpack/tests/range_search_test.cpp b/src/mlpack/tests/range_search_test.cpp index 84cfed8dfc..88429719bd 100644 --- a/src/mlpack/tests/range_search_test.cpp +++ b/src/mlpack/tests/range_search_test.cpp @@ -9,7 +9,7 @@ #include #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::range; diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index f9278c554a..92595c6841 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -12,7 +12,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::neighbor; diff --git a/src/mlpack/tests/recurrent_network_test.cpp b/src/mlpack/tests/recurrent_network_test.cpp index 355aa81d4b..0ec1bcfe4f 100644 --- a/src/mlpack/tests/recurrent_network_test.cpp +++ b/src/mlpack/tests/recurrent_network_test.cpp @@ -20,7 +20,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::ann; diff --git a/src/mlpack/tests/regularized_svd_test.cpp b/src/mlpack/tests/regularized_svd_test.cpp index 1cbb741b5a..b29fe844f6 100644 --- a/src/mlpack/tests/regularized_svd_test.cpp +++ b/src/mlpack/tests/regularized_svd_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::svd; diff --git a/src/mlpack/tests/rmsprop_test.cpp b/src/mlpack/tests/rmsprop_test.cpp index 6fb3e745a3..b62d77f531 100644 --- a/src/mlpack/tests/rmsprop_test.cpp +++ b/src/mlpack/tests/rmsprop_test.cpp @@ -20,7 +20,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace arma; using namespace mlpack; diff --git a/src/mlpack/tests/sa_test.cpp b/src/mlpack/tests/sa_test.cpp index 05e7d9f181..3d87e5d954 100644 --- a/src/mlpack/tests/sa_test.cpp +++ b/src/mlpack/tests/sa_test.cpp @@ -14,7 +14,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace std; using namespace arma; diff --git a/src/mlpack/tests/sdp_primal_dual_test.cpp b/src/mlpack/tests/sdp_primal_dual_test.cpp index f3ed242497..0c42ea1d13 100644 --- a/src/mlpack/tests/sdp_primal_dual_test.cpp +++ b/src/mlpack/tests/sdp_primal_dual_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::optimization; diff --git a/src/mlpack/tests/serialization.hpp b/src/mlpack/tests/serialization.hpp index 39cf1d8ad9..a53a6e8b8f 100644 --- a/src/mlpack/tests/serialization.hpp +++ b/src/mlpack/tests/serialization.hpp @@ -17,7 +17,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" namespace mlpack { diff --git a/src/mlpack/tests/serialization_test.cpp b/src/mlpack/tests/serialization_test.cpp index 5dbb9aaf40..49a913f23f 100644 --- a/src/mlpack/tests/serialization_test.cpp +++ b/src/mlpack/tests/serialization_test.cpp @@ -7,7 +7,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include "serialization.hpp" #include diff --git a/src/mlpack/tests/sgd_test.cpp b/src/mlpack/tests/sgd_test.cpp index 1b273dcbdf..a8f389b1ac 100644 --- a/src/mlpack/tests/sgd_test.cpp +++ b/src/mlpack/tests/sgd_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace std; using namespace arma; diff --git a/src/mlpack/tests/softmax_regression_test.cpp b/src/mlpack/tests/softmax_regression_test.cpp index d7ed969dce..4fe8568eaf 100644 --- a/src/mlpack/tests/softmax_regression_test.cpp +++ b/src/mlpack/tests/softmax_regression_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::regression; diff --git a/src/mlpack/tests/sort_policy_test.cpp b/src/mlpack/tests/sort_policy_test.cpp index c6fcf5b87a..e336a76170 100644 --- a/src/mlpack/tests/sort_policy_test.cpp +++ b/src/mlpack/tests/sort_policy_test.cpp @@ -12,7 +12,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::neighbor; diff --git a/src/mlpack/tests/sparse_autoencoder_test.cpp b/src/mlpack/tests/sparse_autoencoder_test.cpp index 03d4e72d99..24d0cc6feb 100644 --- a/src/mlpack/tests/sparse_autoencoder_test.cpp +++ b/src/mlpack/tests/sparse_autoencoder_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::nn; diff --git a/src/mlpack/tests/sparse_coding_test.cpp b/src/mlpack/tests/sparse_coding_test.cpp index eab0b9c8aa..6815405e01 100644 --- a/src/mlpack/tests/sparse_coding_test.cpp +++ b/src/mlpack/tests/sparse_coding_test.cpp @@ -11,7 +11,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" #include "serialization.hpp" using namespace arma; diff --git a/src/mlpack/tests/split_data_test.cpp b/src/mlpack/tests/split_data_test.cpp index bbc529baae..1c52f632ba 100644 --- a/src/mlpack/tests/split_data_test.cpp +++ b/src/mlpack/tests/split_data_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace arma; diff --git a/src/mlpack/tests/svd_batch_test.cpp b/src/mlpack/tests/svd_batch_test.cpp index 36f354442f..45f9a12da3 100644 --- a/src/mlpack/tests/svd_batch_test.cpp +++ b/src/mlpack/tests/svd_batch_test.cpp @@ -7,7 +7,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(SVDBatchTest); diff --git a/src/mlpack/tests/svd_incremental_test.cpp b/src/mlpack/tests/svd_incremental_test.cpp index 2def227416..4fdd4e4ca2 100644 --- a/src/mlpack/tests/svd_incremental_test.cpp +++ b/src/mlpack/tests/svd_incremental_test.cpp @@ -9,7 +9,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(SVDIncrementalTest); diff --git a/src/mlpack/tests/termination_policy_test.cpp b/src/mlpack/tests/termination_policy_test.cpp index da51afbd88..945d82ce46 100644 --- a/src/mlpack/tests/termination_policy_test.cpp +++ b/src/mlpack/tests/termination_policy_test.cpp @@ -10,7 +10,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" BOOST_AUTO_TEST_SUITE(TerminationPolicyTest); diff --git a/src/mlpack/tests/old_boost_test_definitions.hpp b/src/mlpack/tests/test_tools.hpp similarity index 79% rename from src/mlpack/tests/old_boost_test_definitions.hpp rename to src/mlpack/tests/test_tools.hpp index 1586f6272c..2d4e56e235 100644 --- a/src/mlpack/tests/old_boost_test_definitions.hpp +++ b/src/mlpack/tests/test_tools.hpp @@ -1,12 +1,11 @@ /** - * @file old_boost_test_definitions.hpp + * @file test_tools.hpp * @author Ryan Curtin * - * Ancient Boost.Test versions don't act how we expect. This file includes the - * things we need to fix that. + * This file includes some useful macros for tests. */ -#ifndef MLPACK_TESTS_OLD_BOOST_TEST_DEFINITIONS_HPP -#define MLPACK_TESTS_OLD_BOOST_TEST_DEFINITIONS_HPP +#ifndef MLPACK_TESTS_TEST_TOOLS_HPP +#define MLPACK_TESTS_TEST_TOOLS_HPP #include diff --git a/src/mlpack/tests/tree_test.cpp b/src/mlpack/tests/tree_test.cpp index 14b2f51dd0..81a94463b2 100644 --- a/src/mlpack/tests/tree_test.cpp +++ b/src/mlpack/tests/tree_test.cpp @@ -14,7 +14,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::math; diff --git a/src/mlpack/tests/tree_traits_test.cpp b/src/mlpack/tests/tree_traits_test.cpp index e7b4925924..cf0395d19f 100644 --- a/src/mlpack/tests/tree_traits_test.cpp +++ b/src/mlpack/tests/tree_traits_test.cpp @@ -15,7 +15,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::tree; diff --git a/src/mlpack/tests/union_find_test.cpp b/src/mlpack/tests/union_find_test.cpp index 8468a2a78e..89e78ea8b6 100644 --- a/src/mlpack/tests/union_find_test.cpp +++ b/src/mlpack/tests/union_find_test.cpp @@ -8,7 +8,7 @@ #include #include -#include "old_boost_test_definitions.hpp" +#include "test_tools.hpp" using namespace mlpack; using namespace mlpack::emst; From af12e7665519f7342c6662594bb4b22d20a37d5b Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Thu, 9 Jun 2016 17:46:08 -0300 Subject: [PATCH 18/38] Fix style in test comments. --- src/mlpack/tests/aknn_test.cpp | 16 +++++++++++----- src/mlpack/tests/knn_test.cpp | 4 +++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/mlpack/tests/aknn_test.cpp b/src/mlpack/tests/aknn_test.cpp index 8d865ba360..70abcc8c05 100644 --- a/src/mlpack/tests/aknn_test.cpp +++ b/src/mlpack/tests/aknn_test.cpp @@ -241,7 +241,9 @@ BOOST_AUTO_TEST_CASE(DualBallTreeTest) REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); } -// Make sure sparse nearest neighbors works with kd trees. +/** + * Make sure sparse nearest neighbors works with kd trees. + */ BOOST_AUTO_TEST_CASE(SparseKNNKDTreeTest) { // The dimensionality of these datasets must be high so that the probability @@ -274,8 +276,10 @@ BOOST_AUTO_TEST_CASE(SparseKNNKDTreeTest) REQUIRE_RELATIVE_ERR(sparseDistances(j, i), naiveDistances(j, i), 0.05); } -// Ensure that we can build an NSModel and get correct -// results. +/** + * Ensure that we can build an NSModel and get correct + * results. + */ BOOST_AUTO_TEST_CASE(KNNModelTest) { typedef NSModel KNNModel; @@ -335,8 +339,10 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) } } -// Ensure that we can build an NSModel and get correct -// results, in the case where the reference set is the same as the query set. +/** + * Ensure that we can build an NSModel and get correct + * results, in the case where the reference set is the same as the query set. + */ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) { typedef NSModel KNNModel; diff --git a/src/mlpack/tests/knn_test.cpp b/src/mlpack/tests/knn_test.cpp index aa169b7b8a..85c6b7a79a 100644 --- a/src/mlpack/tests/knn_test.cpp +++ b/src/mlpack/tests/knn_test.cpp @@ -888,7 +888,9 @@ BOOST_AUTO_TEST_CASE(DualBallTreeTest) } } -// Make sure sparse nearest neighbors works with kd trees. +/** + * Make sure sparse nearest neighbors works with kd trees. + */ BOOST_AUTO_TEST_CASE(SparseKNNKDTreeTest) { // The dimensionality of these datasets must be high so that the probability From 7a5f66857932b98ca2a631ace6fb7beead7fe962 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Sat, 11 Jun 2016 13:59:38 -0300 Subject: [PATCH 19/38] Replace Naive by Dual Tree in approximate tests. Also, Improve code details. --- src/mlpack/tests/akfn_test.cpp | 167 +++++++++++---------- src/mlpack/tests/aknn_test.cpp | 255 ++++++++++++++++----------------- 2 files changed, 209 insertions(+), 213 deletions(-) diff --git a/src/mlpack/tests/akfn_test.cpp b/src/mlpack/tests/akfn_test.cpp index 3621916c4b..61ec6f57a3 100644 --- a/src/mlpack/tests/akfn_test.cpp +++ b/src/mlpack/tests/akfn_test.cpp @@ -23,21 +23,21 @@ BOOST_AUTO_TEST_SUITE(AKFNTest); * * Errors are produced if the results are not according to relative error. */ -BOOST_AUTO_TEST_CASE(DualTreeVsNaive1) +BOOST_AUTO_TEST_CASE(AproxVsExact1) { arma::mat dataset; if (!data::Load("test_data_3_1000.csv", dataset)) BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); - KFN naive(dataset, true); - arma::Mat neighborsNaive; - arma::mat distancesNaive; - naive.Search(dataset, 15, neighborsNaive, distancesNaive); + KFN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(dataset, 15, neighborsExact, distancesExact); for (size_t c = 0; c < 4; c++) { - KFN* kfn; + KFN* akfn; double epsilon; switch (c) @@ -56,107 +56,106 @@ BOOST_AUTO_TEST_CASE(DualTreeVsNaive1) break; } - kfn = new KFN(dataset, false, false, epsilon); - // Now perform the actual calculation. - arma::Mat neighborsTree; - arma::mat distancesTree; - kfn->Search(dataset, 15, neighborsTree, distancesTree); + akfn = new KFN(dataset, false, false, epsilon); + arma::Mat neighborsAprox; + arma::mat distancesAprox; + akfn->Search(dataset, 15, neighborsAprox, distancesAprox); - for (size_t i = 0; i < neighborsTree.n_elem; i++) - REQUIRE_RELATIVE_ERR(distancesTree(i), distancesNaive(i), epsilon); + for (size_t i = 0; i < neighborsAprox.n_elem; i++) + REQUIRE_RELATIVE_ERR(distancesAprox(i), distancesExact(i), epsilon); // Clean the memory. - delete kfn; + delete akfn; } } /** - * Test the dual-tree furthest-neighbors method with the naive method. This + * Test the dual-tree furthest-neighbors method with the exact method. This * uses only a reference dataset. * * Errors are produced if the results are not according to relative error. */ -BOOST_AUTO_TEST_CASE(DualTreeVsNaive2) +BOOST_AUTO_TEST_CASE(AproxVsExact2) { arma::mat dataset; if (!data::Load("test_data_3_1000.csv", dataset)) BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); - KFN naive(dataset, true); - arma::Mat neighborsNaive; - arma::mat distancesNaive; - naive.Search(15, neighborsNaive, distancesNaive); + KFN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(15, neighborsExact, distancesExact); - KFN kfn(dataset, false, false, 0.05); - arma::Mat neighborsTree; - arma::mat distancesTree; - kfn.Search(15, neighborsTree, distancesTree); + KFN akfn(dataset, false, false, 0.05); + arma::Mat neighborsAprox; + arma::mat distancesAprox; + akfn.Search(15, neighborsAprox, distancesAprox); - for (size_t i = 0; i < neighborsTree.n_elem; i++) - REQUIRE_RELATIVE_ERR(distancesTree[i], distancesNaive[i], 0.05); + for (size_t i = 0; i < neighborsAprox.n_elem; i++) + REQUIRE_RELATIVE_ERR(distancesAprox[i], distancesExact[i], 0.05); } /** - * Test the single-tree furthest-neighbors method with the naive method. This + * Test the single-tree furthest-neighbors method with the exact method. This * uses only a reference dataset. * * Errors are produced if the results are not according to relative error. */ -BOOST_AUTO_TEST_CASE(SingleTreeVsNaive) +BOOST_AUTO_TEST_CASE(SingleTreeVsExact) { arma::mat dataset; if (!data::Load("test_data_3_1000.csv", dataset)) BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); - KFN naive(dataset, true); - arma::Mat neighborsNaive; - arma::mat distancesNaive; - naive.Search(15, neighborsNaive, distancesNaive); + KFN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(15, neighborsExact, distancesExact); - KFN kfn(dataset, false, true, 0.05); - arma::Mat neighborsTree; - arma::mat distancesTree; - kfn.Search(15, neighborsTree, distancesTree); + KFN akfn(dataset, false, true, 0.05); + arma::Mat neighborsAprox; + arma::mat distancesAprox; + akfn.Search(15, neighborsAprox, distancesAprox); - for (size_t i = 0; i < neighborsTree.n_elem; i++) - REQUIRE_RELATIVE_ERR(distancesTree[i], distancesNaive[i], 0.05); + for (size_t i = 0; i < neighborsAprox.n_elem; i++) + REQUIRE_RELATIVE_ERR(distancesAprox[i], distancesExact[i], 0.05); } /** - * Test the cover tree single-tree furthest-neighbors method against the naive + * Test the cover tree single-tree furthest-neighbors method against the exact * method. This uses only a random reference dataset. * * Errors are produced if the results are not according to relative error. */ BOOST_AUTO_TEST_CASE(SingleCoverTreeTest) { - arma::mat data; - data.randu(75, 1000); // 75 dimensional, 1000 points. + arma::mat dataset; + dataset.randu(75, 1000); // 75 dimensional, 1000 points. - KFN naive(data, true); - arma::Mat naiveNeighbors; - arma::mat naiveDistances; - naive.Search(data, 15, naiveNeighbors, naiveDistances); + KFN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(dataset, 15, neighborsExact, distancesExact); StandardCoverTree, - arma::mat> tree(data); + arma::mat> tree(dataset); NeighborSearch, arma::mat, StandardCoverTree> coverTreeSearch(&tree, true, 0.05); - arma::Mat coverTreeNeighbors; - arma::mat coverTreeDistances; - coverTreeSearch.Search(data, 15, coverTreeNeighbors, coverTreeDistances); + arma::Mat neighborsCoverTree; + arma::mat distancesCoverTree; + coverTreeSearch.Search(dataset, 15, neighborsCoverTree, distancesCoverTree); - for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) - REQUIRE_RELATIVE_ERR(coverTreeDistances[i], naiveDistances[i], 0.05); + for (size_t i = 0; i < neighborsCoverTree.n_elem; ++i) + REQUIRE_RELATIVE_ERR(distancesCoverTree[i], distancesExact[i], 0.05); } /** - * Test the cover tree dual-tree furthest neighbors method against the naive + * Test the cover tree dual-tree furthest neighbors method against the exact * method. * * Errors are produced if the results are not according to relative error. @@ -166,10 +165,10 @@ BOOST_AUTO_TEST_CASE(DualCoverTreeTest) arma::mat dataset; data::Load("test_data_3_1000.csv", dataset); - KFN naive(dataset, true); - arma::Mat naiveNeighbors; - arma::mat naiveDistances; - naive.Search(dataset, 15, naiveNeighbors, naiveDistances); + KFN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(dataset, 15, neighborsExact, distancesExact); StandardCoverTree, arma::mat> referenceTree(dataset); @@ -177,43 +176,43 @@ BOOST_AUTO_TEST_CASE(DualCoverTreeTest) NeighborSearch, arma::mat, StandardCoverTree> coverTreeSearch(&referenceTree, false, 0.05); - arma::Mat coverTreeNeighbors; - arma::mat coverTreeDistances; - coverTreeSearch.Search(dataset, 15, coverTreeNeighbors, coverTreeDistances); + arma::Mat neighborsCoverTree; + arma::mat distancesCoverTree; + coverTreeSearch.Search(dataset, 15, neighborsCoverTree, distancesCoverTree); - for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) - REQUIRE_RELATIVE_ERR(coverTreeDistances[i], naiveDistances[i], 0.05); + for (size_t i = 0; i < neighborsCoverTree.n_elem; ++i) + REQUIRE_RELATIVE_ERR(distancesCoverTree[i], distancesExact[i], 0.05); } /** - * Test the ball tree single-tree furthest-neighbors method against the naive + * Test the ball tree single-tree furthest-neighbors method against the exact * method. This uses only a random reference dataset. * * Errors are produced if the results are not according to relative error. */ BOOST_AUTO_TEST_CASE(SingleBallTreeTest) { - arma::mat data; - data.randu(75, 1000); // 75 dimensional, 1000 points. + arma::mat dataset; + dataset.randu(75, 1000); // 75 dimensional, 1000 points. - KFN naive(data, true); - arma::Mat naiveNeighbors; - arma::mat naiveDistances; - naive.Search(data, 15, naiveNeighbors, naiveDistances); + KFN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(dataset, 15, neighborsExact, distancesExact); NeighborSearch - ballTreeSearch(data, false, true, 0.05); + ballTreeSearch(dataset, false, true, 0.05); - arma::Mat ballNeighbors; - arma::mat ballDistances; - ballTreeSearch.Search(data, 15, ballNeighbors, ballDistances); + arma::Mat neighborsBallTree; + arma::mat distancesBallTree; + ballTreeSearch.Search(dataset, 15, neighborsBallTree, distancesBallTree); - for (size_t i = 0; i < ballNeighbors.n_elem; ++i) - REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); + for (size_t i = 0; i < neighborsBallTree.n_elem; ++i) + REQUIRE_RELATIVE_ERR(distancesBallTree(i), distancesExact(i), 0.05); } /** - * Test the ball tree dual-tree furthest neighbors method against the naive + * Test the ball tree dual-tree furthest neighbors method against the exact * method. * * Errors are produced if the results are not according to relative error. @@ -223,19 +222,19 @@ BOOST_AUTO_TEST_CASE(DualBallTreeTest) arma::mat dataset; data::Load("test_data_3_1000.csv", dataset); - KFN naive(dataset, true); - arma::Mat naiveNeighbors; - arma::mat naiveDistances; - naive.Search(15, naiveNeighbors, naiveDistances); + KFN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(15, neighborsExact, distancesExact); NeighborSearch ballTreeSearch(dataset, false, false, 0.05); - arma::Mat ballNeighbors; - arma::mat ballDistances; - ballTreeSearch.Search(15, ballNeighbors, ballDistances); + arma::Mat neighborsBallTree; + arma::mat distancesBallTree; + ballTreeSearch.Search(15, neighborsBallTree, distancesBallTree); - for (size_t i = 0; i < ballNeighbors.n_elem; ++i) - REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); + for (size_t i = 0; i < neighborsBallTree.n_elem; ++i) + REQUIRE_RELATIVE_ERR(distancesBallTree(i), distancesExact(i), 0.05); } BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/mlpack/tests/aknn_test.cpp b/src/mlpack/tests/aknn_test.cpp index 70abcc8c05..23c7c9fda5 100644 --- a/src/mlpack/tests/aknn_test.cpp +++ b/src/mlpack/tests/aknn_test.cpp @@ -26,21 +26,21 @@ BOOST_AUTO_TEST_SUITE(AKNNTest); * * Errors are produced if the results are not according to relative error. */ -BOOST_AUTO_TEST_CASE(DualTreeVsNaive1) +BOOST_AUTO_TEST_CASE(AproxVsExact1) { arma::mat dataset; if (!data::Load("test_data_3_1000.csv", dataset)) BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); - KNN naive(dataset, true); - arma::Mat neighborsNaive; - arma::mat distancesNaive; - naive.Search(dataset, 15, neighborsNaive, distancesNaive); + KNN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(dataset, 15, neighborsExact, distancesExact); for (size_t c = 0; c < 4; c++) { - KNN* knn; + KNN* aknn; double epsilon; switch (c) @@ -59,107 +59,106 @@ BOOST_AUTO_TEST_CASE(DualTreeVsNaive1) break; } - knn = new KNN(dataset, false, false, epsilon); - // Now perform the actual calculation. - arma::Mat neighborsTree; - arma::mat distancesTree; - knn->Search(dataset, 15, neighborsTree, distancesTree); + aknn = new KNN(dataset, false, false, epsilon); + arma::Mat neighborsAprox; + arma::mat distancesAprox; + aknn->Search(dataset, 15, neighborsAprox, distancesAprox); - for (size_t i = 0; i < neighborsTree.n_elem; i++) - REQUIRE_RELATIVE_ERR(distancesTree(i), distancesNaive(i), epsilon); + for (size_t i = 0; i < neighborsAprox.n_elem; i++) + REQUIRE_RELATIVE_ERR(distancesAprox(i), distancesExact(i), epsilon); // Clean the memory. - delete knn; + delete aknn; } } /** - * Test the dual-tree nearest-neighbors method with the naive method. This uses + * Test the dual-tree nearest-neighbors method with the exact method. This uses * only a reference dataset. * * Errors are produced if the results are not according to relative error. */ -BOOST_AUTO_TEST_CASE(DualTreeVsNaive2) +BOOST_AUTO_TEST_CASE(AproxVsExact2) { arma::mat dataset; if (!data::Load("test_data_3_1000.csv", dataset)) BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); - KNN naive(dataset, true); - arma::Mat neighborsNaive; - arma::mat distancesNaive; - naive.Search(15, neighborsNaive, distancesNaive); + KNN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(15, neighborsExact, distancesExact); - KNN knn(dataset, false, false, 0.05); - arma::Mat neighborsTree; - arma::mat distancesTree; - knn.Search(15, neighborsTree, distancesTree); + KNN aknn(dataset, false, false, 0.05); + arma::Mat neighborsAprox; + arma::mat distancesAprox; + aknn.Search(15, neighborsAprox, distancesAprox); - for (size_t i = 0; i < neighborsTree.n_elem; i++) - REQUIRE_RELATIVE_ERR(distancesTree(i), distancesNaive(i), 0.05); + for (size_t i = 0; i < neighborsAprox.n_elem; i++) + REQUIRE_RELATIVE_ERR(distancesAprox(i), distancesExact(i), 0.05); } /** - * Test the single-tree nearest-neighbors method with the naive method. This + * Test the single-tree nearest-neighbors method with the exact method. This * uses only a reference dataset. * * Errors are produced if the results are not according to relative error. */ -BOOST_AUTO_TEST_CASE(SingleTreeVsNaive) +BOOST_AUTO_TEST_CASE(SingleTreeAproxVsExact) { arma::mat dataset; if (!data::Load("test_data_3_1000.csv", dataset)) BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!"); - KNN naive(dataset, true); - arma::Mat neighborsNaive; - arma::mat distancesNaive; - naive.Search(15, neighborsNaive, distancesNaive); + KNN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(15, neighborsExact, distancesExact); - KNN knn(dataset, false, true, 0.05); - arma::Mat neighborsTree; - arma::mat distancesTree; - knn.Search(15, neighborsTree, distancesTree); + KNN aknn(dataset, false, true, 0.05); + arma::Mat neighborsAprox; + arma::mat distancesAprox; + aknn.Search(15, neighborsAprox, distancesAprox); - for (size_t i = 0; i < neighborsTree.n_elem; i++) - REQUIRE_RELATIVE_ERR(distancesTree[i], distancesNaive[i], 0.05); + for (size_t i = 0; i < neighborsAprox.n_elem; i++) + REQUIRE_RELATIVE_ERR(distancesAprox[i], distancesExact[i], 0.05); } /** - * Test the cover tree single-tree nearest-neighbors method against the naive + * Test the cover tree single-tree nearest-neighbors method against the exact * method. This uses only a random reference dataset. * * Errors are produced if the results are not according to relative error. */ BOOST_AUTO_TEST_CASE(SingleCoverTreeTest) { - arma::mat data; - data.randu(75, 1000); // 75 dimensional, 1000 points. + arma::mat dataset; + dataset.randu(75, 1000); // 75 dimensional, 1000 points. - KNN naive(data, true); - arma::Mat naiveNeighbors; - arma::mat naiveDistances; - naive.Search(data, 15, naiveNeighbors, naiveDistances); + KNN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(dataset, 15, neighborsExact, distancesExact); StandardCoverTree, - arma::mat> tree(data); + arma::mat> tree(dataset); NeighborSearch, arma::mat, StandardCoverTree> coverTreeSearch(&tree, true, 0.05); - arma::Mat coverTreeNeighbors; - arma::mat coverTreeDistances; - coverTreeSearch.Search(data, 15, coverTreeNeighbors, coverTreeDistances); + arma::Mat neighborsCoverTree; + arma::mat distancesCoverTree; + coverTreeSearch.Search(dataset, 15, neighborsCoverTree, distancesCoverTree); - for (size_t i = 0; i < coverTreeNeighbors.n_elem; ++i) - REQUIRE_RELATIVE_ERR(coverTreeDistances[i], naiveDistances[i], 0.05); + for (size_t i = 0; i < neighborsCoverTree.n_elem; ++i) + REQUIRE_RELATIVE_ERR(distancesCoverTree[i], distancesExact[i], 0.05); } /** - * Test the cover tree dual-tree nearest neighbors method against the naive + * Test the cover tree dual-tree nearest neighbors method against the exact * method. * * Errors are produced if the results are not according to relative error. @@ -169,10 +168,10 @@ BOOST_AUTO_TEST_CASE(DualCoverTreeTest) arma::mat dataset; data::Load("test_data_3_1000.csv", dataset); - KNN naive(dataset, true); - arma::Mat naiveNeighbors; - arma::mat naiveDistances; - naive.Search(dataset, 15, naiveNeighbors, naiveDistances); + KNN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(dataset, 15, neighborsExact, distancesExact); StandardCoverTree, arma::mat> referenceTree(dataset); @@ -180,43 +179,44 @@ BOOST_AUTO_TEST_CASE(DualCoverTreeTest) NeighborSearch coverTreeSearch(&referenceTree, false, 0.05); - arma::Mat coverNeighbors; - arma::mat coverDistances; - coverTreeSearch.Search(&referenceTree, 15, coverNeighbors, coverDistances); + arma::Mat neighborsCoverTree; + arma::mat distancesCoverTree; + coverTreeSearch.Search(&referenceTree, 15, neighborsCoverTree, + distancesCoverTree); - for (size_t i = 0; i < coverNeighbors.n_elem; ++i) - REQUIRE_RELATIVE_ERR(coverDistances[i], naiveDistances[i], 0.05); + for (size_t i = 0; i < neighborsCoverTree.n_elem; ++i) + REQUIRE_RELATIVE_ERR(distancesCoverTree[i], distancesExact[i], 0.05); } /** - * Test the ball tree single-tree nearest-neighbors method against the naive + * Test the ball tree single-tree nearest-neighbors method against the exact * method. This uses only a random reference dataset. * * Errors are produced if the results are not according to relative error. */ BOOST_AUTO_TEST_CASE(SingleBallTreeTest) { - arma::mat data; - data.randu(50, 300); // 50 dimensional, 300 points. + arma::mat dataset; + dataset.randu(50, 300); // 50 dimensional, 300 points. - KNN naive(data, true); - arma::Mat naiveNeighbors; - arma::mat naiveDistances; - naive.Search(data, 15, naiveNeighbors, naiveDistances); + KNN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(dataset, 15, neighborsExact, distancesExact); NeighborSearch - ballTreeSearch(data, false, true, 0.05); + ballTreeSearch(dataset, false, true, 0.05); - arma::Mat ballNeighbors; - arma::mat ballDistances; - ballTreeSearch.Search(data, 15, ballNeighbors, ballDistances); + arma::Mat neighborsBallTree; + arma::mat distancesBallTree; + ballTreeSearch.Search(dataset, 15, neighborsBallTree, distancesBallTree); - for (size_t i = 0; i < ballNeighbors.n_elem; ++i) - REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); + for (size_t i = 0; i < neighborsBallTree.n_elem; ++i) + REQUIRE_RELATIVE_ERR(distancesBallTree(i), distancesExact(i), 0.05); } /** - * Test the ball tree dual-tree nearest neighbors method against the naive + * Test the ball tree dual-tree nearest neighbors method against the exact * method. * * Errors are produced if the results are not according to relative error. @@ -226,19 +226,19 @@ BOOST_AUTO_TEST_CASE(DualBallTreeTest) arma::mat dataset; data::Load("test_data_3_1000.csv", dataset); - KNN naive(dataset, true); - arma::Mat naiveNeighbors; - arma::mat naiveDistances; - naive.Search(15, naiveNeighbors, naiveDistances); + KNN exact(dataset); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(15, neighborsExact, distancesExact); NeighborSearch ballTreeSearch(dataset, false, false, 0.05); - arma::Mat ballNeighbors; - arma::mat ballDistances; - ballTreeSearch.Search(15, ballNeighbors, ballDistances); + arma::Mat neighborsBallTree; + arma::mat distancesBallTree; + ballTreeSearch.Search(15, neighborsBallTree, distancesBallTree); - for (size_t i = 0; i < ballNeighbors.n_elem; ++i) - REQUIRE_RELATIVE_ERR(ballDistances(i), naiveDistances(i), 0.05); + for (size_t i = 0; i < neighborsBallTree.n_elem; ++i) + REQUIRE_RELATIVE_ERR(distancesBallTree(i), distancesExact(i), 0.05); } /** @@ -260,20 +260,19 @@ BOOST_AUTO_TEST_CASE(SparseKNNKDTreeTest) typedef NeighborSearch SparseKNN; - SparseKNN a(referenceDataset, false, false, 0.05); - KNN naive(denseReference, true); + SparseKNN aknn(referenceDataset, false, false, 0.05); + arma::mat distancesSparse; + arma::Mat neighborsSparse; + aknn.Search(queryDataset, 10, neighborsSparse, distancesSparse); - arma::mat sparseDistances; - arma::Mat sparseNeighbors; - a.Search(queryDataset, 10, sparseNeighbors, sparseDistances); + KNN exact(denseReference); + arma::mat distancesExact; + arma::Mat neighborsExact; + exact.Search(denseQuery, 10, neighborsExact, distancesExact); - arma::mat naiveDistances; - arma::Mat naiveNeighbors; - naive.Search(denseQuery, 10, naiveNeighbors, naiveDistances); - - for (size_t i = 0; i < naiveNeighbors.n_cols; ++i) - for (size_t j = 0; j < naiveNeighbors.n_rows; ++j) - REQUIRE_RELATIVE_ERR(sparseDistances(j, i), naiveDistances(j, i), 0.05); + for (size_t i = 0; i < neighborsExact.n_cols; ++i) + for (size_t j = 0; j < neighborsExact.n_rows; ++j) + REQUIRE_RELATIVE_ERR(distancesSparse(j, i), distancesExact(j, i), 0.05); } /** @@ -305,10 +304,10 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) for (size_t j = 0; j < 3; ++j) { // Get a baseline. - KNN knn(referenceData); - arma::Mat baselineNeighbors; - arma::mat baselineDistances; - knn.Search(queryData, 3, baselineNeighbors, baselineDistances); + KNN aknn(referenceData); + arma::Mat neighborsExact; + arma::mat distancesExact; + aknn.Search(queryData, 3, neighborsExact, distancesExact); for (size_t i = 0; i < 12; ++i) { @@ -322,19 +321,19 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) if (j == 2) models[i].BuildModel(std::move(referenceCopy), 20, true, false); - arma::Mat neighbors; - arma::mat distances; + arma::Mat neighborsAprox; + arma::mat distancesAprox; - models[i].Search(std::move(queryCopy), 3, neighbors, distances); + models[i].Search(std::move(queryCopy), 3, neighborsAprox, distancesAprox); - BOOST_REQUIRE_EQUAL(neighbors.n_rows, baselineNeighbors.n_rows); - BOOST_REQUIRE_EQUAL(neighbors.n_cols, baselineNeighbors.n_cols); - BOOST_REQUIRE_EQUAL(neighbors.n_elem, baselineNeighbors.n_elem); - BOOST_REQUIRE_EQUAL(distances.n_rows, baselineDistances.n_rows); - BOOST_REQUIRE_EQUAL(distances.n_cols, baselineDistances.n_cols); - BOOST_REQUIRE_EQUAL(distances.n_elem, baselineDistances.n_elem); - for (size_t k = 0; k < distances.n_elem; ++k) - REQUIRE_RELATIVE_ERR(distances[k], baselineDistances[k], 0.05); + BOOST_REQUIRE_EQUAL(neighborsAprox.n_rows, neighborsExact.n_rows); + BOOST_REQUIRE_EQUAL(neighborsAprox.n_cols, neighborsExact.n_cols); + BOOST_REQUIRE_EQUAL(neighborsAprox.n_elem, neighborsExact.n_elem); + BOOST_REQUIRE_EQUAL(distancesAprox.n_rows, distancesExact.n_rows); + BOOST_REQUIRE_EQUAL(distancesAprox.n_cols, distancesExact.n_cols); + BOOST_REQUIRE_EQUAL(distancesAprox.n_elem, distancesExact.n_elem); + for (size_t k = 0; k < distancesAprox.n_elem; ++k) + REQUIRE_RELATIVE_ERR(distancesAprox[k], distancesExact[k], 0.05); } } } @@ -364,13 +363,13 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); - for (size_t j = 0; j < 3; ++j) + for (size_t j = 0; j < 2; ++j) { // Get a baseline. - KNN knn(referenceData); - arma::Mat baselineNeighbors; - arma::mat baselineDistances; - knn.Search(3, baselineNeighbors, baselineDistances); + KNN exact(referenceData); + arma::Mat neighborsExact; + arma::mat distancesExact; + exact.Search(3, neighborsExact, distancesExact); for (size_t i = 0; i < 12; ++i) { @@ -380,22 +379,20 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) models[i].BuildModel(std::move(referenceCopy), 20, false, false, 0.05); if (j == 1) models[i].BuildModel(std::move(referenceCopy), 20, false, true, 0.05); - if (j == 2) - models[i].BuildModel(std::move(referenceCopy), 20, true, false); - arma::Mat neighbors; - arma::mat distances; + arma::Mat neighborsAprox; + arma::mat distancesAprox; - models[i].Search(3, neighbors, distances); + models[i].Search(3, neighborsAprox, distancesAprox); - BOOST_REQUIRE_EQUAL(neighbors.n_rows, baselineNeighbors.n_rows); - BOOST_REQUIRE_EQUAL(neighbors.n_cols, baselineNeighbors.n_cols); - BOOST_REQUIRE_EQUAL(neighbors.n_elem, baselineNeighbors.n_elem); - BOOST_REQUIRE_EQUAL(distances.n_rows, baselineDistances.n_rows); - BOOST_REQUIRE_EQUAL(distances.n_cols, baselineDistances.n_cols); - BOOST_REQUIRE_EQUAL(distances.n_elem, baselineDistances.n_elem); - for (size_t k = 0; k < distances.n_elem; ++k) - REQUIRE_RELATIVE_ERR(distances[k], baselineDistances[k], 0.05); + BOOST_REQUIRE_EQUAL(neighborsAprox.n_rows, neighborsExact.n_rows); + BOOST_REQUIRE_EQUAL(neighborsAprox.n_cols, neighborsExact.n_cols); + BOOST_REQUIRE_EQUAL(neighborsAprox.n_elem, neighborsExact.n_elem); + BOOST_REQUIRE_EQUAL(distancesAprox.n_rows, distancesExact.n_rows); + BOOST_REQUIRE_EQUAL(distancesAprox.n_cols, distancesExact.n_cols); + BOOST_REQUIRE_EQUAL(distancesAprox.n_elem, distancesExact.n_elem); + for (size_t k = 0; k < distancesAprox.n_elem; ++k) + REQUIRE_RELATIVE_ERR(distancesAprox[k], distancesExact[k], 0.05); } } } From 74648fa7f17cbe70f4215f5c6c93efbc1d3553a1 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Mon, 13 Jun 2016 15:19:51 -0300 Subject: [PATCH 20/38] Replace epsilon by percentage in kfn. --- .../methods/neighbor_search/kfn_main.cpp | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp index a2fbf2eba9..3a4e4b7dd2 100644 --- a/src/mlpack/methods/neighbor_search/kfn_main.cpp +++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp @@ -72,8 +72,10 @@ PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0); PARAM_FLAG("naive", "If true, O(n^2) naive mode is used for computation.", "N"); PARAM_FLAG("single_mode", "If true, single-tree search is used (as opposed to " "dual-tree search).", "s"); -PARAM_DOUBLE("epsilon", "If specified, will do approximate furthest neighbor " - "search with given relative error.", "e", 0); +PARAM_DOUBLE("percentage", "If specified, will do approximate furthest neighbor" + " search. Must be in the range (0,1] (decimal form). Resultant neighbors " + "will be at least (p*100) % of the distance as the true furthest neighbor.", + "p", 1); // Convenience typedef. typedef NSModel KFNModel; @@ -140,11 +142,11 @@ int main(int argc, char *argv[]) Log::Fatal << "Invalid leaf size: " << lsInt << ". Must be greater than 0." << endl; - // Sanity check on epsilon. - const double epsilon = CLI::GetParam("epsilon"); - if (epsilon < 0) - Log::Fatal << "Invalid epsilon: " << epsilon << ". Must be non-negative. " - << endl; + // Sanity check on percentage. + const double percentage = CLI::GetParam("percentage"); + if (percentage <= 0 || percentage > 1) + Log::Fatal << "Invalid percentage: " << percentage + << ". Must be in the range (0,1] (decimal form)."<< endl; // We either have to load the reference data, or we have to load the model. NSModel kfn; @@ -184,7 +186,7 @@ int main(int argc, char *argv[]) << referenceSet.n_rows << "x" << referenceSet.n_cols << ")." << endl; kfn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode, - epsilon); + 1 - percentage); } else { @@ -200,7 +202,7 @@ int main(int argc, char *argv[]) kfn.SingleMode() = CLI::HasParam("single_mode"); kfn.Naive() = CLI::HasParam("naive"); kfn.LeafSize() = size_t(lsInt); - kfn.Epsilon() = epsilon; + kfn.Epsilon() = 1 - percentage; } // Perform search, if desired. From 7f68a278d9a69de9d0564cea6d54c7db8c95a49a Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Tue, 14 Jun 2016 10:57:57 -0300 Subject: [PATCH 21/38] Add epsilon for kfn (both epsilon and percentage). --- .../methods/neighbor_search/kfn_main.cpp | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp index 3a4e4b7dd2..163e8ed741 100644 --- a/src/mlpack/methods/neighbor_search/kfn_main.cpp +++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp @@ -72,6 +72,8 @@ PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0); PARAM_FLAG("naive", "If true, O(n^2) naive mode is used for computation.", "N"); PARAM_FLAG("single_mode", "If true, single-tree search is used (as opposed to " "dual-tree search).", "s"); +PARAM_DOUBLE("epsilon", "If specified, will do approximate furthest neighbor " + "search with given relative error. Must be in the range [0,1).", "e", 0); PARAM_DOUBLE("percentage", "If specified, will do approximate furthest neighbor" " search. Must be in the range (0,1] (decimal form). Resultant neighbors " "will be at least (p*100) % of the distance as the true furthest neighbor.", @@ -142,11 +144,23 @@ int main(int argc, char *argv[]) Log::Fatal << "Invalid leaf size: " << lsInt << ". Must be greater than 0." << endl; + // Sanity check on epsilon. + double epsilon = CLI::GetParam("epsilon"); + if (epsilon < 0 || epsilon >= 1) + Log::Fatal << "Invalid epsilon: " << epsilon << ". Must be in the range " + << "[0,1)." << endl; + // Sanity check on percentage. const double percentage = CLI::GetParam("percentage"); if (percentage <= 0 || percentage > 1) - Log::Fatal << "Invalid percentage: " << percentage - << ". Must be in the range (0,1] (decimal form)."<< endl; + Log::Fatal << "Invalid percentage: " << percentage << ". Must be in the " + << "range (0,1] (decimal form)." << endl; + + if (CLI::HasParam("percentage") && CLI::HasParam("epsilon")) + Log::Fatal << "Cannot provide both epsilon and percentage." << endl; + + if (CLI::HasParam("percentage")) + epsilon = 1 - percentage; // We either have to load the reference data, or we have to load the model. NSModel kfn; @@ -186,7 +200,7 @@ int main(int argc, char *argv[]) << referenceSet.n_rows << "x" << referenceSet.n_cols << ")." << endl; kfn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode, - 1 - percentage); + epsilon); } else { @@ -202,7 +216,7 @@ int main(int argc, char *argv[]) kfn.SingleMode() = CLI::HasParam("single_mode"); kfn.Naive() = CLI::HasParam("naive"); kfn.LeafSize() = size_t(lsInt); - kfn.Epsilon() = 1 - percentage; + kfn.Epsilon() = epsilon; } // Perform search, if desired. From 7f4dfd005a8aa71afa703012ab613a3afc08785a Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Tue, 14 Jun 2016 11:44:18 -0300 Subject: [PATCH 22/38] Use absolute value when considering relative error. --- src/mlpack/tests/test_tools.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/tests/test_tools.hpp b/src/mlpack/tests/test_tools.hpp index 2d4e56e235..77fd1c189b 100644 --- a/src/mlpack/tests/test_tools.hpp +++ b/src/mlpack/tests/test_tools.hpp @@ -37,6 +37,6 @@ // Require the approximation L to be within a relative error of E respect to the // actual value R. #define REQUIRE_RELATIVE_ERR( L, R, E ) \ - BOOST_REQUIRE_LE( abs((R) - (L)), (E) * (R)) + BOOST_REQUIRE_LE( abs((R) - (L)), (E) * abs(R)) #endif From 49822ef4179f31123e1e7e27be40d76f8e0804e0 Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Wed, 22 Jun 2016 12:44:37 -0300 Subject: [PATCH 23/38] Improve log message. --- .../methods/neighbor_search/ns_model_impl.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp index 0a705626a6..075306bece 100644 --- a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp @@ -414,16 +414,16 @@ void NSModel::Search(arma::mat&& querySet, if (randomBasis) querySet = q * querySet; - Log::Info << "Searching for " << k; - if (Epsilon() != 0) - Log::Info << " approximate (e=" << Epsilon() << ")"; - Log::Info << " neighbors with "; + Log::Info << "Searching for " << k << " neighbors with "; if (!Naive() && !SingleMode()) Log::Info << "dual-tree " << TreeName() << " search..." << std::endl; else if (!Naive()) Log::Info << "single-tree " << TreeName() << " search..." << std::endl; else Log::Info << "brute-force (naive) search..." << std::endl; + if (Epsilon() != 0 && !Naive()) + Log::Info << "Maximum of " << Epsilon() * 100 << "% relative error." + << std::endl; BiSearchVisitor search(querySet, k, neighbors, distances, leafSize); @@ -436,16 +436,16 @@ void NSModel::Search(const size_t k, arma::Mat& neighbors, arma::mat& distances) { - Log::Info << "Searching for " << k; - if (Epsilon() != 0) - Log::Info << " approximate (e=" << Epsilon() << ")"; - Log::Info << " neighbors with "; + Log::Info << "Searching for " << k << " neighbors with "; if (!Naive() && !SingleMode()) Log::Info << "dual-tree " << TreeName() << " search..." << std::endl; else if (!Naive()) Log::Info << "single-tree " << TreeName() << " search..." << std::endl; else Log::Info << "brute-force (naive) search..." << std::endl; + if (Epsilon() != 0 && !Naive()) + Log::Info << "Maximum of " << Epsilon() * 100 << "% relative error." + << std::endl; MonoSearchVisitor search(k, neighbors, distances); boost::apply_visitor(search, nSearch); From fd33fd5ca64fb9f6a82168dc259e66fcec55ae4b Mon Sep 17 00:00:00 2001 From: MarcosPividori Date: Wed, 22 Jun 2016 14:33:26 -0300 Subject: [PATCH 24/38] Move constructor to header, to avoid linking problems (MonoSearchVisitor is not a template class). --- src/mlpack/methods/neighbor_search/ns_model.hpp | 6 +++++- src/mlpack/methods/neighbor_search/ns_model_impl.hpp | 9 --------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/mlpack/methods/neighbor_search/ns_model.hpp b/src/mlpack/methods/neighbor_search/ns_model.hpp index db3331a3e4..711d640703 100644 --- a/src/mlpack/methods/neighbor_search/ns_model.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model.hpp @@ -69,7 +69,11 @@ class MonoSearchVisitor : public boost::static_visitor MonoSearchVisitor(const size_t k, arma::Mat& neighbors, - arma::mat& distances); + arma::mat& distances) : + k(k), + neighbors(neighbors), + distances(distances) + {}; }; /** diff --git a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp index 075306bece..da8ab07f98 100644 --- a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp @@ -18,15 +18,6 @@ namespace mlpack { namespace neighbor { -//! Save parameters for monochromatic neighbor search. -MonoSearchVisitor::MonoSearchVisitor(const size_t k, - arma::Mat& neighbors, - arma::mat& distances) : - k(k), - neighbors(neighbors), - distances(distances) -{} - //! Monochromatic neighbor search on the given NSType instance. template void MonoSearchVisitor::operator()(NSType *ns) const From 64525dea5a9e084331d00278d26bdfd82d8c166a Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Thu, 23 Jun 2016 23:04:35 +0300 Subject: [PATCH 25/38] Fixed comments. Removed RecursiveHilbertValue. Added a template parameter splitOrder. --- src/mlpack/core/tree/CMakeLists.txt | 2 - src/mlpack/core/tree/rectangle_tree.hpp | 1 - .../rectangle_tree/discrete_hilbert_value.hpp | 57 ++- .../discrete_hilbert_value_impl.hpp | 94 +++-- .../hilbert_r_tree_auxiliary_information.hpp | 3 - ...bert_r_tree_auxiliary_information_impl.hpp | 16 - .../rectangle_tree/hilbert_r_tree_split.hpp | 2 +- .../hilbert_r_tree_split_impl.hpp | 25 +- .../no_auxiliary_information.hpp | 41 ++- .../rectangle_tree/rectangle_tree_impl.hpp | 2 +- .../recursive_hilbert_value.hpp | 219 ----------- .../recursive_hilbert_value_impl.hpp | 347 ------------------ .../core/tree/rectangle_tree/typedef.hpp | 16 +- .../x_tree_auxiliary_information.hpp | 48 ++- src/mlpack/tests/rectangle_tree_test.cpp | 72 +--- 15 files changed, 170 insertions(+), 775 deletions(-) delete mode 100644 src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp delete mode 100644 src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp diff --git a/src/mlpack/core/tree/CMakeLists.txt b/src/mlpack/core/tree/CMakeLists.txt index 28415d528d..0399e84cda 100644 --- a/src/mlpack/core/tree/CMakeLists.txt +++ b/src/mlpack/core/tree/CMakeLists.txt @@ -59,8 +59,6 @@ set(SOURCES rectangle_tree/hilbert_r_tree_split_impl.hpp rectangle_tree/hilbert_r_tree_auxiliary_information.hpp rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp - rectangle_tree/recursive_hilbert_value.hpp - rectangle_tree/recursive_hilbert_value_impl.hpp rectangle_tree/discrete_hilbert_value.hpp rectangle_tree/discrete_hilbert_value_impl.hpp statistic.hpp diff --git a/src/mlpack/core/tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree.hpp index de236ad40e..c2ff9bb44b 100644 --- a/src/mlpack/core/tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree.hpp @@ -28,7 +28,6 @@ #include "rectangle_tree/hilbert_r_tree_descent_heuristic.hpp" #include "rectangle_tree/hilbert_r_tree_split.hpp" #include "rectangle_tree/hilbert_r_tree_auxiliary_information.hpp" -#include "rectangle_tree/recursive_hilbert_value.hpp" #include "rectangle_tree/discrete_hilbert_value.hpp" #include "rectangle_tree/typedef.hpp" diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp index ab7f8a06ef..98eec7937e 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -139,11 +139,21 @@ class DiscreteHilbertValue */ template void Copy(TreeType* dst, TreeType* src); - + + /** + * Copy the local Hilbert value's pointer. + * @param val The DiscreteHilbertValue object from which the dataset + * will be copied. + */ + DiscreteHilbertValue& operator = (const DiscreteHilbertValue& val); + + /** + * Nullify the localHilbertValues pointer in order to prevent an invalid free. + */ void NullifyData(); /** - * Update the largest Hilbert value and the local dataset. + * Update the largest Hilbert value and the local Hilbert values of an intermediate node. * The children of the node (or the points that the node contains) should be * arranged according to their Hilbert values. * @param node The node in which the information should be updated. @@ -151,8 +161,16 @@ class DiscreteHilbertValue template void UpdateLargestValue(TreeType* node); + /** + * This method updates the largest Hilbert value of a leaf node and + * redistributes the Hilbert values of points according to their new position + * after the split algorithm. + * @param parent The parent of the node that was split. + * @param firstSibling The first cooperationg sibling. + * @param lastSibling The last cooperating sibling. + */ template - void UpdateHilbertValues(TreeType* parent, size_t firstSibling, + void RedistributeHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling); /** @@ -182,11 +200,12 @@ class DiscreteHilbertValue { return numValues; } //! Return the local dataset - const arma::Mat* LocalDataset() const - { return localDataset; } + const arma::Mat* LocalHilbertValues() const + { return localHilbertValues; } //! Modify the dataset - arma::Mat*& LocalDataset() { return localDataset; } + arma::Mat*& LocalHilbertValues() + { return localHilbertValues; } //! Modify the valueToInsert arma::Col* ValueToInsert() { return valueToInsert; } @@ -198,21 +217,21 @@ class DiscreteHilbertValue private: //! The number of bits that we can store static constexpr size_t order = sizeof(HilbertElemType) * CHAR_BIT; - //! The local dataset - arma::Mat* localDataset; - //! Indicates that the node owns the local dataset - bool ownsLocalDataset; - //! The number of values in the local dataset + //! The local Hilbert values + arma::Mat* localHilbertValues; + //! Indicates that the node owns the localHilbertValues variable + bool ownsLocalHilbertValues; + //! The number of values in the localHilbertValues dataset size_t numValues; - //! The Hilbert value of the point that is being inserted - arma::Col* valueToInsert; - //! Indicates that the node owns the valueToInsert - bool ownsValueToInsert; - - /** - * Returns true if the node has the largest Hilbert value. + /** The Hilbert value of the point that is being inserted. + * The pointer is the same in all nodes. The value is updated in InsertPoint() + * if it is invoked at the root level. This variable helps to avoid + * multiple computation of the Hilbert value of a point in the insertion + * process. */ - bool HasValue() const; + arma::Col* valueToInsert; + //! Indicates that the node owns the valueToInsert. + bool ownsValueToInsert; public: template diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index a9b47836cb..bf3cd27c72 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -15,8 +15,8 @@ namespace tree /** Trees and tree-building procedures. */ { template DiscreteHilbertValue::DiscreteHilbertValue() : - localDataset(NULL), - ownsLocalDataset(false), + localHilbertValues(NULL), + ownsLocalHilbertValues(false), numValues(0), valueToInsert(NULL), ownsValueToInsert(false) @@ -27,8 +27,8 @@ DiscreteHilbertValue::DiscreteHilbertValue() : template DiscreteHilbertValue::~DiscreteHilbertValue() { - if (ownsLocalDataset) - delete localDataset; + if (ownsLocalHilbertValues) + delete localHilbertValues; if (ownsValueToInsert) delete valueToInsert; } @@ -36,8 +36,8 @@ DiscreteHilbertValue::~DiscreteHilbertValue() template template DiscreteHilbertValue::DiscreteHilbertValue(const TreeType* tree) : - localDataset(NULL), - ownsLocalDataset(false), + localHilbertValues(NULL), + ownsLocalHilbertValues(false), numValues(0), valueToInsert(tree->Parent() ? tree->Parent()->AuxiliaryInfo().HilbertValue().ValueToInsert() : @@ -46,17 +46,17 @@ DiscreteHilbertValue::DiscreteHilbertValue(const TreeType* tree) : { // Calculate the Hilbert value for all points if (!tree->Parent()) // This is the root node - ownsLocalDataset = true; + ownsLocalHilbertValues = true; else if (tree->Parent()->Children()[0]->IsLeaf()) { // This is a leaf node assert(tree->Parent()->NumChildren() > 0); - ownsLocalDataset = true; + ownsLocalHilbertValues = true; } - if (ownsLocalDataset) + if (ownsLocalHilbertValues) { - localDataset = new arma::Mat(tree->Dataset().n_rows, + localHilbertValues = new arma::Mat(tree->Dataset().n_rows, tree->MaxLeafSize() + 1); } @@ -65,8 +65,8 @@ DiscreteHilbertValue::DiscreteHilbertValue(const TreeType* tree) : template DiscreteHilbertValue:: DiscreteHilbertValue(const DiscreteHilbertValue& other) : - localDataset(const_cast*>(other.LocalDataset())), - ownsLocalDataset(other.ownsLocalDataset), + localHilbertValues(const_cast*>(other.LocalHilbertValues())), + ownsLocalHilbertValues(other.ownsLocalHilbertValues), numValues(other.NumValues()), valueToInsert(const_cast*>(other.ValueToInsert())), ownsValueToInsert(false) @@ -216,15 +216,15 @@ int DiscreteHilbertValue:: CompareValues(const DiscreteHilbertValue& val1, const DiscreteHilbertValue& val2) { - if (val1.HasValue() && !val2.HasValue()) + if (val1.NumValues() > 0 && val2.NumValues() == 0) return 1; - else if (!val1.HasValue() && val2.HasValue()) + else if (val1.NumValues() == 0 && val2.NumValues() > 0) return -1; - else if (!val1.HasValue() && !val2.HasValue()) + else if (val1.NumValues() == 0 && val2.NumValues() == 0) return 0; - return CompareValues(val1.LocalDataset()->col(val1.NumValues() - 1), - val2.LocalDataset()->col(val2.NumValues() - 1)); + return CompareValues(val1.LocalHilbertValues()->col(val1.NumValues() - 1), + val2.LocalHilbertValues()->col(val2.NumValues() - 1)); } template @@ -242,10 +242,10 @@ CompareWith(const VecType& pt, { arma::Col val = CalculateValue(pt); - if (!HasValue()) + if (numValues == 0) return -1; - return CompareValues(localDataset->col(numValues - 1),val); + return CompareValues(localHilbertValues->col(numValues - 1),val); } template @@ -254,10 +254,10 @@ int DiscreteHilbertValue:: CompareWithCachedPoint(const VecType& , typename boost::enable_if>*) const { - if (!HasValue()) + if (numValues == 0) return -1; - return CompareValues(localDataset->col(numValues - 1),*valueToInsert); + return CompareValues(localHilbertValues->col(numValues - 1),*valueToInsert); } template @@ -275,13 +275,13 @@ InsertPoint(TreeType *node, const VecType& pt, { // Find an appropriate place for (i = 0; i < numValues; i++) - if (CompareValues(localDataset->col(i), *valueToInsert) > 0) + if (CompareValues(localHilbertValues->col(i), *valueToInsert) > 0) break; for (size_t j = numValues; j > i; j--) - localDataset->col(j) = localDataset->col(j-1); + localHilbertValues->col(j) = localHilbertValues->col(j-1); - localDataset->col(i) = *valueToInsert; + localHilbertValues->col(i) = *valueToInsert; numValues++; // Propogate changes of the largest Hilbert value downward TreeType* root = node->Parent(); @@ -306,7 +306,7 @@ void DiscreteHilbertValue::InsertNode(TreeType* node) if (CompareWith(node,val) < 0) { - localDataset = val.LocalDataset(); + localHilbertValues = val.LocalHilbertValues(); numValues = val.NumValues(); } } @@ -319,7 +319,7 @@ DeletePoint(TreeType* node, const size_t localIndex) // Delete the Hilbert value from the local dataset for (size_t i = numValues - 1; i > localIndex; i--) - localDataset->col(i-1) = localDataset->col(i); + localHilbertValues->col(i-1) = localHilbertValues->col(i); numValues--; } @@ -331,7 +331,7 @@ RemoveNode(TreeType* node, const size_t nodeIndex) { if (node->NumChildren() <= 1) { - localDataset = NULL; + localHilbertValues = NULL; numValues = 0; return; } @@ -342,31 +342,32 @@ RemoveNode(TreeType* node, const size_t nodeIndex) if (child->AuxiliaryInfo.HilbertValue().NumValues() != 0) { numValues = child->AuxiliaryInfo.HilbertValue().NumValues(); - localDataset = child->AuxiliaryInfo.HilbertValue().LocalDataset(); + localHilbertValues = child->AuxiliaryInfo.HilbertValue().LocalHilbertValues(); } else { - localDataset = NULL; + localHilbertValues = NULL; numValues = 0; } } } template -template -void DiscreteHilbertValue::Copy(TreeType* dst, TreeType* src) +DiscreteHilbertValue& DiscreteHilbertValue:: +operator = (const DiscreteHilbertValue& val) { - DiscreteHilbertValue &dstVal = dst->AuxiliaryInfo().HilbertValue(); - DiscreteHilbertValue &srcVal = src->AuxiliaryInfo().HilbertValue(); + localHilbertValues = const_cast* > + (val.LocalHilbertValues()); + ownsLocalHilbertValues = false; + numValues = val.NumValues(); - dst.LocalDataset() = src.LocalDataset(); - dst.NumValues() = src.NumValues(); + return *this; } template void DiscreteHilbertValue::NullifyData() { - ownsLocalDataset = false; + ownsLocalHilbertValues = false; } template @@ -376,7 +377,7 @@ void DiscreteHilbertValue::UpdateLargestValue(TreeType* node) if (!node->IsLeaf()) { // Update the largest Hilbert value - localDataset = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().HilbertValue().LocalDataset(); + localHilbertValues = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().HilbertValue().LocalHilbertValues(); numValues = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().HilbertValue().NumValues(); } } @@ -384,7 +385,7 @@ void DiscreteHilbertValue::UpdateLargestValue(TreeType* node) template template void DiscreteHilbertValue:: -UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) +RedistributeHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) { // We should update the local dataset if points were redistributed @@ -394,7 +395,7 @@ UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) numPoints += parent->Children()[i]->NumPoints(); // Copy the local datasets - arma::Mat tmp(localDataset->n_rows,numPoints); + arma::Mat tmp(localHilbertValues->n_rows,numPoints); size_t iPoint = 0; for (size_t i = firstSibling; i<= lastSibling; i++) @@ -404,7 +405,7 @@ UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) for (size_t j = 0; j < value.NumValues(); j++) { - tmp.col(iPoint) = value.LocalDataset()->col(j); + tmp.col(iPoint) = value.LocalHilbertValues()->col(j); iPoint++; } } @@ -420,7 +421,7 @@ UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) for (size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) { - value.LocalDataset()->col(j) = tmp.col(iPoint); + value.LocalHilbertValues()->col(j) = tmp.col(iPoint); iPoint++; } value.NumValues() = parent->Children()[i]->NumPoints(); @@ -430,13 +431,6 @@ UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) } - -template -bool DiscreteHilbertValue::HasValue() const -{ - return numValues > 0; -} - template template void DiscreteHilbertValue:: @@ -444,8 +438,8 @@ Serialize(Archive& ar, const unsigned int /* version */) { using data::CreateNVP; - ar & CreateNVP(localDataset, "localDataset"); - ar & CreateNVP(ownsLocalDataset, "ownsLocalDataset"); + ar & CreateNVP(localHilbertValues, "localHilbertValues"); + ar & CreateNVP(ownsLocalHilbertValues, "ownsLocalHilbertValues"); ar & CreateNVP(numValues, "numValues"); ar & CreateNVP(valueToInsert, "valueToInsert"); ar & CreateNVP(ownsValueToInsert, "ownsValueToInsert"); diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp index be18a1c215..a51a0ebd69 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp @@ -34,9 +34,6 @@ class HilbertRTreeAuxiliaryInformation */ HilbertRTreeAuxiliaryInformation(const HilbertRTreeAuxiliaryInformation& other); - //! Free memory - ~HilbertRTreeAuxiliaryInformation(); - /** * The Hilbert R tree requires to insert points according to their * Hilbert value. This method should take care of it. diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index 507388e45d..3e5e5f3fb0 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -40,14 +40,6 @@ HilbertRTreeAuxiliaryInformation(const HilbertRTreeAuxiliaryInformation& other) { }; - -template class HilbertValueType> -HilbertRTreeAuxiliaryInformation:: -~HilbertRTreeAuxiliaryInformation() -{ - -} template class HilbertValueType> @@ -158,14 +150,6 @@ UpdateAuxiliaryInfo(TreeType* node) return false; } -template class HilbertValueType> -void HilbertRTreeAuxiliaryInformation:: -Copy(TreeType* dst, TreeType* src) -{ - hilbertValue.Copy(dst,src); -} - template class HilbertValueType> void HilbertRTreeAuxiliaryInformation:: diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp index f830c134ba..5ffdda5e3b 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp @@ -17,8 +17,8 @@ namespace tree /** Trees and tree-building procedures. */ { * The order of the splitting policy. The Hilbert R tree splits a node * on overflow, turnung splitOrder node to (splitOrder+1) nodes. */ -constexpr int splitOrder = 2; +template class HilbertRTreeSplit { public: diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index fd39961094..8fff710382 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -14,8 +14,9 @@ namespace mlpack { namespace tree { +template template -void HilbertRTreeSplit:: +void HilbertRTreeSplit:: SplitLeafNode(TreeType* tree, std::vector& relevels) { // If we are splitting the root node, we need will do things differently so @@ -30,7 +31,7 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) tree->NullifyData(); // Because this was a leaf node, numChildren must be 0. tree->Children()[(tree->NumChildren())++] = copy; - HilbertRTreeSplit::SplitLeafNode(copy, relevels); + SplitLeafNode(copy, relevels); return; } @@ -72,12 +73,13 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) RedistributePointsEvenly(parent, firstSibling, lastSibling); if (parent->NumChildren() == parent->MaxNumChildren() + 1) - HilbertRTreeSplit::SplitNonLeafNode(parent, relevels); + SplitNonLeafNode(parent, relevels); } +template template -bool HilbertRTreeSplit:: +bool HilbertRTreeSplit:: SplitNonLeafNode(TreeType* tree,std::vector& relevels) { // If we are splitting the root node, we need will do things differently so @@ -93,7 +95,7 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) tree->NullifyData(); tree->Children()[(tree->NumChildren())++] = copy; - HilbertRTreeSplit::SplitNonLeafNode(copy, relevels); + SplitNonLeafNode(copy, relevels); return true; } @@ -137,12 +139,13 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) RedistributeNodesEvenly(parent, firstSibling, lastSibling); if (parent->NumChildren() == parent->MaxNumChildren() + 1) - HilbertRTreeSplit::SplitNonLeafNode(parent, relevels); + SplitNonLeafNode(parent, relevels); return false; } +template template -bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent, size_t iTree, +bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent, size_t iTree, size_t &firstSibling, size_t &lastSibling) { size_t start = (iTree > splitOrder-1 ? iTree - splitOrder + 1 : 0); @@ -192,8 +195,9 @@ bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent, size_t iTree, return true; } +template template -void HilbertRTreeSplit:: +void HilbertRTreeSplit:: RedistributeNodesEvenly(const TreeType *parent, size_t firstSibling, size_t lastSibling) { @@ -254,8 +258,9 @@ RedistributeNodesEvenly(const TreeType *parent, } } +template template -void HilbertRTreeSplit:: +void HilbertRTreeSplit:: RedistributePointsEvenly(TreeType *parent, size_t firstSibling, size_t lastSibling) { @@ -308,7 +313,7 @@ RedistributePointsEvenly(TreeType *parent, parent->Children()[i]->MaxLeafSize()); } // Fix the largest Hilbert values of the siblings. - parent->AuxiliaryInfo().HilbertValue().UpdateHilbertValues(parent, firstSibling, lastSibling); + parent->AuxiliaryInfo().HilbertValue().RedistributeHilbertValues(parent, firstSibling, lastSibling); TreeType* root = parent; diff --git a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp index ac37908b2f..8f6a34c8ae 100644 --- a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp @@ -21,7 +21,12 @@ class NoAuxiliaryInformation /** * Some tree types require to save some properties at the insertion process. - * This method should return false if it does not handle the process. + * This method allows the auxiliary information the option of manipulating + * the tree in order to perform the insertion process. If the auxiliary + * information does that, then the method should return true; if the method + * returns false the RectangleTree performs its default behavior. + * @param node The node in which the point is being inserted. + * @param point The global number of the point being inserted. */ bool HandlePointInsertion(TreeType* , const size_t) { @@ -30,7 +35,14 @@ class NoAuxiliaryInformation /** * Some tree types require to save some properties at the insertion process. - * This method should return false if it does not handle the process. + * This method allows the auxiliary information the option of manipulating + * the tree in order to perform the insertion process. If the auxiliary + * information does that, then the method should return true; if the method + * returns false the RectangleTree performs its default behavior. + * @param node The node in which the nodeToInsert is being inserted. + * @param nodeToInsert The node being inserted. + * @param insertionLevel The level of the tree at which the nodeToInsert + * should be inserted. */ bool HandleNodeInsertion(TreeType* , TreeType* ,bool) { @@ -39,7 +51,12 @@ class NoAuxiliaryInformation /** * Some tree types require to save some properties at the deletion process. - * This method should return false if it does not handle the process. + * This method allows the auxiliary information the option of manipulating + * the tree in order to perform the deletion process. If the auxiliary + * information does that, then the method should return true; if the method + * returns false the RectangleTree performs its default behavior. + * @param node The node from which the point is being deleted. + * @param localIndex The local index of the point being deleted. */ bool HandlePointDeletion(TreeType* , const size_t) { @@ -48,7 +65,12 @@ class NoAuxiliaryInformation /** * Some tree types require to save some properties at the deletion process. - * This method should return false if it does not handle the process. + * This method allows the auxiliary information the option of manipulating + * the tree in order to perform the deletion process. If the auxiliary + * information does that, then the method should return true; if the method + * returns false the RectangleTree performs its default behavior. + * @param node The node from which the node is being deleted. + * @param nodeIndex The local index of the node being deleted. */ bool HandleNodeRemoval(TreeType* , const size_t) { @@ -56,8 +78,10 @@ class NoAuxiliaryInformation } /** - * Some tree types require to propagate the information downward. - * This method should return false if this is not the case. + * Some tree types require to propagate the information upward. + * This method should return false if this is not the case. If true is + * returned, the update will be propogated upward. + * @param node The node in which the auxiliary information being update. */ bool UpdateAuxiliaryInfo(TreeType* ) { @@ -65,11 +89,8 @@ class NoAuxiliaryInformation } /** - * Nothing to copy. + * Nullify the auxiliary information in order to prevent an invalid free. */ - void Copy(TreeType* , TreeType* ) - { } - void NullifyData() { } 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 c11d62c136..836752b7d3 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -881,7 +881,7 @@ void RectangleTreePoints()[i]; } - auxiliaryInfo.Copy(this,child); + auxiliaryInfo = child->AuxiliaryInfo(); count = child->Count(); child->SoftDelete(); diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp deleted file mode 100644 index 09f0403cb3..0000000000 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value.hpp +++ /dev/null @@ -1,219 +0,0 @@ -/** - * @file recursive_hilbert_value.hpp - * @author Mikhail Lozhnikov - * - * Defintion of the RecursiveHilbertValue class, a class that measures - * ordering of points recursively. - */ -#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_HPP -#define MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_HPP - -#include - -namespace mlpack { -namespace tree /** Trees and tree-building procedures. */ { - -constexpr int recursionDepth = 500; - -template -class RecursiveHilbertValue -{ - public: - //! Default constructor - RecursiveHilbertValue(); - - /** - * Construct this for the node tree. If the node is the root this method - * computes the Hilbert value for each point in the tree's dataset. - * @param node The node that stores this Hilbert value. - */ - template - RecursiveHilbertValue(const TreeType* tree); - - /** - * Create a Hilbert value object by copying from another one. - * @param other The Hilbert value object from which the value will be copied. - */ - RecursiveHilbertValue(const RecursiveHilbertValue& other); - - ~RecursiveHilbertValue(); - - //! This struct is designed in order to facilitate the recursion. - typedef struct tagCompareStruct - { - //! Lower bound - arma::Col Lo; - //! High bound - arma::Col Hi; - //! Permutation of axes - std::vector permutation; - //! Indicates that the axis should be inverted - std::vector inversion; - //! Indicates that the result should be inverted - arma::Col center; - arma::Col vec; - std::vector bits; - std::vector bits2; - bool invertResult; - int recursionLevel; - - - tagCompareStruct(size_t dim) : - Lo(dim), - Hi(dim), - permutation(dim), - inversion(dim), - center(dim), - vec(dim), - bits(dim), - bits2(dim), - invertResult(false), - recursionLevel(0) - { - for (size_t i = 0; i < dim; i++) - { - Lo[i] = std::numeric_limits::lowest(); - Hi[i] = std::numeric_limits::max(); - permutation[i] = i; - inversion[i] = false; - } - } - } CompareStruct; - - /** - * Compare two points. It returns 1 if the first point is greater than - * the second one, -1 if the first point is less than the second one and - * 0 if the Hilbert values of the points are equal. - * @param pt1 The first point. - * @param pt2 The second point. - */ - template - static int ComparePoints(const VecType1& pt1, const VecType2& pt2, - typename boost::enable_if>* = 0, - typename boost::enable_if>* = 0); - - /** - * Compare two Hilbert values. It returns 1 if the first value is greater than - * the second one, -1 if the first value is less than the second one and - * 0 if the values are equal. - * @param val1 The first Hilbert value. - * @param val2 The second Hilbert value. - */ - - static int CompareValues(const RecursiveHilbertValue& val1, - const RecursiveHilbertValue& val2); - - /** - * Compare the largest Hilbert value of the node with the val value. - * It returns 1 if the value of the node is greater than val, - * -1 if the value of the node is less than val and - * 0 if the values are equal. - * @param val The Hilbert value to compare with. - */ - int CompareWith(const RecursiveHilbertValue& val) const; - - /** - * Compare the largest Hilbert value of the node with the Hilbert value - * of the point. It returns 1 if the value of the node is greater than - * the value of the point, -1 if the value of the node is less than - * the value of the point and 0 if the values are equal. - * @param point The point to compare with. - */ - template - int CompareWith(const VecType& point, - typename boost::enable_if>* = 0) const; - - template - int CompareWithCachedPoint(const VecType& point, - typename boost::enable_if>* = 0) const; - - - /** - * Update the largest Hilbert value of the node. - * @param node The node in which the point is being inserted. - * @param point The number of the point being inserted. - */ - template - size_t InsertPoint(TreeType* node, const VecType& point, - typename boost::enable_if>* = 0); - - /** - * Update the largest Hilbert value of the node. - * @param node The node being inserted. - */ - template - void InsertNode(TreeType* node); - - /** - * Update the largest Hilbert value of the node. - * @param node The node from which another node is being deleted. - * @param nodeIndex The number of the node being deleted. - */ - template - void DeletePoint(TreeType* node, const size_t localIndex); - - /** - * Update the largest Hilbert value of the node. - * @param node The node from which another node is being deleted. - * @param nodeIndex The number of the node being deleted. - */ - template - void RemoveNode(TreeType* node, const size_t nodeIndex); - - - /** - * Copy the largest Hilbert value. - * @param dst The node to which the information is being copied. - * @param src The node from which the information is being copied. - */ - template - void Copy(TreeType* dst, TreeType* src); - - void NullifyData(); - - /** - * Update the largest Hilbert value. - * @param node The node in which the information should be updated. - */ - template - void UpdateLargestValue(TreeType* node); - - template - void UpdateHilbertValues(TreeType* parent, size_t firstSibling, - size_t lastSibling); - - //! Return the largest Hilbert value - const arma::Col* LargestValue() const { return largestValue; } - - //! Modify the largest Hilbert value - arma::Col*& LargestValue() { return largestValue; } - - private: - //! The point that has the largest Hilbert value. - arma::Col* largestValue; - bool ownsLargestValue; - bool hasLargestValue; - - /** - * Compare two points. It returns 1 if the first point is greater than - * the second one, -1 if the first point is less than the second one and - * 0 if the Hilbert values of the points are equal. - * @param pt1 The first point. - * @param pt2 The second point. - * @param comp An object of CompareStruct. - */ - template - static int ComparePoints(const VecType1& pt1, const VecType2& pt2, - CompareStruct& comp, typename boost::enable_if>* = 0, - typename boost::enable_if>* = 0); - public: - template - void Serialize(Archive& ar, const unsigned int /* version */); -}; -} // namespace tree -} // namespace mlpack - -// Include implementation -#include "recursive_hilbert_value_impl.hpp" - -#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp deleted file mode 100644 index 1b744cb902..0000000000 --- a/src/mlpack/core/tree/rectangle_tree/recursive_hilbert_value_impl.hpp +++ /dev/null @@ -1,347 +0,0 @@ -/** - * @file recursive_hilbert_value_impl.hpp - * @author Mikhail Lozhnikov - * - * Implementation of the RecursiveHilbertValue class, a class that measures - * ordering of points recursively. - */ -#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_IMPL_HPP -#define MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_IMPL_HPP - -namespace mlpack { -namespace tree /** Trees and tree-building procedures. */ { - -template -RecursiveHilbertValue::RecursiveHilbertValue() : - largestValue(NULL), - ownsLargestValue(false), - hasLargestValue(false) -{ - -} - -template -template -RecursiveHilbertValue:: -RecursiveHilbertValue(const TreeType* tree) : - largestValue(NULL), - ownsLargestValue(false), - hasLargestValue(false) -{ - if (!tree->Parent()) // This is the root node - ownsLargestValue = true; - else if (tree->Parent()->Children()[0]->IsLeaf()) - { - // This is a leaf node - assert(tree->Parent()->NumChildren() > 0); - ownsLargestValue = true; - } - - if (ownsLargestValue) - { - largestValue = new arma::Col(tree->Dataset().n_rows); - } -} - -template -RecursiveHilbertValue:: -RecursiveHilbertValue(const RecursiveHilbertValue& other) : - largestValue(const_cast*>(other.LargestValue())), - ownsLargestValue(other.ownsLargestValue), - hasLargestValue(other.hasLargestValue) -{ - -} - -template -RecursiveHilbertValue::~RecursiveHilbertValue() -{ - if (ownsLargestValue) - delete largestValue; -} - -template -template -int RecursiveHilbertValue:: -ComparePoints(const VecType1& pt1, const VecType2& pt2, - typename boost::enable_if>*, - typename boost::enable_if>* ) -{ - size_t dim = pt1.n_rows; - CompareStruct comp(dim); - - return ComparePoints(pt1, pt2, comp); -}; - -template -int RecursiveHilbertValue:: -CompareValues(const RecursiveHilbertValue& val1, - const RecursiveHilbertValue& val2) -{ - if (!val1.hasLargestValue && val2.hasLargestValue) - return -1; - else if (val1.hasLargestValue && !val2.hasLargestValue) - return 1; - else if (!val1.hasLargestValue && !val2.hasLargestValue) - return 0; - - return ComparePoints(*val1.LargestValue(), - *val2.LargestValue()); -} - -template -int RecursiveHilbertValue:: -CompareWith(const RecursiveHilbertValue& val) const -{ - if (!hasLargestValue) - return -1; - return CompareValues(*this, val); -} - -template -template -int RecursiveHilbertValue:: -CompareWith(const VecType& point, - typename boost::enable_if>* ) const -{ - if (!hasLargestValue) - return -1; - return ComparePoints(*largestValue, point); -} - -template -template -int RecursiveHilbertValue:: -CompareWithCachedPoint(const VecType& point, - typename boost::enable_if>* ) const -{ - return CompareWith(point); -} - -template -template -int RecursiveHilbertValue:: -ComparePoints(const VecType1& pt1, const VecType2& pt2, - CompareStruct& comp, typename boost::enable_if>*, - typename boost::enable_if>* ) - -{ - comp.center = comp.Hi * 0.5; - comp.vec = comp.Lo * 0.5; - - comp.center += comp.vec; - - // Get bits in order to use the Gray code - for (size_t i = 0; i < pt1.n_rows; i++) - { - size_t j = comp.permutation[i]; - comp.bits[i] = (pt1(j) > comp.center(j) && !comp.inversion[j]) || - (pt1(j) <= comp.center(j) && !comp.inversion[j]); - - comp.bits2[i] = (pt2(j) > comp.center(j) && !comp.inversion[j]) || - (pt2(j) <= comp.center(j) && !comp.inversion[j]); - } - - // Gray encode - for (size_t i = 1; i < pt1.n_rows; i++) - { - comp.bits[i] ^= comp.bits[i-1]; - comp.bits2[i] ^= comp.bits2[i-1]; - } - - if (comp.invertResult) - { - for (size_t i = 0; i < pt1.n_rows; i++) - { - comp.bits[i] = !comp.bits[i]; - comp.bits2[i] = !comp.bits2[i]; - } - } - - for (size_t i = 0; i < pt1.n_rows; i++) - { - if (comp.bits[i] < comp.bits2[i]) - return -1; - if (comp.bits[i] > comp.bits2[i]) - return 1; - } - - if (comp.recursionLevel >= recursionDepth) - return 0; - - comp.recursionLevel++; - - if (comp.bits[pt1.n_rows-1]) - comp.invertResult = !comp.invertResult; - - // Since the Hilbert curve is continuous we should permutate and intend - // coordinate axes depending on the position of the point - for (size_t i = 0; i < pt1.n_rows; i++) - { - size_t j = comp.permutation[i]; - size_t j0 = comp.permutation[0]; - if ((pt1(j) > comp.center(j) && !comp.inversion[j]) || - (pt1(j) <= comp.center(j) && !comp.inversion[j])) - comp.inversion[j0] = !comp.inversion[j0]; - else - { - size_t tmp; - tmp = comp.permutation[0]; - comp.permutation[0] = comp.permutation[i]; - comp.permutation[i] = tmp; - } - } - - // Choose an appropriate subhypercube - for (size_t i = 0; i < pt1.n_rows; i++) - { - if (pt1(i) > comp.center(i)) - comp.Lo(i) = comp.center(i); - else - comp.Hi(i) = comp.center(i); - } - - return ComparePoints(pt1, pt2, comp); -} - -template -template -size_t RecursiveHilbertValue:: -InsertPoint(TreeType* node, const VecType& point, - typename boost::enable_if>* ) -{ - if (node->IsLeaf()) - { - size_t i; - - for (i = 0; i < node->NumPoints(); i++) - if (ComparePoints(node->Dataset().col(node->Point(i)), point) > 0) - break; - if (i == node->NumPoints()) - *largestValue = point; - - hasLargestValue = true; - - // Propogate changes of the largest Hilbert value downward - TreeType* root = node->Parent(); - - while (root != NULL) - { - root->AuxiliaryInfo().HilbertValue().LargestValue() = largestValue; - root->AuxiliaryInfo().HilbertValue().hasLargestValue = true; - - root = root->Parent(); - } - - return i; - } - - return 0; -} - - -template -template -void RecursiveHilbertValue::InsertNode(TreeType* node) -{ - if (CompareWith(node->AuxiliaryInfo().HilbertValue()) < 0) - { - largestValue = node->AuxiliaryInfo().HilbertValue().LargestValue(); - hasLargestValue = true; - } -} - -template -template -void RecursiveHilbertValue:: -DeletePoint(TreeType* node, const size_t localIndex) -{ - if (node->NumPoints() <= 1) - { - hasLargestValue = false; - return; - } - if (localIndex + 1 == node->NumPoints()) - *largestValue = node->Dataset()[node->Point(localIndex-1)]; - -} - -template -template -void RecursiveHilbertValue:: -RemoveNode(TreeType* node, const size_t nodeIndex) -{ - if (node->NumChildren() <= 1) - { - hasLargestValue = false; - return; - } - if (nodeIndex + 1 == node->NumChildren()) - largestValue = node->Children()[nodeIndex-1]->AuxiliaryInfo.HilbertValue().LargestValue(); - -} - -template -template -void RecursiveHilbertValue::Copy(TreeType* dst, TreeType* src) -{ - dst->AuxiliaryInfo().HilbertValue().LargestValue() = - src->AuxiliaryInfo().HilbertValue().LargestValue(); - dst->AuxiliaryInfo().HilbertValue().hasLargestValue = - src->AuxiliaryInfo().HilbertValue().hasLargestValue; -} - -template -void RecursiveHilbertValue::NullifyData() -{ - ownsLargestValue = false; -} - -template -template -void RecursiveHilbertValue::UpdateLargestValue(TreeType* node) -{ - if (!node->IsLeaf()) - { - largestValue = (node->NumChildren() > 0 ? - node->Children()[node->NumChildren() - 1]->AuxiliaryInfo().HilbertValue().LargestValue() : NULL); - hasLargestValue = (node->NumChildren() > 0 ? - node->Children()[node->NumChildren() - 1]->AuxiliaryInfo().HilbertValue().hasLargestValue : false); - } -} - -template -template -void RecursiveHilbertValue:: -UpdateHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) -{ - for (size_t i = firstSibling; i<= lastSibling; i++) - { - RecursiveHilbertValue &value = - parent->Children()[i]->AuxiliaryInfo().HilbertValue(); - - assert(parent->Children()[i]->NumPoints() > 0); - - TreeType *child = parent->Children()[i]; - *value.LargestValue() = child->Dataset().col(child->Point(child->NumPoints() - 1)); - value.hasLargestValue = true; - } - -} - -template -template -void RecursiveHilbertValue:: -Serialize(Archive& ar, const unsigned int /* version */) -{ - using data::CreateNVP; - - ar & CreateNVP(largestValue, "largestValue"); - ar & CreateNVP(ownsLargestValue, "ownsLargestValue"); - ar & CreateNVP(hasLargestValue, "hasLargestValue"); -} - -} // namespace tree -} // namespace mlpack - -#endif //MLPACK_CORE_TREE_RECTANGLE_TREE_RECURSIVE_HILBERT_VALUE_IMPL_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/typedef.hpp b/src/mlpack/core/tree/rectangle_tree/typedef.hpp index c16b4181f0..6557b36143 100644 --- a/src/mlpack/core/tree/rectangle_tree/typedef.hpp +++ b/src/mlpack/core/tree/rectangle_tree/typedef.hpp @@ -119,26 +119,14 @@ using XTree = RectangleTree -using RecursiveHilbertRTreeAuxiliaryInformation = - HilbertRTreeAuxiliaryInformation; - -template -using RecursiveHilbertRTree = RectangleTree; - -template using DiscreteHilbertRTreeAuxiliaryInformation = HilbertRTreeAuxiliaryInformation; template -using DiscreteHilbertRTree = RectangleTree, HilbertRTreeDescentHeuristic, DiscreteHilbertRTreeAuxiliaryInformation>; diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp index a95bfe1821..ebfdd90506 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp @@ -43,7 +43,12 @@ class XTreeAuxiliaryInformation /** * Some tree types require to save some properties at the insertion process. - * This method should return false if it does not handle the process. + * This method allows the auxiliary information the option of manipulating + * the tree in order to perform the insertion process. If the auxiliary + * information does that, then the method should return true; if the method + * returns false the RectangleTree performs its default behavior. + * @param node The node in which the point is being inserted. + * @param point The global number of the point being inserted. */ bool HandlePointInsertion(TreeType* , const size_t) { @@ -52,7 +57,14 @@ class XTreeAuxiliaryInformation /** * Some tree types require to save some properties at the insertion process. - * This method should return false if it does not handle the process. + * This method allows the auxiliary information the option of manipulating + * the tree in order to perform the insertion process. If the auxiliary + * information does that, then the method should return true; if the method + * returns false the RectangleTree performs its default behavior. + * @param node The node in which the nodeToInsert is being inserted. + * @param nodeToInsert The node being inserted. + * @param insertionLevel The level of the tree at which the nodeToInsert + * should be inserted. */ bool HandleNodeInsertion(TreeType* , TreeType *,bool) { @@ -61,7 +73,12 @@ class XTreeAuxiliaryInformation /** * Some tree types require to save some properties at the deletion process. - * This method should return false if it does not handle the process. + * This method allows the auxiliary information the option of manipulating + * the tree in order to perform the deletion process. If the auxiliary + * information does that, then the method should return true; if the method + * returns false the RectangleTree performs its default behavior. + * @param node The node from which the point is being deleted. + * @param localIndex The local index of the point being deleted. */ bool HandlePointDeletion(TreeType* , const size_t) { @@ -70,7 +87,12 @@ class XTreeAuxiliaryInformation /** * Some tree types require to save some properties at the deletion process. - * This method should return false if it does not handle the process. + * This method allows the auxiliary information the option of manipulating + * the tree in order to perform the deletion process. If the auxiliary + * information does that, then the method should return true; if the method + * returns false the RectangleTree performs its default behavior. + * @param node The node from which the node is being deleted. + * @param nodeIndex The local index of the node being deleted. */ bool HandleNodeRemoval(TreeType* , const size_t) { @@ -78,8 +100,10 @@ class XTreeAuxiliaryInformation } /** - * Some tree types require to propagate the information downward. - * This method should return false if this is not the case. + * Some tree types require to propagate the information upward. + * This method should return false if this is not the case. If true is + * returned, the update will be propogated upward. + * @param node The node in which the auxiliary information being update. */ bool UpdateAuxiliaryInfo(TreeType* ) { @@ -87,18 +111,8 @@ class XTreeAuxiliaryInformation } /** - * Copy the auxiliary information from one node to another. - * @param dst The node to which the information being copied. - * @param src The node from which the information being copied. + * Nullify the auxiliary information in order to prevent an invalid free. */ - void Copy(TreeType* dst,TreeType* src) - { - dst->AuxiliaryInfo().NormalNodeMaxNumChildren() = - src->AuxiliaryInfo().NormalNodeMaxNumChildren(); - - dst->AuxiliaryInfo().SplitHistory() = src->AuxiliaryInfo().SplitHistory(); - } - void NullifyData() { } diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index ec85aeb7e5..e165a75c05 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -570,7 +570,7 @@ BOOST_AUTO_TEST_CASE(XTreeTraverserTest) } } -BOOST_AUTO_TEST_CASE(DiscreteHilbertRTreeTraverserTest) +BOOST_AUTO_TEST_CASE(HilbertRTreeTraverserTest) { arma::mat dataset; @@ -582,14 +582,14 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertRTreeTraverserTest) arma::Mat neighbors2; arma::mat distances2; - typedef DiscreteHilbertRTree,arma::mat> TreeType; TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); // Nearest neighbor search with the Hilbert R tree. NeighborSearch, arma::mat, - DiscreteHilbertRTree > knn1(&hilbertRTree, true); + HilbertRTree > knn1(&hilbertRTree, true); BOOST_REQUIRE_EQUAL(hilbertRTree.NumDescendants(), numP); @@ -611,50 +611,6 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertRTreeTraverserTest) } } -/* -BOOST_AUTO_TEST_CASE(RecursiveHilbertRTreeTraverserTest) -{ - arma::mat dataset; - - const int numP = 1000; - - dataset.randu(8, numP); // 1000 points in 8 dimensions. - arma::Mat neighbors1; - arma::mat distances1; - arma::Mat neighbors2; - arma::mat distances2; - - typedef RecursiveHilbertRTree,arma::mat> TreeType; - TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); - - // Nearest neighbor search with the Hilbert R tree. - - NeighborSearch, arma::mat, - RecursiveHilbertRTree > knn1(&hilbertRTree, true); - - BOOST_REQUIRE_EQUAL(hilbertRTree.NumDescendants(), numP); - - CheckSync(hilbertRTree); - CheckContainment(hilbertRTree); - CheckExactContainment(hilbertRTree); - CheckHierarchy(hilbertRTree); - - knn1.Search(5, neighbors1, distances1); - - // Nearest neighbor search the naive way. - KNN knn2(dataset, true, true); - - knn2.Search(5, neighbors2, distances2); - - for (size_t i = 0; i < neighbors1.size(); i++) - { - BOOST_REQUIRE_EQUAL(neighbors1[i], neighbors2[i]); - BOOST_REQUIRE_EQUAL(distances1[i], distances2[i]); - } -} -*/ - template void CheckHilbertOrdering(TreeType* tree) { @@ -691,12 +647,12 @@ void CheckHilbertOrdering(TreeType* tree) } } -BOOST_AUTO_TEST_CASE(DiscreteHilbertOrderingTest) +BOOST_AUTO_TEST_CASE(HilbertRTreeOrderingTest) { arma::mat dataset; dataset.randu(8, 1000); // 1000 points in 8 dimensions. - typedef DiscreteHilbertRTree,arma::mat> TreeType; TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); @@ -719,7 +675,7 @@ void CheckDiscreteHilbertValueSync(const TreeType* tree) arma::Col pointValue = HilbertValue::CalculateValue(tree->Dataset().col(tree->Points()[i])); - int equal = HilbertValue::CompareValues(value.LocalDataset()->col(i), pointValue); + int equal = HilbertValue::CompareValues(value.LocalHilbertValues()->col(i), pointValue); BOOST_REQUIRE_EQUAL(equal, 0); } @@ -734,27 +690,13 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueSyncTest) arma::mat dataset; dataset.randu(8, 1000); // 1000 points in 8 dimensions. - typedef DiscreteHilbertRTree,arma::mat> TreeType; TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); CheckDiscreteHilbertValueSync(&hilbertRTree); } -/* -BOOST_AUTO_TEST_CASE(RecursiveHilbertOrderingTest) -{ - arma::mat dataset; - dataset.randu(8, 1000); // 1000 points in 8 dimensions. - - typedef RecursiveHilbertRTree,arma::mat> TreeType; - TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); - - CheckHilbertOrdering(&hilbertRTree); -} -*/ - BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) { arma::vec point01(1); From aa33a92a321fceafbea00835817e3200290b87f1 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 27 Jun 2016 14:44:16 -0400 Subject: [PATCH 26/38] Fix style. --- .../rectangle_tree/discrete_hilbert_value.hpp | 164 ++++++++++-------- .../discrete_hilbert_value_impl.hpp | 156 +++++++++-------- .../hilbert_r_tree_auxiliary_information.hpp | 56 +++--- ...bert_r_tree_auxiliary_information_impl.hpp | 55 +++--- .../hilbert_r_tree_descent_heuristic.hpp | 25 +-- .../hilbert_r_tree_descent_heuristic_impl.hpp | 23 +-- .../rectangle_tree/hilbert_r_tree_split.hpp | 51 +++--- .../hilbert_r_tree_split_impl.hpp | 94 +++++----- .../no_auxiliary_information.hpp | 23 ++- .../r_star_tree_descent_heuristic.hpp | 14 +- .../r_tree_descent_heuristic.hpp | 2 +- .../x_tree_auxiliary_information.hpp | 35 ++-- 12 files changed, 386 insertions(+), 312 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp index 98eec7937e..715bc822eb 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -13,19 +13,30 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { +/** + * The DiscreteHilbertValue class stores Hilbert values for all of the points in + * a RectangleTree node, and calculates Hilbert values for new points. This + * implementation calculates the full discrete Hilbert value; for a + * d-dimensional vector filled with elements of size E, each Hilbert value will + * take dE space. + */ template class DiscreteHilbertValue { public: - typedef typename std::conditional::type HilbertElemType; - //! Default constructor + + //! Default constructor. DiscreteHilbertValue(); /** * Construct this for the node tree. If the node is the root this method * computes the Hilbert value for each point in the tree's dataset. + * * @param node The node that stores this Hilbert value. */ template @@ -33,6 +44,7 @@ class DiscreteHilbertValue /** * Create a Hilbert value object by copying from another one. + * * @param other The Hilbert value object from which the value will be copied. */ DiscreteHilbertValue(const DiscreteHilbertValue& other); @@ -41,10 +53,11 @@ class DiscreteHilbertValue ~DiscreteHilbertValue(); /** - * Compare two points. It returns 1 if the first point is greater than - * the second one, -1 if the first point is less than the second one and - * 0 if the Hilbert values of the points are equal. In order to do it - * this method computes the Hilbert values of the points. + * Compare two points. It returns 1 if the first point is greater than the + * second one, -1 if the first point is less than the second one and 0 if the + * Hilbert values of the points are equal. In order to do it this method + * computes the Hilbert values of the points. + * * @param pt1 The first point. * @param pt2 The second point. */ @@ -55,85 +68,95 @@ class DiscreteHilbertValue /** * Compare two Hilbert values. It returns 1 if the first value is greater than - * the second one, -1 if the first value is less than the second one and - * 0 if the values are equal. This method does not compute the Hilbert values. + * the second one, -1 if the first value is less than the second one and 0 if + * the values are equal. This method does not compute the Hilbert values. + * * @param val1 The first point. * @param val2 The second point. */ static int CompareValues(const DiscreteHilbertValue& val1, - const DiscreteHilbertValue& val2); + const DiscreteHilbertValue& val2); /** - * Compare the largest Hilbert value of the node with the val value. - * It returns 1 if the value of the node is greater than val, - * -1 if the value of the node is less than val and - * 0 if the values are equal. This method does not compute the Hilbert values. + * Compare the largest Hilbert value of the node with the val value. It + * returns 1 if the value of the node is greater than val, -1 if the value of + * the node is less than val and 0 if the values are equal. This method does + * not compute the Hilbert values. + * * @param val The Hilbert value to compare with. */ int CompareWith(const DiscreteHilbertValue& val) const; /** - * Compare the largest Hilbert value of the node with the Hilbert value - * of the point. It returns 1 if the value of the node is greater than - * the value of the point, -1 if the value of the node is less than - * the value of the point and 0 if the values are equal. - * This method computes the Hilbert value of the point. - * @param tree Not used - * @param val The point to compare with. + * Compare the largest Hilbert value of the node with the Hilbert value of the + * point. It returns 1 if the value of the node is greater than the value of + * the point, -1 if the value of the node is less than the value of the point + * and 0 if the values are equal. This method computes the Hilbert value of + * the point. + * + * @param pt The point to compare with. */ template int CompareWith(const VecType& pt, typename boost::enable_if>* = 0) const; /** - * Compare the largest Hilbert value of the node with the Hilbert value - * of the point. It returns 1 if the value of the node is greater than - * the value of the point, -1 if the value of the node is less than - * the value of the point and 0 if the values are equal. - * This method computes the Hilbert value of the point. - * @param tree Not used - * @param val The number of the point to compare with. + * Compare the Hilbert value of the cached point with the Hilbert value of the + * given point. It returns 1 if the value of the node is greater than the + * value of the point, -1 if the value of the node is less than the value of + * the point and 0 if the values are equal. This method computes the Hilbert + * value of the point. + * + * @param pt The point to compare with. */ template - int CompareWithCachedPoint(const VecType& pt, - typename boost::enable_if>* = 0) const; + int CompareWithCachedPoint( + const VecType& pt, + typename boost::enable_if>* = 0) const; /** - * Update the largest Hilbert value of the node and insert the point - * in the local dataset if the node is a leaf. + * Update the largest Hilbert value of the node and insert the point in the + * local dataset if the node is a leaf. + * * @param node The node in which the point is being inserted. * @param point The number of the point being inserted. */ template - size_t InsertPoint(TreeType *node, const VecType& pt, + size_t InsertPoint(TreeType *node, + const VecType& pt, typename boost::enable_if>* = 0); + /** * Update the largest Hilbert value of the node. + * * @param node The node being inserted. */ template void InsertNode(TreeType* node); /** - * Update the largest Hilbert value of the node and delete the point - * from the local dataset. + * Update the largest Hilbert value of the node and delete the point from the + * local dataset. + * * @param node The node from which the point is being deleted. - * @param localIndex The number of the point in the local dataset. + * @param localIndex The index of the point in the local dataset. */ template void DeletePoint(TreeType* node, const size_t localIndex); /** * Update the largest Hilbert value of the node. + * * @param node The node from which another node is being deleted. - * @param nodeIndex The number of the node being deleted. + * @param nodeIndex The index of the node being deleted. */ template void RemoveNode(TreeType* node, const size_t nodeIndex); /** - * Copy the largest Hilbert value and the local dataset + * Copy the largest Hilbert value and the local dataset. + * * @param dst The node to which the information is being copied. * @param src The node from which the information is being copied. */ @@ -142,20 +165,22 @@ class DiscreteHilbertValue /** * Copy the local Hilbert value's pointer. + * * @param val The DiscreteHilbertValue object from which the dataset * will be copied. */ - DiscreteHilbertValue& operator = (const DiscreteHilbertValue& val); + DiscreteHilbertValue& operator=(const DiscreteHilbertValue& val); /** * Nullify the localHilbertValues pointer in order to prevent an invalid free. */ void NullifyData(); - + /** - * Update the largest Hilbert value and the local Hilbert values of an intermediate node. - * The children of the node (or the points that the node contains) should be - * arranged according to their Hilbert values. + * Update the largest Hilbert value and the local Hilbert values of an + * intermediate node. The children of the node (or the points that the node + * contains) should be arranged according to their Hilbert values. + * * @param node The node in which the information should be updated. */ template @@ -165,63 +190,63 @@ class DiscreteHilbertValue * This method updates the largest Hilbert value of a leaf node and * redistributes the Hilbert values of points according to their new position * after the split algorithm. + * * @param parent The parent of the node that was split. - * @param firstSibling The first cooperationg sibling. + * @param firstSibling The first cooperating sibling. * @param lastSibling The last cooperating sibling. */ template - void RedistributeHilbertValues(TreeType* parent, size_t firstSibling, - size_t lastSibling); + void RedistributeHilbertValues(TreeType* parent, + const size_t firstSibling, + const size_t lastSibling); /** * Calculate the Hilbert value of the point pt. + * * @param pt The point for which the Hilbert value should be calculated. */ template - static arma::Col CalculateValue(const VecType& pt, - typename boost::enable_if>* = 0); + static arma::Col CalculateValue( + const VecType& pt, + typename boost::enable_if>* = 0); /** * Compare two Hilbert values. It returns 1 if the first value is greater than - * the second one, -1 if the first value is less than the second one and - * 0 if the values are equal. This method does not compute the Hilbert values. + * the second one, -1 if the first value is less than the second one and 0 if + * the values are equal. This method does not compute the Hilbert values. + * * @param value1 The first value. * @param value2 The second value. */ static int CompareValues(const arma::Col& value1, const arma::Col& value2); - //! Return the number of values - size_t NumValues() const - { return numValues; } + //! Return the number of values. + size_t NumValues() const { return numValues; } + //! Modify the number of values. + size_t& NumValues() { return numValues; } - //! Modify the number of values - size_t& NumValues() - { return numValues; } - - //! Return the local dataset + //! Return the Hilbert values. const arma::Mat* LocalHilbertValues() const { return localHilbertValues; } - - //! Modify the dataset + //! Modify the Hilbert values. arma::Mat*& LocalHilbertValues() { return localHilbertValues; } - - //! Modify the valueToInsert - arma::Col* ValueToInsert() { return valueToInsert; } - //! Modify the valueToInsert + //! Return the cached point (valueToInsert). const arma::Col* ValueToInsert() const { return valueToInsert; } + //! Modify the cached point (valueToInsert). + arma::Col* ValueToInsert() { return valueToInsert; } private: - //! The number of bits that we can store + //! The number of bits that we can store. static constexpr size_t order = sizeof(HilbertElemType) * CHAR_BIT; - //! The local Hilbert values + //! The local Hilbert values. arma::Mat* localHilbertValues; - //! Indicates that the node owns the localHilbertValues variable + //! Indicates that the node owns the localHilbertValues variable. bool ownsLocalHilbertValues; - //! The number of values in the localHilbertValues dataset + //! The number of values in the localHilbertValues dataset. size_t numValues; /** The Hilbert value of the point that is being inserted. * The pointer is the same in all nodes. The value is updated in InsertPoint() @@ -237,10 +262,11 @@ class DiscreteHilbertValue template void Serialize(Archive& ar, const unsigned int /* version */); }; + } // namespace tree } // namespace mlpack -// Include implementation +// Include implementation. #include "discrete_hilbert_value_impl.hpp" -#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_HPP +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index bf3cd27c72..22e2b13195 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -20,9 +20,7 @@ DiscreteHilbertValue::DiscreteHilbertValue() : numValues(0), valueToInsert(NULL), ownsValueToInsert(false) -{ - -} +{ } template DiscreteHilbertValue::~DiscreteHilbertValue() @@ -39,40 +37,39 @@ DiscreteHilbertValue::DiscreteHilbertValue(const TreeType* tree) : localHilbertValues(NULL), ownsLocalHilbertValues(false), numValues(0), - valueToInsert(tree->Parent() ? - tree->Parent()->AuxiliaryInfo().HilbertValue().ValueToInsert() : - new arma::Col(tree->Dataset().n_rows)), + valueToInsert(tree->Parent() ? + tree->Parent()->AuxiliaryInfo().HilbertValue().ValueToInsert() : + new arma::Col(tree->Dataset().n_rows)), ownsValueToInsert(tree->Parent() ? false : true) { - // Calculate the Hilbert value for all points - if (!tree->Parent()) // This is the root node + // Calculate the Hilbert value for all points. + if (!tree->Parent()) // This is the root node. ownsLocalHilbertValues = true; else if (tree->Parent()->Children()[0]->IsLeaf()) { - // This is a leaf node + // This is a leaf node. assert(tree->Parent()->NumChildren() > 0); ownsLocalHilbertValues = true; } - + if (ownsLocalHilbertValues) { - localHilbertValues = new arma::Mat(tree->Dataset().n_rows, - tree->MaxLeafSize() + 1); + localHilbertValues = new arma::Mat(tree->Dataset().n_rows, + tree->MaxLeafSize() + 1); } - } template DiscreteHilbertValue:: DiscreteHilbertValue(const DiscreteHilbertValue& other) : - localHilbertValues(const_cast*>(other.LocalHilbertValues())), + localHilbertValues( + const_cast*>(other.LocalHilbertValues())), ownsLocalHilbertValues(other.ownsLocalHilbertValues), numValues(other.NumValues()), - valueToInsert(const_cast*>(other.ValueToInsert())), + valueToInsert( + const_cast*>(other.ValueToInsert())), ownsValueToInsert(false) -{ - -} +{ } template template @@ -82,11 +79,12 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) { typedef typename VecType::elem_type VecElemType; arma::Col res(pt.n_rows); - // The number of bits for the exponent - const int numExpBits = - std::ceil(std::log2(std::numeric_limits::max_exponent - - std::numeric_limits::min_exponent + 1.0)); - // The number of bits for the mantissa + // Calculate the number of bits for the exponent. + const int numExpBits = std::ceil(std::log2( + std::numeric_limits::max_exponent - + std::numeric_limits::min_exponent + 1.0)); + + // Calculate the number of bits for the mantissa. const int numMantBits = order - numExpBits - 1; for (size_t i = 0; i < pt.n_rows; i++) @@ -103,46 +101,50 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) if (e < std::numeric_limits::min_exponent) { - HilbertElemType tmp = 1 << (std::numeric_limits::min_exponent - e); + HilbertElemType tmp = + 1 << (std::numeric_limits::min_exponent - e); + e = std::numeric_limits::min_exponent; normalizedVal /= tmp; } - // Extract the mantissa - HilbertElemType tmp = (HilbertElemType)1 << numMantBits; + // Extract the mantissa. + HilbertElemType tmp = (HilbertElemType) 1 << numMantBits; res(i) = std::floor(normalizedVal * tmp); - // Add the exponent - assert(res(i) < ((HilbertElemType)1 << numMantBits)); - res(i) |= ((HilbertElemType)(e - std::numeric_limits::min_exponent)) << numMantBits; + // Add the exponent. + assert(res(i) < ((HilbertElemType) 1 << numMantBits)); + res(i) |= ((HilbertElemType) + (e - std::numeric_limits::min_exponent)) << numMantBits; - assert(res(i) < ((HilbertElemType)1 << (order - 1)) - 1); - // Negative values should be inverted + assert(res(i) < ((HilbertElemType) 1 << (order - 1)) - 1); + + // Negative values should be inverted. if (sgn) { - res(i) = ((HilbertElemType)1 << (order - 1)) - 1 - res(i); + res(i) = ((HilbertElemType) 1 << (order - 1)) - 1 - res(i); assert((res(i) >> (order - 1)) == 0); } else { - res(i) |= (HilbertElemType)1 << (order - 1); + res(i) |= (HilbertElemType) 1 << (order - 1); assert((res(i) >> (order - 1)) == 1); } } - HilbertElemType M = (HilbertElemType)1 << (order - 1); + HilbertElemType M = (HilbertElemType) 1 << (order - 1); // Since the Hilbert curve is continuous we should permutate and intend - // coordinate axes depending on the position of the point + // coordinate axes depending on the position of the point. for (HilbertElemType Q = M; Q > 1; Q >>= 1) { HilbertElemType P = Q - 1; for (size_t i = 0; i < pt.n_rows; i++) { - if (res(i) & Q) // Invert + if (res(i) & Q) // Invert. res(0) ^= P; - else // Permutate + else // Permutate. { HilbertElemType t = (res(0) ^ res(i)) & P; res(0) ^= t; @@ -151,22 +153,22 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) } } - // Gray encode + // Gray encode. for (size_t i = 1; i < pt.n_rows; i++) - res(i) ^= res(i-1); + res(i) ^= res(i - 1); HilbertElemType t = 0; - // Some coordinate axes should be inverted + // Some coordinate axes should be inverted. for (HilbertElemType Q = M; Q > 1; Q >>= 1) - if ( res(pt.n_rows - 1) & Q) + if (res(pt.n_rows - 1) & Q) t ^= Q - 1; for (size_t i = 0; i < pt.n_rows; i++) res(i) ^= t; - // We should rearrange bits in order to compare two Hilbert values faster - arma::Col rearrangedResult(pt.n_rows,arma::fill::zeros); + // We should rearrange bits in order to compare two Hilbert values faster. + arma::Col rearrangedResult(pt.n_rows, arma::fill::zeros); for (size_t i = 0; i < order; i++) for (size_t j = 0; j < pt.n_rows; j++) @@ -174,7 +176,8 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) size_t bit = (i * pt.n_rows + j) % order; size_t row = (i * pt.n_rows + j) / order; - rearrangedResult(row) |= (((res(j) >> (order - 1 - i)) & 1) << (order - 1 - bit)); + rearrangedResult(row) |= (((res(j) >> (order - 1 - i)) & 1) << + (order - 1 - bit)); } return rearrangedResult; @@ -185,7 +188,7 @@ int DiscreteHilbertValue:: CompareValues(const arma::Col& value1, const arma::Col& value2) { - for (size_t i = 0;i < value1.n_rows; i++) + for (size_t i = 0; i < value1.n_rows; i++) { if (value1(i) > value2(i)) return 1; @@ -201,7 +204,8 @@ CompareValues(const arma::Col& value1, template template int DiscreteHilbertValue:: -ComparePoints(const VecType1& pt1, const VecType2& pt2, +ComparePoints(const VecType1& pt1, + const VecType2& pt2, typename boost::enable_if>*, typename boost::enable_if>*) { @@ -257,23 +261,24 @@ CompareWithCachedPoint(const VecType& , if (numValues == 0) return -1; - return CompareValues(localHilbertValues->col(numValues - 1),*valueToInsert); + return CompareValues(localHilbertValues->col(numValues - 1), *valueToInsert); } template template size_t DiscreteHilbertValue:: -InsertPoint(TreeType *node, const VecType& pt, - typename boost::enable_if>*) +InsertPoint(TreeType *node, + const VecType& pt, + typename boost::enable_if>*) { size_t i = 0; - // All point are inserted to the root node + // All points are inserted to the root node. if (!node->Parent()) *valueToInsert = CalculateValue(pt); if (node->IsLeaf()) { - // Find an appropriate place + // Find an appropriate place. for (i = 0; i < numValues; i++) if (CompareValues(localHilbertValues->col(i), *valueToInsert) > 0) break; @@ -283,7 +288,7 @@ InsertPoint(TreeType *node, const VecType& pt, localHilbertValues->col(i) = *valueToInsert; numValues++; - // Propogate changes of the largest Hilbert value downward + // Propagate changes of the largest Hilbert value downward. TreeType* root = node->Parent(); while (root != NULL) @@ -292,7 +297,6 @@ InsertPoint(TreeType *node, const VecType& pt, root = root->Parent(); } - } return i; @@ -303,7 +307,7 @@ template void DiscreteHilbertValue::InsertNode(TreeType* node) { DiscreteHilbertValue &val = node->AuxiliaryInfo().HilbertValue(); - + if (CompareWith(node,val) < 0) { localHilbertValues = val.LocalHilbertValues(); @@ -319,7 +323,7 @@ DeletePoint(TreeType* node, const size_t localIndex) // Delete the Hilbert value from the local dataset for (size_t i = numValues - 1; i > localIndex; i--) - localHilbertValues->col(i-1) = localHilbertValues->col(i); + localHilbertValues->col(i - 1) = localHilbertValues->col(i); numValues--; } @@ -338,11 +342,12 @@ RemoveNode(TreeType* node, const size_t nodeIndex) if (nodeIndex + 1 == node->NumChildren()) { // Update the largest Hilbert value if the value exists - TreeType* child = node->Children()[nodeIndex-1]; + TreeType* child = node->Children()[nodeIndex - 1]; if (child->AuxiliaryInfo.HilbertValue().NumValues() != 0) { numValues = child->AuxiliaryInfo.HilbertValue().NumValues(); - localHilbertValues = child->AuxiliaryInfo.HilbertValue().LocalHilbertValues(); + localHilbertValues = + child->AuxiliaryInfo.HilbertValue().LocalHilbertValues(); } else { @@ -354,7 +359,7 @@ RemoveNode(TreeType* node, const size_t nodeIndex) template DiscreteHilbertValue& DiscreteHilbertValue:: -operator = (const DiscreteHilbertValue& val) +operator=(const DiscreteHilbertValue& val) { localHilbertValues = const_cast* > (val.LocalHilbertValues()); @@ -377,32 +382,34 @@ void DiscreteHilbertValue::UpdateLargestValue(TreeType* node) if (!node->IsLeaf()) { // Update the largest Hilbert value - localHilbertValues = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().HilbertValue().LocalHilbertValues(); - numValues = node->Children()[node->NumChildren()-1]->AuxiliaryInfo().HilbertValue().NumValues(); + localHilbertValues = node->Children()[node->NumChildren() - + 1]->AuxiliaryInfo().HilbertValue().LocalHilbertValues(); + numValues = node->Children()[node->NumChildren() - + 1]->AuxiliaryInfo().HilbertValue().NumValues(); } } template template -void DiscreteHilbertValue:: -RedistributeHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibling) +void DiscreteHilbertValue::RedistributeHilbertValues( + TreeType* parent, + const size_t firstSibling, + const size_t lastSibling) { - // We should update the local dataset if points were redistributed - + // We need to update the local dataset if points were redistributed. size_t numPoints = 0; - - for (size_t i = firstSibling; i<= lastSibling; i++) + for (size_t i = firstSibling; i <= lastSibling; i++) numPoints += parent->Children()[i]->NumPoints(); - // Copy the local datasets - arma::Mat tmp(localHilbertValues->n_rows,numPoints); + // Copy the local Hilbert values. + arma::Mat tmp(localHilbertValues->n_rows, numPoints); size_t iPoint = 0; for (size_t i = firstSibling; i<= lastSibling; i++) { DiscreteHilbertValue &value = - parent->Children()[i]->AuxiliaryInfo().HilbertValue(); - + parent->Children()[i]->AuxiliaryInfo().HilbertValue(); + for (size_t j = 0; j < value.NumValues(); j++) { tmp.col(iPoint) = value.LocalHilbertValues()->col(j); @@ -413,12 +420,12 @@ RedistributeHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibl iPoint = 0; - // Redistribute the Hilbert values - for (size_t i = firstSibling; i<= lastSibling; i++) + // Redistribute the Hilbert values. + for (size_t i = firstSibling; i <= lastSibling; i++) { DiscreteHilbertValue &value = - parent->Children()[i]->AuxiliaryInfo().HilbertValue(); - + parent->Children()[i]->AuxiliaryInfo().HilbertValue(); + for (size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) { value.LocalHilbertValues()->col(j) = tmp.col(iPoint); @@ -428,7 +435,6 @@ RedistributeHilbertValues(TreeType* parent, size_t firstSibling, size_t lastSibl } assert(iPoint == numPoints); - } template diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp index a51a0ebd69..2477f9bba2 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp @@ -23,76 +23,83 @@ class HilbertRTreeAuxiliaryInformation HilbertRTreeAuxiliaryInformation(); /** - * Construct this as an axiliary information for the node node. + * Construct this as an auxiliary information for the given node. + * * @param node The node that stores this auxiliary information. */ HilbertRTreeAuxiliaryInformation(const TreeType* node); /** * Create an auxiliary information object by copying from the other node. + * * @param other The node from which the information will be copied. */ - HilbertRTreeAuxiliaryInformation(const HilbertRTreeAuxiliaryInformation& other); + HilbertRTreeAuxiliaryInformation( + const HilbertRTreeAuxiliaryInformation& other); /** - * The Hilbert R tree requires to insert points according to their - * Hilbert value. This method should take care of it. - * It returns false if it does nothing and true if it handles - * the insertion process. + * The Hilbert R tree requires to insert points according to their Hilbert + * value. This method should take care of it. It returns false if it does + * nothing and true if it handles the insertion process. + * * @param node The node in which the point is being inserted. * @param point The number of the point being inserted. */ bool HandlePointInsertion(TreeType* node, const size_t point); /** - * The Hilbert R tree requires to insert nodes according to their - * Hilbert value. This method should take care of it. - * It returns false if it does nothing and true if it handles - * the insertion process. + * The Hilbert R tree requires to insert nodes according to their Hilbert + * value. This method should take care of it. It returns false if it does + * nothing and true if it handles the insertion process. + * * @param node The node in which the nodeToInsert is being inserted. * @param nodeToInsert The node being inserted. * @param insertionLevel The level of the tree at which the nodeToInsert * should be inserted. */ bool HandleNodeInsertion(TreeType* node, - TreeType* nodeToInsert,bool insertionLevel); + TreeType* nodeToInsert, + bool insertionLevel); /** * The Hilbert R tree requires all points to be arranged according to their - * Hilbert value. This method should take care of saving this property - * after the deletion process. - * It returns false if it does nothing and true if it handles - * the deletion process. + * Hilbert value. This method should take care of saving this property after + * the deletion process. It returns false if it does nothing and true if it + * handles the deletion process. + * * @param node The node from which the point is being deleted. * @param localIndex The index of the point being deleted. */ - bool HandlePointDeletion(TreeType* node,const size_t localIndex); + bool HandlePointDeletion(TreeType* node, const size_t localIndex); /** * The Hilbert R tree requires all nodes to be arranged according to their - * Hilbert value. This method should take care of saving this property - * after the deletion process. - * It returns false if it does nothing and true if it handles - * the deletion process. + * Hilbert value. This method should take care of saving this property after + * the deletion process. It returns false if it does nothing and true if it + * handles the deletion process. + * * @param node The node from which the node is being deleted. * @param nodeIndex The index of the node being deleted. */ - bool HandleNodeRemoval(TreeType* node,const size_t nodeIndex); + bool HandleNodeRemoval(TreeType* node, const size_t nodeIndex); /** - * Update the auxiliary information in the node. The method returns true - * if the update should be propogated downward. + * Update the auxiliary information in the node. The method returns true if + * the update should be propogated downward. + * * @param node The node in which the auxiliary information being update. */ bool UpdateAuxiliaryInfo(TreeType* node); /** * Copy the auxiliary information from one node to another. + * * @param dst The node to which the information is being copied. * @param src The node from which the information is being copied. */ - void Copy(TreeType* dst,TreeType* src); + void Copy(TreeType* dst, TreeType* src); + //! Clear memory. void NullifyData(); private: @@ -111,7 +118,6 @@ class HilbertRTreeAuxiliaryInformation */ template void Serialize(Archive& ar, const unsigned int /* version */); - }; } // namespace tree diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index 3e5e5f3fb0..11d9e2b071 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -2,9 +2,8 @@ * @file hilbert_r_tree_auxiliary_information.hpp * @author Mikhail Lozhnikov * - * Implementation of the HilbertRTreeAuxiliaryInformation class, - * a class that provides some Hilbert r-tree specific information - * about the nodes. + * Implementation of the HilbertRTreeAuxiliaryInformation class, a class that + * provides some Hilbert r-tree specific information about the nodes. */ #ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_IMPL_HPP #define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_IMPL_HPP @@ -14,33 +13,27 @@ namespace mlpack { namespace tree { - template class HilbertValueType> HilbertRTreeAuxiliaryInformation:: HilbertRTreeAuxiliaryInformation() -{ - -}; +{ } template class HilbertValueType> HilbertRTreeAuxiliaryInformation:: HilbertRTreeAuxiliaryInformation(const TreeType* node) : hilbertValue(node) -{ - -}; +{ } template class HilbertValueType> HilbertRTreeAuxiliaryInformation:: -HilbertRTreeAuxiliaryInformation(const HilbertRTreeAuxiliaryInformation& other) : +HilbertRTreeAuxiliaryInformation( + const HilbertRTreeAuxiliaryInformation& other) : hilbertValue(other.HilbertValue()) -{ +{ } -}; - template class HilbertValueType> bool HilbertRTreeAuxiliaryInformation:: @@ -48,20 +41,21 @@ HandlePointInsertion(TreeType* node, const size_t point) { if (node->IsLeaf()) { - // Get the position at which the point should be inserted - // Update the largest Hilbert value of the node + // Get the position at which the point should be inserted, and then update + // the largest Hilbert value of the node. size_t pos = hilbertValue.InsertPoint(node, node->Dataset().col(point)); - // Move points + // Move points. for (size_t i = node->NumPoints(); i > pos; i--) - node->Points()[i] = node->Points()[i-1]; - // Insert the point + node->Points()[i] = node->Points()[i - 1]; + + // Insert the point. node->Points()[pos] = point; node->Count()++; } else { - // Calculate the Hilbert value + // Calculate the Hilbert value. hilbertValue.InsertPoint(node, node->Dataset().col(point)); } @@ -85,19 +79,19 @@ HandleNodeInsertion(TreeType* node, TreeType* nodeToInsert, bool insertionLevel) nodeToInsert->AuxiliaryInfo().HilbertValue()) < 0) break; - // Move nodes + // Move nodes. for (size_t i = node->NumChildren(); i > pos; i--) - node->Children()[i] = node->Children()[i-1]; + node->Children()[i] = node->Children()[i - 1]; - // Insert the node + // Insert the node. node->Children()[pos] = nodeToInsert; nodeToInsert->Parent() = node; - // Update the largest Hilbert value + // Update the largest Hilbert value. hilbertValue.InsertNode(nodeToInsert); } else - hilbertValue.InsertNode(nodeToInsert); // Update LHV + hilbertValue.InsertNode(nodeToInsert); // Update the largest Hilbert value. return true; } @@ -107,11 +101,11 @@ template:: HandlePointDeletion(TreeType* node, const size_t localIndex) { - // Update the largest Hilbert value + // Update the largest Hilbert value. hilbertValue.DeletePoint(node,localIndex); for (size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) - node->Points()[i-1] = node->Points()[i]; + node->Points()[i - 1] = node->Points()[i]; node->NumPoints()--; return true; @@ -122,11 +116,11 @@ template:: HandleNodeRemoval(TreeType* node, const size_t nodeIndex) { - // Update the largest Hilbert value + // Update the largest Hilbert value. hilbertValue.RemoveNode(node,nodeIndex); for (size_t i = nodeIndex + 1; nodeIndex < node->NumChildren(); i++) - node->Children()[i-1] = node->Children()[i]; + node->Children()[i - 1] = node->Children()[i]; node->NumChildren()--; return true; @@ -140,11 +134,10 @@ UpdateAuxiliaryInfo(TreeType* node) if (node->IsLeaf()) // Should already be updated return true; - TreeType *child = node->Children()[node->NumChildren()-1]; + TreeType* child = node->Children()[node->NumChildren() - 1]; if (hilbertValue.CompareWith(child->AuxiliaryInfo().hilbertValue()) < 0) { hilbertValue.Copy(node,child); -// hilbertValue = child->AuxiliaryInfo().hilbertValue(); return true; } return false; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp index c83532a009..2a01e6320e 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic.hpp @@ -2,8 +2,8 @@ * @file hilbert_r_tree_descent_heuristic.hpp * @author Mikhail Lozhnikov * - * Definition of HilbertRTreeDescentHeuristic, a class that chooses the best child of a - * node in an R tree when inserting a new point. + * Definition of HilbertRTreeDescentHeuristic, 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_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP #define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP @@ -13,13 +13,18 @@ namespace mlpack { namespace tree { +/** + * This class chooses the best child of a node in a Hilbert R tree when + * inserting a new point. This is done, in this class, by using the Hilbert + * value of the point to be inserted. + */ class HilbertRTreeDescentHeuristic { public: /** - * Evaluate the node using a heuristic. Returns the number of the node - * with minimum largest Hilbert value is greater than the Hilbert value of - * the point being inserted. + * Evaluate the node using a heuristic. Returns the number of the node with + * minimum largest Hilbert value that is greater than the Hilbert value of the + * point being inserted. * * @param node The node that is being evaluated. * @param point The number of the point that is being inserted. @@ -28,9 +33,9 @@ class HilbertRTreeDescentHeuristic static size_t ChooseDescentNode(const TreeType* node, const size_t point); /** - * Evaluate the node using a heuristic. Returns the number of the node - * with minimum largest Hilbert value is greater than the largest - * Hilbert value of the point being inserted. + * Evaluate the node using a heuristic. Returns the number of the node with + * minimum largest Hilbert value that is greater than the largest Hilbert + * value of the point being inserted. * * @param node The node that is being evaluated. * @param insertedNode The node that is being inserted. @@ -38,11 +43,11 @@ class HilbertRTreeDescentHeuristic template static size_t ChooseDescentNode(const TreeType* node, const TreeType* insertedNode); - }; + } // namespace tree } // namespace mlpack #include "hilbert_r_tree_descent_heuristic_impl.hpp" -#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp index 41fc492c6f..ca61000def 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp @@ -2,8 +2,8 @@ * @file hilbert_r_tree_descent_heuristic_impl.hpp * @author Mikhail Lozhnikov * - * Implementation of HilbertRTreeDescentHeuristic, a class that chooses the best child - * of a node in an R tree when inserting a new point. + * Implementation of HilbertRTreeDescentHeuristic, 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_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP #define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP @@ -14,33 +14,36 @@ namespace mlpack { namespace tree { template -size_t HilbertRTreeDescentHeuristic:: -ChooseDescentNode(const TreeType* node, const size_t point) +size_t HilbertRTreeDescentHeuristic::ChooseDescentNode( + const TreeType* node, + const size_t point) { size_t bestIndex = 0; for (bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if (node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWithCachedPoint(node->Dataset().col(point)) > 0) + if (node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue(). + CompareWithCachedPoint(node->Dataset().col(point)) > 0) break; return bestIndex; } template -size_t HilbertRTreeDescentHeuristic:: -ChooseDescentNode(const TreeType* node, const TreeType* insertedNode) +size_t HilbertRTreeDescentHeuristic::ChooseDescentNode( + const TreeType* node, + const TreeType* insertedNode) { size_t bestIndex = 0; for (bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++) - if (node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue().CompareWith(node,node->AuxiliaryInfo().HilbertValue()) > 0) + if (node->Children()[bestIndex]->AuxiliaryInfo().HilbertValue(). + CompareWith(node, node->AuxiliaryInfo().HilbertValue()) > 0) break; return bestIndex; } - } // namespace tree } // namespace mlpack -#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP +#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp index 5ffdda5e3b..90fd77e9e3 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split.hpp @@ -14,10 +14,12 @@ namespace mlpack { namespace tree /** Trees and tree-building procedures. */ { /** - * The order of the splitting policy. The Hilbert R tree splits a node - * on overflow, turnung splitOrder node to (splitOrder+1) nodes. + * The splitting procedure for the Hilbert R tree. The template parameter + * splitOrder is the order of the splitting policy. The Hilbert R tree splits a + * node on overflow, turning splitOrder nodes into (splitOrder + 1) nodes. + * + * @tparam splitOrder Number of nodes to split. */ - template class HilbertRTreeSplit { @@ -25,61 +27,68 @@ class HilbertRTreeSplit /** * Split a leaf node using the "default" algorithm. If necessary, this split * will propagate upwards through the tree. - * @param node. The node that is being split. + * + * @param node The node that is being split. * @param relevels Not used. */ template - static void SplitLeafNode(TreeType *tree,std::vector& relevels); + static 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. - * @param node. The node that is being split. + * + * @param node The node that is being split. * @param relevels Not used. */ template - static bool SplitNonLeafNode(TreeType *tree,std::vector& relevels); + static bool SplitNonLeafNode(TreeType* tree, std::vector& relevels); private: /** - * Try to find splitOrder cooperating siblings in order to redistribute - * their children evenly. Returns true on success. + * Try to find splitOrder cooperating siblings in order to redistribute their + * children evenly. Returns true on success. + * * @param parent The parent of of the overflowing node. * @param iTree The number of the overflowing node. * @param firstSibling The first cooperating sibling. * @param lastSibling The last cooperating sibling. */ template - static bool FindCooperatingSiblings(TreeType *parent,size_t iTree, - size_t &firstSibling,size_t &lastSibling); + static bool FindCooperatingSiblings(TreeType* parent, + const size_t iTree, + size_t& firstSibling, + size_t& lastSibling); /** - * Redistribute the children of the cooperating siblings evenly - * among them. + * Redistribute the children of the cooperating siblings evenly among them. + * * @param parent The parent of of the overflowing node. * @param firstSibling The first cooperating sibling. * @param lastSibling The last cooperating sibling. */ template - static void RedistributeNodesEvenly(const TreeType *parent, - size_t firstSibling,size_t lastSibling); + static void RedistributeNodesEvenly(const TreeType* parent, + const size_t firstSibling, + const size_t lastSibling); /** - * Redistribute the points of the cooperating siblings evenly - * among them. + * Redistribute the points of the cooperating siblings evenly among them. + * * @param parent The parent of of the overflowing node. * @param firstSibling The first cooperating sibling. * @param lastSibling The last cooperating sibling. */ template - static void RedistributePointsEvenly(TreeType *parent, - size_t firstSibling,size_t lastSibling); - + static void RedistributePointsEvenly(TreeType* parent, + const size_t firstSibling, + const size_t lastSibling); }; + } // namespace tree } // namespace mlpack -// Include implementation +// Include implementation. #include "hilbert_r_tree_split_impl.hpp" #endif diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index 8fff710382..1210f0d590 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -16,8 +16,8 @@ namespace tree { template template -void HilbertRTreeSplit:: -SplitLeafNode(TreeType* tree, std::vector& relevels) +void HilbertRTreeSplit::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 @@ -37,11 +37,11 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) TreeType* parent = tree->Parent(); size_t iTree = 0; - for (iTree = 0;parent->Children()[iTree] != tree; iTree++); + for (iTree = 0; parent->Children()[iTree] != tree; iTree++); - // Try to find splitOrder cooperating siblings in order to redistribute - // points among them and avoid split. - size_t firstSibling,lastSibling; + // Try to find splitOrder cooperating siblings in order to redistribute points + // among them and avoid split. + size_t firstSibling, lastSibling; if (FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling)) { RedistributePointsEvenly(parent, firstSibling, lastSibling); @@ -50,12 +50,11 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) // We can not find splitOrder cooperating siblings since they are all full. // We introduce new one instead. - size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); for (size_t i = parent->NumChildren(); i > iNewSibling ; i--) - parent->Children()[i] = parent->Children()[i-1]; + parent->Children()[i] = parent->Children()[i - 1]; parent->NumChildren()++; @@ -69,18 +68,17 @@ SplitLeafNode(TreeType* tree, std::vector& relevels) assert(firstSibling >= 0); assert(lastSibling < parent->NumChildren()); - // Redistribute the points among (splitOrder+1) cooperating siblings evenly. + // Redistribute the points among (splitOrder + 1) cooperating siblings evenly. RedistributePointsEvenly(parent, firstSibling, lastSibling); if (parent->NumChildren() == parent->MaxNumChildren() + 1) SplitNonLeafNode(parent, relevels); - } template template bool HilbertRTreeSplit:: -SplitNonLeafNode(TreeType* tree,std::vector& relevels) +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 @@ -102,11 +100,11 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) TreeType* parent = tree->Parent(); size_t iTree = 0; - for (iTree = 0;parent->Children()[iTree] != tree; iTree++); + for (iTree = 0; parent->Children()[iTree] != tree; iTree++); // Try to find splitOrder cooperating siblings in order to redistribute // children among them and avoid split. - size_t firstSibling,lastSibling; + size_t firstSibling, lastSibling; if (FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling)) { RedistributeNodesEvenly(parent, firstSibling, lastSibling); @@ -115,12 +113,11 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) // We can not find splitOrder cooperating siblings since they are all full. // We introduce new one instead. - size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); for (size_t i = parent->NumChildren(); i > iNewSibling ; i--) - parent->Children()[i] = parent->Children()[i-1]; + parent->Children()[i] = parent->Children()[i - 1]; parent->NumChildren()++; @@ -135,7 +132,7 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) assert(firstSibling >= 0); assert(lastSibling < parent->NumChildren()); - // Redistribute children among (splitOrder+1) cooperating siblings evenly. + // Redistribute children among (splitOrder + 1) cooperating siblings evenly. RedistributeNodesEvenly(parent, firstSibling, lastSibling); if (parent->NumChildren() == parent->MaxNumChildren() + 1) @@ -145,47 +142,52 @@ SplitNonLeafNode(TreeType* tree,std::vector& relevels) template template -bool HilbertRTreeSplit::FindCooperatingSiblings(TreeType *parent, size_t iTree, - size_t &firstSibling, size_t &lastSibling) +bool HilbertRTreeSplit::FindCooperatingSiblings( + TreeType* parent, + const size_t iTree, + size_t& firstSibling, + size_t& lastSibling) { - size_t start = (iTree > splitOrder-1 ? iTree - splitOrder + 1 : 0); + size_t start = (iTree > splitOrder - 1 ? iTree - splitOrder + 1 : 0); size_t end = (iTree + splitOrder <= parent->NumChildren() ? iTree + splitOrder : parent->NumChildren()); size_t iUnderfullSibling; - // Try to find empty space among cooperating siblings. + // Try to find empty space among cooperating siblings. if (parent->Children()[iTree]->NumChildren() != 0) { - for (iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) + for (iUnderfullSibling = start; iUnderfullSibling < end; + iUnderfullSibling++) if (parent->Children()[iUnderfullSibling]->NumChildren() < - parent->Children()[iUnderfullSibling]->MaxNumChildren() - 1) + parent->Children()[iUnderfullSibling]->MaxNumChildren() - 1) break; } else { - for (iUnderfullSibling = start; iUnderfullSibling < end; iUnderfullSibling++) + for (iUnderfullSibling = start; iUnderfullSibling < end; + iUnderfullSibling++) if (parent->Children()[iUnderfullSibling]->NumPoints() < - parent->Children()[iUnderfullSibling]->MaxLeafSize() - 1) + parent->Children()[iUnderfullSibling]->MaxLeafSize() - 1) break; } - if (iUnderfullSibling == end) // All nodes are full. + if (iUnderfullSibling == end) // All nodes are full. return false; if (iUnderfullSibling > iTree) { - lastSibling = (iTree + splitOrder-1 < parent->NumChildren() ? - iTree + splitOrder-1 : parent->NumChildren() - 1); - firstSibling = (lastSibling > splitOrder-1 ? + lastSibling = (iTree + splitOrder - 1 < parent->NumChildren() ? + iTree + splitOrder - 1 : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder - 1 ? lastSibling - splitOrder + 1 : 0); } else { - lastSibling = (iUnderfullSibling + splitOrder-1 < parent->NumChildren() ? - iUnderfullSibling + splitOrder-1 : parent->NumChildren() - 1); - firstSibling = (lastSibling > splitOrder-1 ? - lastSibling - splitOrder + 1 : 0); + lastSibling = (iUnderfullSibling + splitOrder - 1 < parent->NumChildren() ? + iUnderfullSibling + splitOrder - 1 : parent->NumChildren() - 1); + firstSibling = (lastSibling > splitOrder - 1 ? + lastSibling - splitOrder + 1 : 0); } assert(lastSibling - firstSibling <= splitOrder - 1); @@ -202,7 +204,7 @@ RedistributeNodesEvenly(const TreeType *parent, size_t firstSibling, size_t lastSibling) { size_t numChildren = 0; - size_t numChildrenPerNode,numRestChildren; + size_t numChildrenPerNode, numRestChildren; for (size_t i = firstSibling; i <= lastSibling; i++) numChildren += parent->Children()[i]->NumChildren(); @@ -226,20 +228,20 @@ RedistributeNodesEvenly(const TreeType *parent, iChild = 0; for (size_t i = firstSibling; i <= lastSibling; i++) { - // Since we redistribute children of a sibling we should - // recalculate the bound. + // Since we redistribute children of a sibling we should recalculate the + // bound. parent->Children()[i]->Bound().Clear(); for (size_t j = 0; j < numChildrenPerNode; j++) { - parent->Children()[i]->Bound() |= children[iChild]->Bound(); + parent->Children()[i]->Bound() |= children[iChild]->Bound(); parent->Children()[i]->Children()[j] = children[iChild]; children[iChild]->Parent() = parent->Children()[i]; iChild++; } if (numRestChildren > 0) { - parent->Children()[i]->Bound() |= children[iChild]->Bound(); + parent->Children()[i]->Bound() |= children[iChild]->Bound(); parent->Children()[i]->Children()[numChildrenPerNode] = children[iChild]; children[iChild]->Parent() = parent->Children()[i]; parent->Children()[i]->NumChildren() = numChildrenPerNode + 1; @@ -254,18 +256,20 @@ RedistributeNodesEvenly(const TreeType *parent, parent->Children()[i]->MaxNumChildren()); // Fix the largest Hilbert value of the sibling. - parent->Children()[i]->AuxiliaryInfo().HilbertValue().UpdateLargestValue(parent->Children()[i]); + parent->Children()[i]->AuxiliaryInfo().HilbertValue().UpdateLargestValue( + parent->Children()[i]); } } template template void HilbertRTreeSplit:: -RedistributePointsEvenly(TreeType *parent, - size_t firstSibling, size_t lastSibling) +RedistributePointsEvenly(TreeType* parent, + const size_t firstSibling, + const size_t lastSibling) { size_t numPoints = 0; - size_t numPointsPerNode,numRestPoints; + size_t numPointsPerNode, numRestPoints; for (size_t i = firstSibling; i <= lastSibling; i++) numPoints += parent->Children()[i]->NumPoints(); @@ -286,8 +290,8 @@ RedistributePointsEvenly(TreeType *parent, iPoint = 0; for (size_t i = firstSibling; i <= lastSibling; i++) { - // Since we redistribute points of a sibling we should - // recalculate the bound. + // Since we redistribute points of a sibling we should recalculate the + // bound. parent->Children()[i]->Bound().Clear(); size_t j; @@ -312,8 +316,10 @@ RedistributePointsEvenly(TreeType *parent, assert(parent->Children()[i]->NumPoints() <= parent->Children()[i]->MaxLeafSize()); } + // Fix the largest Hilbert values of the siblings. - parent->AuxiliaryInfo().HilbertValue().RedistributeHilbertValues(parent, firstSibling, lastSibling); + parent->AuxiliaryInfo().HilbertValue().RedistributeHilbertValues(parent, + firstSibling, lastSibling); TreeType* root = parent; diff --git a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp index 8f6a34c8ae..e3b5fd455b 100644 --- a/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/no_auxiliary_information.hpp @@ -15,9 +15,12 @@ template class NoAuxiliaryInformation { public: + //! Construct the auxiliary information object. NoAuxiliaryInformation() { }; - NoAuxiliaryInformation(const TreeType* ) { }; - NoAuxiliaryInformation(const TreeType& ) { }; + //! Construct the auxiliary information object. + NoAuxiliaryInformation(const TreeType* /* node */) { }; + //! Construct the auxiliary information object. + NoAuxiliaryInformation(const TreeType& /* node */) { }; /** * Some tree types require to save some properties at the insertion process. @@ -25,6 +28,7 @@ class NoAuxiliaryInformation * the tree in order to perform the insertion process. If the auxiliary * information does that, then the method should return true; if the method * returns false the RectangleTree performs its default behavior. + * * @param node The node in which the point is being inserted. * @param point The global number of the point being inserted. */ @@ -39,12 +43,15 @@ class NoAuxiliaryInformation * the tree in order to perform the insertion process. If the auxiliary * information does that, then the method should return true; if the method * returns false the RectangleTree performs its default behavior. + * * @param node The node in which the nodeToInsert is being inserted. * @param nodeToInsert The node being inserted. * @param insertionLevel The level of the tree at which the nodeToInsert * should be inserted. */ - bool HandleNodeInsertion(TreeType* , TreeType* ,bool) + bool HandleNodeInsertion(TreeType* /* node */, + TreeType* /* nodeToInsert */, + bool /* insertionLevel */) { return false; } @@ -55,10 +62,11 @@ class NoAuxiliaryInformation * the tree in order to perform the deletion process. If the auxiliary * information does that, then the method should return true; if the method * returns false the RectangleTree performs its default behavior. + * * @param node The node from which the point is being deleted. * @param localIndex The local index of the point being deleted. */ - bool HandlePointDeletion(TreeType* , const size_t) + bool HandlePointDeletion(TreeType* /* node */, const size_t /* localIndex */) { return false; } @@ -69,10 +77,11 @@ class NoAuxiliaryInformation * the tree in order to perform the deletion process. If the auxiliary * information does that, then the method should return true; if the method * returns false the RectangleTree performs its default behavior. + * * @param node The node from which the node is being deleted. * @param nodeIndex The local index of the node being deleted. */ - bool HandleNodeRemoval(TreeType* , const size_t) + bool HandleNodeRemoval(TreeType* /* node */, const size_t /* nodeIndex */) { return false; } @@ -81,9 +90,10 @@ class NoAuxiliaryInformation * Some tree types require to propagate the information upward. * This method should return false if this is not the case. If true is * returned, the update will be propogated upward. + * * @param node The node in which the auxiliary information being update. */ - bool UpdateAuxiliaryInfo(TreeType* ) + bool UpdateAuxiliaryInfo(TreeType* /* node */) { return false; } @@ -94,7 +104,6 @@ class NoAuxiliaryInformation void NullifyData() { } - /** * Serialize the information. */ 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 index 4c226eb05b..175b5dadc8 100644 --- 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 @@ -14,22 +14,22 @@ namespace mlpack { namespace tree { /** - * When descending a Rectangle tree to insert a point, we need to have a way to + * When descending a RectangleTree 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. + * heuristic is used to do so using the rules for the R* tree. */ class RStarTreeDescentHeuristic { public: /** - * Evaluate the node using a hueristic. The heuristic guarantees two things: + * Evaluate the node using a heuristic. 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. + * 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 number of the point that is being inserted. + * @param point The index of the point that is being inserted. */ template static size_t ChooseDescentNode(const TreeType* node, const size_t point); diff --git a/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp b/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp index c03f21828b..9254a42f3f 100644 --- a/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp +++ b/src/mlpack/core/tree/rectangle_tree/r_tree_descent_heuristic.hpp @@ -29,7 +29,7 @@ class RTreeDescentHeuristic * is greater than zero. * * @param node The node that is being evaluated. - * @param point The number of the point that is being inserted. + * @param point The index of the point that is being inserted. */ template static size_t ChooseDescentNode(const TreeType* node, const size_t point); diff --git a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp index ebfdd90506..f8a553fbc6 100644 --- a/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/x_tree_auxiliary_information.hpp @@ -11,6 +11,10 @@ namespace mlpack { namespace tree { +/** + * The XTreeAuxiliaryInformation class provides information specific to X trees + * for each node in a RectangleTree. + */ template class XTreeAuxiliaryInformation { @@ -23,50 +27,57 @@ class XTreeAuxiliaryInformation /** * Construct this whith the specified node. + * * @param node The node that stores this auxiliary information. */ XTreeAuxiliaryInformation(const TreeType* node) : - normalNodeMaxNumChildren(node->Parent() ? - node->Parent()->AuxiliaryInfo().NormalNodeMaxNumChildren() : - node->MaxNumChildren()), - splitHistory(node->Bound().Dim()) + normalNodeMaxNumChildren(node->Parent() ? + node->Parent()->AuxiliaryInfo().NormalNodeMaxNumChildren() : + node->MaxNumChildren()), + splitHistory(node->Bound().Dim()) { }; /** * Create an auxiliary information object by copying from the other node. + * * @param other The node from which the information will be copied. */ XTreeAuxiliaryInformation(const TreeType& other) : - normalNodeMaxNumChildren(other.AuxiliaryInfo().NormalNodeMaxNumChildren()), - splitHistory(other.AuxiliaryInfo().SplitHistory()) + normalNodeMaxNumChildren( + other.AuxiliaryInfo().NormalNodeMaxNumChildren()), + splitHistory(other.AuxiliaryInfo().SplitHistory()) { }; /** * Some tree types require to save some properties at the insertion process. - * This method allows the auxiliary information the option of manipulating - * the tree in order to perform the insertion process. If the auxiliary + * This method allows the auxiliary information the option of manipulating the + * tree in order to perform the insertion process. If the auxiliary * information does that, then the method should return true; if the method * returns false the RectangleTree performs its default behavior. + * * @param node The node in which the point is being inserted. * @param point The global number of the point being inserted. */ - bool HandlePointInsertion(TreeType* , const size_t) + bool HandlePointInsertion(TreeType* /* node */, const size_t /* point */) { return false; } /** * Some tree types require to save some properties at the insertion process. - * This method allows the auxiliary information the option of manipulating - * the tree in order to perform the insertion process. If the auxiliary + * This method allows the auxiliary information the option of manipulating the + * tree in order to perform the insertion process. If the auxiliary * information does that, then the method should return true; if the method * returns false the RectangleTree performs its default behavior. + * * @param node The node in which the nodeToInsert is being inserted. * @param nodeToInsert The node being inserted. * @param insertionLevel The level of the tree at which the nodeToInsert * should be inserted. */ - bool HandleNodeInsertion(TreeType* , TreeType *,bool) + bool HandleNodeInsertion(TreeType* /* node */, + TreeType* /* nodeToInsert */, + bool /* insertionLevel */) { return false; } From 0caf99c9231bc2be9710293a73fbf18b5a82d2f4 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 27 Jun 2016 15:02:48 -0400 Subject: [PATCH 27/38] Refactor tests a bit, and style fixes. --- src/mlpack/tests/rectangle_tree_test.cpp | 129 ++++++++++++----------- 1 file changed, 69 insertions(+), 60 deletions(-) diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index 4410f71b44..8f4719ba9c 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -158,9 +158,9 @@ void CheckExactContainment(const TreeType& tree) double max = -1.0 * DBL_MAX; for(size_t j = 0; j < tree.Count(); j++) { - if (tree.Dataset().col(tree.Points()[j])[i] < min) + if (tree.Dataset().col(tree.Point(j))[i] < min) min = tree.Dataset().col(tree.Points()[j])[i]; - if (tree.Dataset().col(tree.Points()[j])[i] > max) + if (tree.Dataset().col(tree.Point(j))[i] > max) max = tree.Dataset().col(tree.Points()[j])[i]; } BOOST_REQUIRE_EQUAL(max, tree.Bound()[i].Hi()); @@ -525,10 +525,8 @@ 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. BOOST_AUTO_TEST_CASE(XTreeTraverserTest) { arma::mat dataset; @@ -546,9 +544,8 @@ BOOST_AUTO_TEST_CASE(XTreeTraverserTest) TreeType xTree(dataset, 20, 6, 5, 2, 0); // Nearest neighbor search with the X tree. - - NeighborSearch, arma::mat, XTree > - knn1(&xTree, true); + NeighborSearch, arma::mat, + XTree> knn1(&xTree, true); BOOST_REQUIRE_EQUAL(xTree.NumDescendants(), numP); @@ -583,13 +580,12 @@ BOOST_AUTO_TEST_CASE(HilbertRTreeTraverserTest) arma::mat distances2; typedef HilbertRTree,arma::mat> TreeType; + NeighborSearchStat, arma::mat> TreeType; TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); // Nearest neighbor search with the Hilbert R tree. - NeighborSearch, arma::mat, - HilbertRTree > knn1(&hilbertRTree, true); + HilbertRTree> knn1(&hilbertRTree, true); BOOST_REQUIRE_EQUAL(hilbertRTree.NumDescendants(), numP); @@ -612,38 +608,34 @@ BOOST_AUTO_TEST_CASE(HilbertRTreeTraverserTest) } template -void CheckHilbertOrdering(TreeType* tree) +void CheckHilbertOrdering(const TreeType& tree) { - if(tree->IsLeaf()) + if (tree.IsLeaf()) { - for(size_t i = 0; i < tree->NumPoints() - 1; i++) - BOOST_REQUIRE_LE( - tree->AuxiliaryInfo().HilbertValue().ComparePoints( - tree->Dataset().col(tree->Points()[i]), - tree->Dataset().col(tree->Points()[i+1])), - 0); + for (size_t i = 0; i < tree.NumPoints() - 1; i++) + BOOST_REQUIRE_LE(tree.AuxiliaryInfo().HilbertValue().ComparePoints( + tree.Dataset().col(tree.Point(i)), + tree.Dataset().col(tree.Point(i + 1))), + 0); - BOOST_REQUIRE_EQUAL( - tree->AuxiliaryInfo().HilbertValue().CompareWith( - tree->Dataset().col(tree->Points()[tree->NumPoints() - 1])), - 0); + BOOST_REQUIRE_EQUAL(tree.AuxiliaryInfo().HilbertValue().CompareWith( + tree.Dataset().col(tree.Points()[tree.NumPoints() - 1])), + 0); } else { - for(size_t i = 0; i < tree->NumChildren() - 1; i++) - BOOST_REQUIRE_LE( - tree->AuxiliaryInfo().HilbertValue().CompareValues( - tree->Children()[i]->AuxiliaryInfo().HilbertValue(), - tree->Children()[i+1]->AuxiliaryInfo().HilbertValue()), - 0); + for (size_t i = 0; i < tree.NumChildren() - 1; i++) + BOOST_REQUIRE_LE(tree.AuxiliaryInfo().HilbertValue().CompareValues( + tree.Child(i).AuxiliaryInfo().HilbertValue(), + tree.Child(i + 1).AuxiliaryInfo().HilbertValue()), + 0); - BOOST_REQUIRE_EQUAL( - tree->AuxiliaryInfo().HilbertValue().CompareWith( - tree->Children()[tree->NumChildren() - 1]->AuxiliaryInfo().HilbertValue()), - 0); + BOOST_REQUIRE_EQUAL(tree.AuxiliaryInfo().HilbertValue().CompareWith( + tree.Child(tree.NumChildren() - 1).AuxiliaryInfo().HilbertValue()), + 0); - for(size_t i = 0; i < tree->NumChildren(); i++) - CheckHilbertOrdering(tree->Children()[i]); + for (size_t i = 0; i < tree.NumChildren(); i++) + CheckHilbertOrdering(tree.Child(i)); } } @@ -653,36 +645,37 @@ BOOST_AUTO_TEST_CASE(HilbertRTreeOrderingTest) dataset.randu(8, 1000); // 1000 points in 8 dimensions. typedef HilbertRTree,arma::mat> TreeType; + NeighborSearchStat, arma::mat> TreeType; TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); - CheckHilbertOrdering(&hilbertRTree); + CheckHilbertOrdering(hilbertRTree); } template -void CheckDiscreteHilbertValueSync(const TreeType* tree) +void CheckDiscreteHilbertValueSync(const TreeType& tree) { typedef DiscreteHilbertValue HilbertValue; typedef typename HilbertValue::HilbertElemType HilbertElemType; - if (tree->IsLeaf()) + if (tree.IsLeaf()) { - const HilbertValue &value = tree->AuxiliaryInfo().HilbertValue(); + const HilbertValue& value = tree.AuxiliaryInfo().HilbertValue(); - for (size_t i = 0; i < tree->NumPoints(); i++) + for (size_t i = 0; i < tree.NumPoints(); i++) { arma::Col pointValue = - HilbertValue::CalculateValue(tree->Dataset().col(tree->Points()[i])); + HilbertValue::CalculateValue(tree.Dataset().col(tree.Points()[i])); - int equal = HilbertValue::CompareValues(value.LocalHilbertValues()->col(i), pointValue); + const int equal = HilbertValue::CompareValues( + value.LocalHilbertValues().col(i), pointValue); BOOST_REQUIRE_EQUAL(equal, 0); } } else - for (size_t i = 0; i < tree->NumChildren(); i++) - CheckDiscreteHilbertValueSync(tree->Children()[i]); + for (size_t i = 0; i < tree.NumChildren(); i++) + CheckDiscreteHilbertValueSync(tree.Child(i)); } BOOST_AUTO_TEST_CASE(DiscreteHilbertValueSyncTest) @@ -705,47 +698,56 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point01[0] = -DBL_MAX; point02[0] = DBL_MAX; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); point01[0] = -DBL_MAX; point02[0] = -100; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); point01[0] = -100; point02[0] = -1; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); point01[0] = -1; point02[0] = -std::numeric_limits::min(); - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); point01[0] = -std::numeric_limits::min(); point02[0] = 0; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); point01[0] = 0; point02[0] = std::numeric_limits::min(); - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); point01[0] = std::numeric_limits::min(); point02[0] = 1; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); point01[0] = 1; point02[0] = 100; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); point01[0] = 100; point02[0] = DBL_MAX; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01,point02), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point01, + point02), -1); arma::vec point1(2); arma::vec point2(2); @@ -756,7 +758,8 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point2[0] = 0; point2[1] = 0; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1, + point2), -1); point1[0] = -1; point1[1] = -1; @@ -764,7 +767,8 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point2[0] = 1; point2[1] = -1; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1, + point2), -1); point1[0] = -1; point1[1] = -1; @@ -772,7 +776,8 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point2[0] = -1; point2[1] = 1; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1, + point2), -1); point1[0] = -DBL_MAX + 1; point1[1] = -DBL_MAX + 1; @@ -780,7 +785,8 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point2[0] = -1; point2[1] = -1; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1, + point2), -1); point1[0] = DBL_MAX * 0.75; point1[1] = DBL_MAX * 0.75; @@ -788,7 +794,8 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point2[0] = DBL_MAX * 0.25; point2[1] = DBL_MAX * 0.25; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1,point2), 1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point1, + point2), 1); arma::vec point3(4); arma::vec point4(4); @@ -803,7 +810,8 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point4[2] = 1.0; point4[3] = 1.0; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point3,point4), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point3, + point4), -1); point3[0] = -DBL_MAX; point3[1] = DBL_MAX; @@ -815,7 +823,8 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) point4[2] = DBL_MAX; point4[3] = DBL_MAX; - BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point3,point4), -1); + BOOST_REQUIRE_EQUAL(DiscreteHilbertValue::ComparePoints(point3, + point4), -1); } // Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low From 9dd66c7312ffcce6bc7b51aff00d38b75263f4b0 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Mon, 27 Jun 2016 15:12:50 -0400 Subject: [PATCH 28/38] Forgot to test before I pushed. I did not get much sleep last night... not running on all cylinders... --- src/mlpack/tests/rectangle_tree_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index 8f4719ba9c..58336b40b0 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -668,7 +668,7 @@ void CheckDiscreteHilbertValueSync(const TreeType& tree) HilbertValue::CalculateValue(tree.Dataset().col(tree.Points()[i])); const int equal = HilbertValue::CompareValues( - value.LocalHilbertValues().col(i), pointValue); + value.LocalHilbertValues()->col(i), pointValue); BOOST_REQUIRE_EQUAL(equal, 0); } @@ -687,7 +687,7 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueSyncTest) NeighborSearchStat,arma::mat> TreeType; TreeType hilbertRTree(dataset, 20, 6, 5, 2, 0); - CheckDiscreteHilbertValueSync(&hilbertRTree); + CheckDiscreteHilbertValueSync(hilbertRTree); } BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest) From 707efdc2a6e1fec461942cfa81323ea232f3427d Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Tue, 28 Jun 2016 00:38:37 +0300 Subject: [PATCH 29/38] Removed RectangleTree::Points() --- .../dual_tree_traverser_impl.hpp | 4 +- ...bert_r_tree_auxiliary_information_impl.hpp | 6 +-- .../hilbert_r_tree_split_impl.hpp | 6 +-- .../rectangle_tree/r_star_tree_split_impl.hpp | 12 +++--- .../tree/rectangle_tree/r_tree_split_impl.hpp | 32 ++++++++-------- .../tree/rectangle_tree/rectangle_tree.hpp | 10 ++--- .../rectangle_tree/rectangle_tree_impl.hpp | 19 +--------- .../single_tree_traverser_impl.hpp | 2 +- .../tree/rectangle_tree/x_tree_split_impl.hpp | 38 +++++++++---------- src/mlpack/tests/rectangle_tree_test.cpp | 12 +++--- 10 files changed, 62 insertions(+), 79 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser_impl.hpp b/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser_impl.hpp index c51cbfe99a..4a8c1d0584 100644 --- a/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/dual_tree_traverser_impl.hpp @@ -67,14 +67,14 @@ DualTreeTraverser::Traverse(RectangleTree& queryNode, { // Restore the traversal information. rule.TraversalInfo() = traversalInfo; - const double childScore = rule.Score(queryNode.Points()[query], + const double childScore = rule.Score(queryNode.Point(query), referenceNode); if (childScore == DBL_MAX) continue; // We don't require a search in this reference node. for(size_t ref = 0; ref < referenceNode.Count(); ++ref) - rule.BaseCase(queryNode.Points()[query], referenceNode.Points()[ref]); + rule.BaseCase(queryNode.Point(query), referenceNode.Point(ref)); numBaseCases += referenceNode.Count(); } diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index 11d9e2b071..0bdec76a56 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -47,10 +47,10 @@ HandlePointInsertion(TreeType* node, const size_t point) // Move points. for (size_t i = node->NumPoints(); i > pos; i--) - node->Points()[i] = node->Points()[i - 1]; + node->Point(i) = node->Point(i - 1); // Insert the point. - node->Points()[pos] = point; + node->Point(pos) = point; node->Count()++; } else @@ -105,7 +105,7 @@ HandlePointDeletion(TreeType* node, const size_t localIndex) hilbertValue.DeletePoint(node,localIndex); for (size_t i = localIndex + 1; localIndex < node->NumPoints(); i++) - node->Points()[i - 1] = node->Points()[i]; + node->Point(i - 1) = node->Point(i); node->NumPoints()--; return true; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp index 1210f0d590..afe30225e6 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_split_impl.hpp @@ -284,7 +284,7 @@ RedistributePointsEvenly(TreeType* parent, for (size_t i = firstSibling; i <= lastSibling; i++) { for (size_t j = 0; j < parent->Children()[i]->NumPoints(); j++) - points[iPoint++] = parent->Children()[i]->Points()[j]; + points[iPoint++] = parent->Children()[i]->Point(j); } iPoint = 0; @@ -298,13 +298,13 @@ RedistributePointsEvenly(TreeType* parent, for (j = 0; j < numPointsPerNode; j++) { parent->Children()[i]->Bound() |= parent->Dataset().col(points[iPoint]); - parent->Children()[i]->Points()[j] = points[iPoint]; + parent->Children()[i]->Point(j) = points[iPoint]; iPoint++; } if (numRestPoints > 0) { parent->Children()[i]->Bound() |= parent->Dataset().col(points[iPoint]); - parent->Children()[i]->Points()[j] = points[iPoint]; + parent->Children()[i]->Point(j) = points[iPoint]; parent->Children()[i]->Count() = numPointsPerNode + 1; numRestPoints--; iPoint++; 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 0ec5c51e4e..25cdb7a8d1 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 @@ -78,9 +78,9 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < p; i++) { // We start from the end of sorted. - pointIndices[i] = tree->Points()[sorted[sorted.size() - 1 - i].n]; + pointIndices[i] = tree->Point(sorted[sorted.size() - 1 - i].n); - root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], + root->DeletePoint(tree->Point(sorted[sorted.size() - 1 - i].n), relevels); } @@ -224,9 +224,9 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); + treeOne->InsertPoint(tree->Point(sorted[i].n)); else - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + treeTwo->InsertPoint(tree->Point(sorted[i].n)); } } else @@ -234,9 +234,9 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); + treeOne->InsertPoint(tree->Point(sorted[i].n)); else - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + treeTwo->InsertPoint(tree->Point(sorted[i].n)); } } 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 db67fbedb9..28c52a2367 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 @@ -199,10 +199,10 @@ void RTreeSplit::GetBoundSeeds(const TreeType *tree,int& iRet, int& jRet) ElemType score = 1.0; 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->Child(i).Bound()[k].Hi(), + tree->Child(j).Bound()[k].Hi()); + const ElemType loMin = std::min(tree->Child(i).Bound()[k].Lo(), + tree->Child(j).Bound()[k].Lo()); score *= (hiMax - loMin); } @@ -235,20 +235,20 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, treeOne->Count() = 0; treeTwo->Count() = 0; - treeOne->InsertPoint(oldTree->Points()[intI]); - treeTwo->InsertPoint(oldTree->Points()[intJ]); + treeOne->InsertPoint(oldTree->Point(intI)); + treeTwo->InsertPoint(oldTree->Point(intJ)); // If intJ is the last point in the tree, we need to switch the order so that // we remove the correct points. if (intI > intJ) { - oldTree->Points()[intI] = oldTree->Points()[--end]; // Decrement end. - oldTree->Points()[intJ] = oldTree->Points()[--end]; // Decrement end. + oldTree->Point(intI) = oldTree->Point(--end); // Decrement end. + oldTree->Point(intJ) = oldTree->Point(--end); // Decrement end. } else { - oldTree->Points()[intJ] = oldTree->Points()[--end]; // Decrement end. - oldTree->Points()[intI] = oldTree->Points()[--end]; // Decrement end. + oldTree->Point(intJ) = oldTree->Point(--end); // Decrement end. + oldTree->Point(intI) = oldTree->Point(--end); // Decrement end. } size_t numAssignedOne = 1; @@ -324,16 +324,16 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, // to the appropriate rectangle. if (bestRect == 1) { - treeOne->InsertPoint(oldTree->Points()[bestIndex]); + treeOne->InsertPoint(oldTree->Point(bestIndex)); numAssignedOne++; } else { - treeTwo->InsertPoint(oldTree->Points()[bestIndex]); + treeTwo->InsertPoint(oldTree->Point(bestIndex)); numAssignedTwo++; } - oldTree->Points()[bestIndex] = oldTree->Points()[--end]; // Decrement end. + oldTree->Point(bestIndex) = oldTree->Point(--end); // Decrement end. } // See if we need to satisfy the minimum fill. @@ -342,12 +342,12 @@ void RTreeSplit::AssignPointDestNode(TreeType* oldTree, if (numAssignedOne < numAssignedTwo) { for (size_t i = 0; i < end; i++) - treeOne->InsertPoint(oldTree->Points()[i]); + treeOne->InsertPoint(oldTree->Point(i)); } else { for (size_t i = 0; i < end; i++) - treeTwo->InsertPoint(oldTree->Points()[i]); + treeTwo->InsertPoint(oldTree->Point(i)); } } } @@ -432,7 +432,7 @@ void RTreeSplit::AssignNodeDestNode(TreeType* oldTree, // For each of the new rectangles, find the width in this dimension if // we add the rectangle at index to the new rectangle. const math::RangeType& range = - oldTree->Children()[index]->Bound()[i]; + oldTree->Child(index).Bound()[i]; newVolOne *= treeOne->Bound()[i].Contains(range) ? treeOne->Bound()[i].Width() : (range.Contains(treeOne->Bound()[i]) ? range.Width() : (range.Lo() < treeOne->Bound()[i].Lo() ? diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index 0f2d9cc6e3..bc2b1d6468 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -328,11 +328,6 @@ class RectangleTree //! Modify the dataset which the tree is built on. Be careful! MatType& Dataset() { return const_cast(*dataset); } - //! Get the points vector for this node. - const std::vector& Points() const { return points; } - //! Modify the points vector for this node. Be careful! - std::vector& Points() { return points; } - //! Get the metric which the tree uses. MetricType Metric() const { return MetricType(); } @@ -424,7 +419,10 @@ class RectangleTree * * @param index Index of point for which a dataset index is wanted. */ - size_t Point(const size_t index) const; + const size_t& Point(const size_t index) const { return points[index]; } + + //! Modify the index of a particular point in this node. + size_t& Point(const size_t index) { return points[index]; } //! Return the minimum distance to another node. ElemType MinDistance(const RectangleTree* other) 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 836752b7d3..064759c1b1 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree_impl.hpp @@ -154,7 +154,7 @@ RectangleTree( parentDistance(other.ParentDistance()), dataset(deepCopy ? new MatType(*other.dataset) : &other.Dataset()), ownsDataset(deepCopy), - points(other.Points()), + points(other.points), auxiliaryInfo(other.auxiliaryInfo) { if (deepCopy) @@ -659,21 +659,6 @@ inline size_t RectangleTree class AuxiliaryInformationType> -inline size_t RectangleTree::Point(const size_t index) const -{ - return points[index]; -} - /** * Split the tree. This calls the SplitType code to split a node. This method * should only be called on a leaf node. @@ -878,7 +863,7 @@ void RectangleTreeCount(); i++) { // In case the tree has a height of two. - points[i] = child->Points()[i]; + points[i] = child->Point(i); } auxiliaryInfo = child->AuxiliaryInfo(); diff --git a/src/mlpack/core/tree/rectangle_tree/single_tree_traverser_impl.hpp b/src/mlpack/core/tree/rectangle_tree/single_tree_traverser_impl.hpp index 6ada71c335..fca8fc476d 100644 --- a/src/mlpack/core/tree/rectangle_tree/single_tree_traverser_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/single_tree_traverser_impl.hpp @@ -49,7 +49,7 @@ SingleTreeTraverser::Traverse( if (referenceNode.IsLeaf()) { for (size_t i = 0; i < referenceNode.Count(); i++) - rule.BaseCase(queryIndex, referenceNode.Points()[i]); + rule.BaseCase(queryIndex, referenceNode.Point(i)); return; } 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 89372a450e..4cad881f67 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 @@ -67,7 +67,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < sorted.size(); i++) { sorted[i].d = tree->Metric().Evaluate(center, - tree->Dataset().col(tree->Points()[i])); + tree->Dataset().col(tree->Point(i))); sorted[i].n = i; } @@ -77,9 +77,9 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < p; i++) { // We start from the end of sorted. - pointIndices[i] = tree->Points()[sorted[sorted.size() - 1 - i].n]; + pointIndices[i] = tree->Point(sorted[sorted.size() - 1 - i].n); - root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n], + root->DeletePoint(tree->Point(sorted[sorted.size() - 1 - i].n), relevels); } @@ -115,7 +115,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) // Since we only have points in the leaf nodes, we only need to sort once. std::vector> sorted(tree->Count()); for (size_t i = 0; i < sorted.size(); i++) { - sorted[i].d = tree->Dataset().col(tree->Points()[i])[j]; + sorted[i].d = tree->Dataset().col(tree->Point(i))[j]; sorted[i].n = i; } @@ -148,25 +148,25 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) std::vector minG2(maxG1.size()); for (size_t k = 0; k < tree->Bound().Dim(); k++) { - minG1[k] = maxG1[k] = tree->Dataset().col(tree->Points()[sorted[0].n])[k]; + minG1[k] = maxG1[k] = tree->Dataset().col(tree->Point(sorted[0].n))[k]; minG2[k] = maxG2[k] = tree->Dataset().col( - tree->Points()[sorted[sorted.size() - 1].n])[k]; + tree->Point(sorted[sorted.size() - 1].n))[k]; for (size_t l = 1; l < tree->Count() - 1; l++) { if (l < cutOff) { - if (tree->Dataset().col(tree->Points()[sorted[l].n])[k] < minG1[k]) - minG1[k] = tree->Dataset().col(tree->Points()[sorted[l].n])[k]; - else if (tree->Dataset().col(tree->Points()[sorted[l].n])[k] > maxG1[k]) - maxG1[k] = tree->Dataset().col(tree->Points()[sorted[l].n])[k]; + if (tree->Dataset().col(tree->Point(sorted[l].n))[k] < minG1[k]) + minG1[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k]; + else if (tree->Dataset().col(tree->Point(sorted[l].n))[k] > maxG1[k]) + maxG1[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k]; } else { - if (tree->Dataset().col(tree->Points()[sorted[l].n])[k] < minG2[k]) - minG2[k] = tree->Dataset().col(tree->Points()[sorted[l].n])[k]; - else if (tree->Dataset().col(tree->Points()[sorted[l].n])[k] > maxG2[k]) - maxG2[k] = tree->Dataset().col(tree->Points()[sorted[l].n])[k]; + if (tree->Dataset().col(tree->Point(sorted[l].n))[k] < minG2[k]) + minG2[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k]; + else if (tree->Dataset().col(tree->Point(sorted[l].n))[k] > maxG2[k]) + maxG2[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k]; } } } @@ -214,7 +214,7 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) std::vector> sorted(tree->Count()); for (size_t i = 0; i < sorted.size(); i++) { - sorted[i].d = tree->Dataset().col(tree->Points()[i])[bestAxis]; + sorted[i].d = tree->Dataset().col(tree->Point(i))[bestAxis]; sorted[i].n = i; } @@ -233,9 +233,9 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); + treeOne->InsertPoint(tree->Point(sorted[i].n)); else - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + treeTwo->InsertPoint(tree->Point(sorted[i].n)); } } else @@ -243,9 +243,9 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector& relevels) for (size_t i = 0; i < tree->Count(); i++) { if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize()) - treeOne->InsertPoint(tree->Points()[sorted[i].n]); + treeOne->InsertPoint(tree->Point(sorted[i].n)); else - treeTwo->InsertPoint(tree->Points()[sorted[i].n]); + treeTwo->InsertPoint(tree->Point(sorted[i].n)); } } diff --git a/src/mlpack/tests/rectangle_tree_test.cpp b/src/mlpack/tests/rectangle_tree_test.cpp index 58336b40b0..3b079f0c28 100644 --- a/src/mlpack/tests/rectangle_tree_test.cpp +++ b/src/mlpack/tests/rectangle_tree_test.cpp @@ -77,7 +77,7 @@ std::vector GetAllPointsInTree(const TreeType& tree) { for (size_t i = 0; i < tree.Count(); i++) { - arma::vec* c = new arma::vec(tree.Dataset().col(tree.Points()[i])); + arma::vec* c = new arma::vec(tree.Dataset().col(tree.Point(i))); vec.push_back(c); } } @@ -130,7 +130,7 @@ void CheckContainment(const TreeType& tree) { for (size_t i = 0; i < tree.Count(); i++) BOOST_REQUIRE(tree.Bound().Contains( - tree.Dataset().unsafe_col(tree.Points()[i]))); + tree.Dataset().unsafe_col(tree.Point(i)))); } else { @@ -159,9 +159,9 @@ void CheckExactContainment(const TreeType& tree) for(size_t j = 0; j < tree.Count(); j++) { if (tree.Dataset().col(tree.Point(j))[i] < min) - min = tree.Dataset().col(tree.Points()[j])[i]; + min = tree.Dataset().col(tree.Point(j))[i]; if (tree.Dataset().col(tree.Point(j))[i] > max) - max = tree.Dataset().col(tree.Points()[j])[i]; + max = tree.Dataset().col(tree.Point(j))[i]; } BOOST_REQUIRE_EQUAL(max, tree.Bound()[i].Hi()); BOOST_REQUIRE_EQUAL(min, tree.Bound()[i].Lo()); @@ -619,7 +619,7 @@ void CheckHilbertOrdering(const TreeType& tree) 0); BOOST_REQUIRE_EQUAL(tree.AuxiliaryInfo().HilbertValue().CompareWith( - tree.Dataset().col(tree.Points()[tree.NumPoints() - 1])), + tree.Dataset().col(tree.Point(tree.NumPoints() - 1))), 0); } else @@ -665,7 +665,7 @@ void CheckDiscreteHilbertValueSync(const TreeType& tree) for (size_t i = 0; i < tree.NumPoints(); i++) { arma::Col pointValue = - HilbertValue::CalculateValue(tree.Dataset().col(tree.Points()[i])); + HilbertValue::CalculateValue(tree.Dataset().col(tree.Point(i))); const int equal = HilbertValue::CompareValues( value.LocalHilbertValues()->col(i), pointValue); From 05fbd27221931f20975e139f4984e5e05a11487f Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Tue, 28 Jun 2016 17:37:17 +0300 Subject: [PATCH 30/38] Some Hilbert R tree fixes --- .../core/tree/rectangle_tree/discrete_hilbert_value.hpp | 9 --------- .../tree/rectangle_tree/discrete_hilbert_value_impl.hpp | 4 ++-- .../hilbert_r_tree_auxiliary_information.hpp | 8 -------- .../hilbert_r_tree_auxiliary_information_impl.hpp | 4 ++-- 4 files changed, 4 insertions(+), 21 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp index 715bc822eb..3ce07887ea 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value.hpp @@ -154,15 +154,6 @@ class DiscreteHilbertValue template void RemoveNode(TreeType* node, const size_t nodeIndex); - /** - * Copy the largest Hilbert value and the local dataset. - * - * @param dst The node to which the information is being copied. - * @param src The node from which the information is being copied. - */ - template - void Copy(TreeType* dst, TreeType* src); - /** * Copy the local Hilbert value's pointer. * diff --git a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp index 22e2b13195..8f06578aa6 100644 --- a/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/discrete_hilbert_value_impl.hpp @@ -101,8 +101,8 @@ CalculateValue(const VecType& pt,typename boost::enable_if>*) if (e < std::numeric_limits::min_exponent) { - HilbertElemType tmp = - 1 << (std::numeric_limits::min_exponent - e); + HilbertElemType tmp = (HilbertElemType) 1 << + (std::numeric_limits::min_exponent - e); e = std::numeric_limits::min_exponent; normalizedVal /= tmp; diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp index 2477f9bba2..8c0b4aea7d 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information.hpp @@ -91,14 +91,6 @@ class HilbertRTreeAuxiliaryInformation */ bool UpdateAuxiliaryInfo(TreeType* node); - /** - * Copy the auxiliary information from one node to another. - * - * @param dst The node to which the information is being copied. - * @param src The node from which the information is being copied. - */ - void Copy(TreeType* dst, TreeType* src); - //! Clear memory. void NullifyData(); diff --git a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp index 11d9e2b071..b01d3d16fd 100644 --- a/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp +++ b/src/mlpack/core/tree/rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp @@ -135,9 +135,9 @@ UpdateAuxiliaryInfo(TreeType* node) return true; TreeType* child = node->Children()[node->NumChildren() - 1]; - if (hilbertValue.CompareWith(child->AuxiliaryInfo().hilbertValue()) < 0) + if (hilbertValue.CompareWith(child->AuxiliaryInfo().HilbertValue()) < 0) { - hilbertValue.Copy(node,child); + hilbertValue = node->AuxiliaryInfo().HilbertValue(); return true; } return false; From a46b834654dc4f25b95675fa6215ec147ead2e7f Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Tue, 28 Jun 2016 17:38:02 +0300 Subject: [PATCH 31/38] Added the Hilbert R tree to NSModel --- src/mlpack/methods/neighbor_search/kfn_main.cpp | 6 ++++-- src/mlpack/methods/neighbor_search/knn_main.cpp | 6 ++++-- src/mlpack/methods/neighbor_search/ns_model.hpp | 6 ++++-- src/mlpack/methods/neighbor_search/ns_model_impl.hpp | 6 ++++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp index 163e8ed741..254dc52b50 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', " - "'x', 'ball'.", "t", "kd"); + "'x', 'ball', 'hilbert-r'.", "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"); @@ -186,9 +186,11 @@ int main(int argc, char *argv[]) tree = KFNModel::BALL_TREE; else if (treeType == "x") tree = KFNModel::X_TREE; + else if (treeType == "hilbert-r") + tree = KFNModel::HILBERT_R_TREE; else Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are " - << "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl; + << "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << 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 880f5db90f..aad1991155 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', " - "'x', 'ball'.", "t", "kd"); + "'x', 'ball', 'hilbert-r'.", "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 " @@ -172,9 +172,11 @@ int main(int argc, char *argv[]) tree = KNNModel::BALL_TREE; else if (treeType == "x") tree = KNNModel::X_TREE; + else if (treeType == "hilbert-r") + tree = KNNModel::HILBERT_R_TREE; else Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are " - << "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl; + << "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << 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 711d640703..55e9e91dbf 100644 --- a/src/mlpack/methods/neighbor_search/ns_model.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model.hpp @@ -229,7 +229,8 @@ class NSModel R_TREE, R_STAR_TREE, BALL_TREE, - X_TREE + X_TREE, + HILBERT_R_TREE }; private: @@ -253,7 +254,8 @@ class NSModel NSType*, NSType*, NSType*, - NSType*> nSearch; + NSType*, + NSType*> nSearch; public: /** diff --git a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp index da8ab07f98..ae34feba75 100644 --- a/src/mlpack/methods/neighbor_search/ns_model_impl.hpp +++ b/src/mlpack/methods/neighbor_search/ns_model_impl.hpp @@ -382,6 +382,10 @@ void NSModel::BuildModel(arma::mat&& referenceSet, case X_TREE: nSearch = new NSType(naive, singleMode, epsilon); break; + case HILBERT_R_TREE: + nSearch = new NSType(naive, singleMode, + epsilon); + break; } TrainVisitor tn(std::move(referenceSet), leafSize); @@ -460,6 +464,8 @@ std::string NSModel::TreeName() const return "ball tree"; case X_TREE: return "X tree"; + case HILBERT_R_TREE: + return "Hilbert R tree"; default: return "unknown tree"; } From 8716e4ac73fbdd8267d177f5cbf4c68f859e291c Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Tue, 28 Jun 2016 17:38:53 +0300 Subject: [PATCH 32/38] Added the Hilbert R tree to RSModel --- .../range_search/range_search_main.cpp | 6 ++++-- src/mlpack/methods/range_search/rs_model.cpp | 21 ++++++++++++++++++- src/mlpack/methods/range_search/rs_model.hpp | 5 ++++- .../methods/range_search/rs_model_impl.hpp | 14 +++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/range_search/range_search_main.cpp b/src/mlpack/methods/range_search/range_search_main.cpp index 3606950e2f..ca3f4330de 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', " - "'x', 'ball'.", "t", "kd"); + "'x', 'ball', 'hilbert-r'.", "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"); @@ -173,9 +173,11 @@ int main(int argc, char *argv[]) tree = RSModel::BALL_TREE; else if (treeType == "x") tree = RSModel::X_TREE; + else if (treeType == "hilbert-r") + tree = RSModel::HILBERT_R_TREE; else Log::Fatal << "Unknown tree type '" << treeType << "; valid choices are " - << "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl; + << "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << 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 e0cdb18da0..1bf565ab67 100644 --- a/src/mlpack/methods/range_search/rs_model.cpp +++ b/src/mlpack/methods/range_search/rs_model.cpp @@ -22,7 +22,8 @@ RSModel::RSModel(TreeTypes treeType, bool randomBasis) : rTreeRS(NULL), rStarTreeRS(NULL), ballTreeRS(NULL), - xTreeRS(NULL) + xTreeRS(NULL), + hilbertRTreeRS(NULL) { // Nothing to do. } @@ -122,6 +123,11 @@ void RSModel::BuildModel(arma::mat&& referenceSet, xTreeRS = new RSType(move(referenceSet), naive, singleMode); break; + + case HILBERT_R_TREE: + hilbertRTreeRS = new RSType(move(referenceSet), naive, + singleMode); + break; } if (!naive) @@ -231,6 +237,10 @@ void RSModel::Search(arma::mat&& querySet, case X_TREE: xTreeRS->Search(querySet, range, neighbors, distances); break; + + case HILBERT_R_TREE: + hilbertRTreeRS->Search(querySet, range, neighbors, distances); + break; } } @@ -273,6 +283,10 @@ void RSModel::Search(const math::Range& range, case X_TREE: xTreeRS->Search(range, neighbors, distances); break; + + case HILBERT_R_TREE: + hilbertRTreeRS->Search(range, neighbors, distances); + break; } } @@ -293,6 +307,8 @@ std::string RSModel::TreeName() const return "ball tree"; case X_TREE: return "X tree"; + case HILBERT_R_TREE: + return "Hilbert R tree"; default: return "unknown tree"; } @@ -313,6 +329,8 @@ void RSModel::CleanMemory() delete ballTreeRS; if (xTreeRS) delete xTreeRS; + if (hilbertRTreeRS) + delete hilbertRTreeRS; kdTreeRS = NULL; coverTreeRS = NULL; @@ -320,4 +338,5 @@ void RSModel::CleanMemory() rStarTreeRS = NULL; ballTreeRS = NULL; xTreeRS = NULL; + hilbertRTreeRS = NULL; } diff --git a/src/mlpack/methods/range_search/rs_model.hpp b/src/mlpack/methods/range_search/rs_model.hpp index 9598981ff4..d256c31984 100644 --- a/src/mlpack/methods/range_search/rs_model.hpp +++ b/src/mlpack/methods/range_search/rs_model.hpp @@ -29,7 +29,8 @@ class RSModel R_TREE, R_STAR_TREE, BALL_TREE, - X_TREE + X_TREE, + HILBERT_R_TREE }; private: @@ -60,6 +61,8 @@ class RSModel RSType* ballTreeRS; //! X tree based range search object (NULL if not in use). RSType* xTreeRS; + //! Hilbert R tree based range search object (NULL if not in use). + RSType* hilbertRTreeRS; public: /** diff --git a/src/mlpack/methods/range_search/rs_model_impl.hpp b/src/mlpack/methods/range_search/rs_model_impl.hpp index 5a71faab4e..0f308d4712 100644 --- a/src/mlpack/methods/range_search/rs_model_impl.hpp +++ b/src/mlpack/methods/range_search/rs_model_impl.hpp @@ -53,6 +53,10 @@ void RSModel::Serialize(Archive& ar, const unsigned int /* version */) case X_TREE: ar & CreateNVP(xTreeRS, "range_search_model"); break; + + case HILBERT_R_TREE: + ar & CreateNVP(hilbertRTreeRS, "range_search_model"); + break; } } @@ -70,6 +74,8 @@ inline const arma::mat& RSModel::Dataset() const return ballTreeRS->ReferenceSet(); else if (xTreeRS) return xTreeRS->ReferenceSet(); + else if (hilbertRTreeRS) + return hilbertRTreeRS->ReferenceSet(); throw std::runtime_error("no range search model initialized"); } @@ -88,6 +94,8 @@ inline bool RSModel::SingleMode() const return ballTreeRS->SingleMode(); else if (xTreeRS) return xTreeRS->SingleMode(); + else if (hilbertRTreeRS) + return hilbertRTreeRS->SingleMode(); throw std::runtime_error("no range search model initialized"); } @@ -106,6 +114,8 @@ inline bool& RSModel::SingleMode() return ballTreeRS->SingleMode(); else if (xTreeRS) return xTreeRS->SingleMode(); + else if (hilbertRTreeRS) + return hilbertRTreeRS->SingleMode(); throw std::runtime_error("no range search model initialized"); } @@ -124,6 +134,8 @@ inline bool RSModel::Naive() const return ballTreeRS->Naive(); else if (xTreeRS) return xTreeRS->Naive(); + else if (hilbertRTreeRS) + return hilbertRTreeRS->Naive(); throw std::runtime_error("no range search model initialized"); } @@ -142,6 +154,8 @@ inline bool& RSModel::Naive() return ballTreeRS->Naive(); else if (xTreeRS) return xTreeRS->Naive(); + else if (hilbertRTreeRS) + return hilbertRTreeRS->Naive(); throw std::runtime_error("no range search model initialized"); } From d8c6ce46f8fe2ba85010c0abaf66fde036ae028a Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Tue, 28 Jun 2016 17:39:29 +0300 Subject: [PATCH 33/38] Added the Hilbert R tree to RAModel --- src/mlpack/methods/rann/krann_main.cpp | 6 ++- src/mlpack/methods/rann/ra_model.hpp | 5 +- src/mlpack/methods/rann/ra_model_impl.hpp | 56 ++++++++++++++++++++++- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/mlpack/methods/rann/krann_main.cpp b/src/mlpack/methods/rann/krann_main.cpp index 5fede8481d..bcd57e188b 100644 --- a/src/mlpack/methods/rann/krann_main.cpp +++ b/src/mlpack/methods/rann/krann_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 " - "'x', 'r-star'.", "t", "kd"); + "'x', 'r-star', 'hilbert-r'.", "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 " @@ -172,9 +172,11 @@ int main(int argc, char *argv[]) tree = RANNModel::R_STAR_TREE; else if (treeType == "x") tree = RANNModel::X_TREE; + else if (treeType == "hilbert-r") + tree = RANNModel::HILBERT_R_TREE; else Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are " - << "'kd', 'cover', 'r', 'r-star' and 'x'." << endl; + << "'kd', 'cover', 'r', 'r-star', 'x' and 'hilbert-r'." << 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 a04107fce7..2c929796d9 100644 --- a/src/mlpack/methods/rann/ra_model.hpp +++ b/src/mlpack/methods/rann/ra_model.hpp @@ -40,7 +40,8 @@ class RAModel COVER_TREE, R_TREE, R_STAR_TREE, - X_TREE + X_TREE, + HILBERT_R_TREE }; private: @@ -73,6 +74,8 @@ class RAModel RAType* rStarTreeRA; //! Non-NULL if the X tree is used. RAType* xTreeRA; + //! Non-NULL if the Hilbert R tree is used. + RAType* hilbertRTreeRA; public: /** diff --git a/src/mlpack/methods/rann/ra_model_impl.hpp b/src/mlpack/methods/rann/ra_model_impl.hpp index 48b4b4aa33..edaf03866b 100644 --- a/src/mlpack/methods/rann/ra_model_impl.hpp +++ b/src/mlpack/methods/rann/ra_model_impl.hpp @@ -22,7 +22,8 @@ RAModel::RAModel(const TreeTypes treeType, const bool randomBasis) : coverTreeRA(NULL), rTreeRA(NULL), rStarTreeRA(NULL), - xTreeRA(NULL) + xTreeRA(NULL), + hilbertRTreeRA(NULL) { // Nothing to do. } @@ -40,6 +41,8 @@ RAModel::~RAModel() delete rStarTreeRA; if (xTreeRA) delete xTreeRA; + if (hilbertRTreeRA) + delete hilbertRTreeRA; } template @@ -64,6 +67,8 @@ void RAModel::Serialize(Archive& ar, delete rStarTreeRA; if (xTreeRA) delete xTreeRA; + if (hilbertRTreeRA) + delete hilbertRTreeRA; // Set all the pointers to NULL. kdTreeRA = NULL; @@ -71,6 +76,7 @@ void RAModel::Serialize(Archive& ar, rTreeRA = NULL; rStarTreeRA = NULL; xTreeRA = NULL; + hilbertRTreeRA = NULL; } // We only need to serialize one of the kRANN objects. @@ -91,6 +97,9 @@ void RAModel::Serialize(Archive& ar, case X_TREE: ar & data::CreateNVP(xTreeRA, "ra_model"); break; + case HILBERT_R_TREE: + ar & data::CreateNVP(hilbertRTreeRA, "ra_model"); + break; } } @@ -107,6 +116,8 @@ const arma::mat& RAModel::Dataset() const return rStarTreeRA->ReferenceSet(); else if (xTreeRA) return xTreeRA->ReferenceSet(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->ReferenceSet(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -125,6 +136,8 @@ bool RAModel::Naive() const return rStarTreeRA->Naive(); else if (xTreeRA) return xTreeRA->Naive(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->Naive(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -143,6 +156,8 @@ bool& RAModel::Naive() return rStarTreeRA->Naive(); else if (xTreeRA) return xTreeRA->Naive(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->Naive(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -161,6 +176,8 @@ bool RAModel::SingleMode() const return rStarTreeRA->SingleMode(); else if (xTreeRA) return xTreeRA->SingleMode(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->SingleMode(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -179,6 +196,8 @@ bool& RAModel::SingleMode() return rStarTreeRA->SingleMode(); else if (xTreeRA) return xTreeRA->SingleMode(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->SingleMode(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -197,6 +216,8 @@ double RAModel::Tau() const return rStarTreeRA->Tau(); else if (xTreeRA) return xTreeRA->Tau(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->Tau(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -215,6 +236,8 @@ double& RAModel::Tau() return rStarTreeRA->Tau(); else if (xTreeRA) return xTreeRA->Tau(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->Tau(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -233,6 +256,8 @@ double RAModel::Alpha() const return rStarTreeRA->Alpha(); else if (xTreeRA) return xTreeRA->Alpha(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->Alpha(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -251,6 +276,8 @@ double& RAModel::Alpha() return rStarTreeRA->Alpha(); else if (xTreeRA) return xTreeRA->Alpha(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->Alpha(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -269,6 +296,8 @@ bool RAModel::SampleAtLeaves() const return rStarTreeRA->SampleAtLeaves(); else if (xTreeRA) return xTreeRA->SampleAtLeaves(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->SampleAtLeaves(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -287,6 +316,8 @@ bool& RAModel::SampleAtLeaves() return rStarTreeRA->SampleAtLeaves(); else if (xTreeRA) return xTreeRA->SampleAtLeaves(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->SampleAtLeaves(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -305,6 +336,8 @@ bool RAModel::FirstLeafExact() const return rStarTreeRA->FirstLeafExact(); else if (xTreeRA) return xTreeRA->FirstLeafExact(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->FirstLeafExact(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -323,6 +356,8 @@ bool& RAModel::FirstLeafExact() return rStarTreeRA->FirstLeafExact(); else if (xTreeRA) return xTreeRA->FirstLeafExact(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->FirstLeafExact(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -341,6 +376,8 @@ size_t RAModel::SingleSampleLimit() const return rStarTreeRA->SingleSampleLimit(); else if (xTreeRA) return xTreeRA->SingleSampleLimit(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->SingleSampleLimit(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -359,6 +396,8 @@ size_t& RAModel::SingleSampleLimit() return rStarTreeRA->SingleSampleLimit(); else if (xTreeRA) return xTreeRA->SingleSampleLimit(); + else if (hilbertRTreeRA) + return hilbertRTreeRA->SingleSampleLimit(); throw std::runtime_error("no rank-approximate nearest neighbor search model " "initialized"); @@ -424,6 +463,8 @@ void RAModel::BuildModel(arma::mat&& referenceSet, delete rStarTreeRA; if (xTreeRA) delete xTreeRA; + if (hilbertRTreeRA) + delete hilbertRTreeRA; if (randomBasis) referenceSet = q * referenceSet; @@ -472,6 +513,10 @@ void RAModel::BuildModel(arma::mat&& referenceSet, xTreeRA = new RAType(std::move(referenceSet), naive, singleMode); break; + case HILBERT_R_TREE: + hilbertRTreeRA = new RAType(std::move(referenceSet), + naive, singleMode); + break; } if (!naive) @@ -549,6 +594,10 @@ void RAModel::Search(arma::mat&& querySet, // No mapping necessary. xTreeRA->Search(querySet, k, neighbors, distances); break; + case HILBERT_R_TREE: + // No mapping necessary. + hilbertRTreeRA->Search(querySet, k, neighbors, distances); + break; } } @@ -583,6 +632,9 @@ void RAModel::Search(const size_t k, case X_TREE: xTreeRA->Search(k, neighbors, distances); break; + case HILBERT_R_TREE: + hilbertRTreeRA->Search(k, neighbors, distances); + break; } } @@ -601,6 +653,8 @@ std::string RAModel::TreeName() const return "R* tree"; case X_TREE: return "X tree"; + case HILBERT_R_TREE: + return "Hilbert R tree"; default: return "unknown tree"; } From 6f19e4d6513d0f1103d708152ee4b849785fdb1e Mon Sep 17 00:00:00 2001 From: Mikhail Lozhnikov Date: Tue, 28 Jun 2016 17:40:02 +0300 Subject: [PATCH 34/38] Added the Hilbert R tree to (A)KNNTest, RangeSearchTest and KRANNTest. --- src/mlpack/tests/aknn_test.cpp | 12 ++++++++---- src/mlpack/tests/knn_test.cpp | 12 ++++++++---- src/mlpack/tests/krann_search_test.cpp | 6 ++++-- src/mlpack/tests/range_search_test.cpp | 12 ++++++++---- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/mlpack/tests/aknn_test.cpp b/src/mlpack/tests/aknn_test.cpp index 23c7c9fda5..4af732b707 100644 --- a/src/mlpack/tests/aknn_test.cpp +++ b/src/mlpack/tests/aknn_test.cpp @@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - KNNModel models[12]; + KNNModel models[14]; models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true); models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false); models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true); @@ -300,6 +300,8 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false); models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); + models[12] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true); + models[13] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false); for (size_t j = 0; j < 3; ++j) { @@ -309,7 +311,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) arma::mat distancesExact; aknn.Search(queryData, 3, neighborsExact, distancesExact); - for (size_t i = 0; i < 12; ++i) + for (size_t i = 0; i < 14; ++i) { // We only have std::move() constructors so make a copy of our data. arma::mat referenceCopy(referenceData); @@ -349,7 +351,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - KNNModel models[12]; + KNNModel models[14]; models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true); models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false); models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true); @@ -362,6 +364,8 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false); models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); + models[12] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true); + models[13] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -371,7 +375,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) arma::mat distancesExact; exact.Search(3, neighborsExact, distancesExact); - for (size_t i = 0; i < 12; ++i) + for (size_t i = 0; i < 14; ++i) { // We only have a std::move() constructor... so copy the data. arma::mat referenceCopy(referenceData); diff --git a/src/mlpack/tests/knn_test.cpp b/src/mlpack/tests/knn_test.cpp index 85c6b7a79a..398aee508b 100644 --- a/src/mlpack/tests/knn_test.cpp +++ b/src/mlpack/tests/knn_test.cpp @@ -977,7 +977,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - KNNModel models[12]; + KNNModel models[14]; models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true); models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false); models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true); @@ -990,6 +990,8 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false); models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); + models[12] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true); + models[13] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -999,7 +1001,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest) arma::mat baselineDistances; knn.Search(queryData, 3, baselineNeighbors, baselineDistances); - for (size_t i = 0; i < 12; ++i) + for (size_t i = 0; i < 14; ++i) { // We only have std::move() constructors so make a copy of our data. arma::mat referenceCopy(referenceData); @@ -1043,7 +1045,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - KNNModel models[12]; + KNNModel models[14]; models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true); models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false); models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true); @@ -1056,6 +1058,8 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false); models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true); models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false); + models[12] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true); + models[13] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -1065,7 +1069,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest) arma::mat baselineDistances; knn.Search(3, baselineNeighbors, baselineDistances); - for (size_t i = 0; i < 12; ++i) + for (size_t i = 0; i < 14; ++i) { // We only have a std::move() constructor... so copy the data. arma::mat referenceCopy(referenceData); diff --git a/src/mlpack/tests/krann_search_test.cpp b/src/mlpack/tests/krann_search_test.cpp index 805adb39e9..fa95c543f1 100644 --- a/src/mlpack/tests/krann_search_test.cpp +++ b/src/mlpack/tests/krann_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[10]; + KNNModel models[12]; models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, false); models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, true); models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false); @@ -636,13 +636,15 @@ BOOST_AUTO_TEST_CASE(RAModelTest) 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); + models[10] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false); + models[11] = KNNModel(KNNModel::TreeTypes::HILBERT_R_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 < 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); diff --git a/src/mlpack/tests/range_search_test.cpp b/src/mlpack/tests/range_search_test.cpp index 88429719bd..1c9f73b3cc 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[12]; + RSModel models[14]; models[0] = RSModel(RSModel::TreeTypes::KD_TREE, true); models[1] = RSModel(RSModel::TreeTypes::KD_TREE, false); models[2] = RSModel(RSModel::TreeTypes::COVER_TREE, true); @@ -1264,6 +1264,8 @@ BOOST_AUTO_TEST_CASE(RSModelTest) models[9] = RSModel(RSModel::TreeTypes::X_TREE, false); models[10] = RSModel(RSModel::TreeTypes::BALL_TREE, true); models[11] = RSModel(RSModel::TreeTypes::BALL_TREE, false); + models[12] = RSModel(RSModel::TreeTypes::HILBERT_R_TREE, true); + models[13] = RSModel(RSModel::TreeTypes::HILBERT_R_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -1277,7 +1279,7 @@ BOOST_AUTO_TEST_CASE(RSModelTest) vector>> baselineSorted; SortResults(baselineNeighbors, baselineDistances, baselineSorted); - for (size_t i = 0; i < 12; ++i) + for (size_t i = 0; i < 14; ++i) { // We only have std::move() constructors, so make a copy of our data. arma::mat referenceCopy(referenceData); @@ -1321,7 +1323,7 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest) arma::mat referenceData = arma::randu(10, 200); // Build all the possible models. - RSModel models[12]; + RSModel models[14]; models[0] = RSModel(RSModel::TreeTypes::KD_TREE, true); models[1] = RSModel(RSModel::TreeTypes::KD_TREE, false); models[2] = RSModel(RSModel::TreeTypes::COVER_TREE, true); @@ -1334,6 +1336,8 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest) models[9] = RSModel(RSModel::TreeTypes::X_TREE, false); models[10] = RSModel(RSModel::TreeTypes::BALL_TREE, true); models[11] = RSModel(RSModel::TreeTypes::BALL_TREE, false); + models[12] = RSModel(RSModel::TreeTypes::HILBERT_R_TREE, true); + models[13] = RSModel(RSModel::TreeTypes::HILBERT_R_TREE, false); for (size_t j = 0; j < 2; ++j) { @@ -1346,7 +1350,7 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest) vector>> baselineSorted; SortResults(baselineNeighbors, baselineDistances, baselineSorted); - for (size_t i = 0; i < 12; ++i) + for (size_t i = 0; i < 14; ++i) { // We only have std::move() cosntructors, so make a copy of our data. arma::mat referenceCopy(referenceData); From cb2ea626951f83627eb826c53e21cee1581b1ee9 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 28 Jun 2016 16:22:33 -0400 Subject: [PATCH 35/38] Remove CopyMe(), just like the documentation says we should. --- .../tree/rectangle_tree/rectangle_tree.hpp | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index 0f2d9cc6e3..8d3c921842 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -496,26 +496,6 @@ class RectangleTree static bool HasSelfChildren() { return false; } private: - /** - * Private copy constructor, available only to fill (pad) the tree to a - * specified level. TO BE REMOVED - */ - RectangleTree(const size_t begin, - const size_t count, - bound::HRectBound bound, - StatisticType stat, - const int maxLeafSize = 20) : - begin(begin), - count(count), - bound(bound), - stat(stat), - maxLeafSize(maxLeafSize) { } - - RectangleTree* CopyMe() - { - return new RectangleTree(begin, count, bound, stat, maxLeafSize); - } - /** * Splits the current node, recursing up the tree. * From 6ffa3488be70150fa3e787d8bf928c023edbc201 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 28 Jun 2016 16:46:38 -0400 Subject: [PATCH 36/38] Change to size_t only, and add a warning for the mutator. --- src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp index 83b667068b..37f9572f26 100644 --- a/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp +++ b/src/mlpack/core/tree/rectangle_tree/rectangle_tree.hpp @@ -419,9 +419,10 @@ class RectangleTree * * @param index Index of point for which a dataset index is wanted. */ - const size_t& Point(const size_t index) const { return points[index]; } + size_t Point(const size_t index) const { return points[index]; } - //! Modify the index of a particular point in this node. + //! Modify the index of a particular point in this node. Be very careful when + //! you do this! You may make the tree invalid. size_t& Point(const size_t index) { return points[index]; } //! Return the minimum distance to another node. From 8e740b02eb97c874bbc9b141d9928644bdba7c6b Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 28 Jun 2016 17:06:55 -0400 Subject: [PATCH 37/38] Fix name of parameter. --- src/mlpack/methods/preprocess/preprocess_split_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlpack/methods/preprocess/preprocess_split_main.cpp b/src/mlpack/methods/preprocess/preprocess_split_main.cpp index 3c47c984b7..ca9641102d 100644 --- a/src/mlpack/methods/preprocess/preprocess_split_main.cpp +++ b/src/mlpack/methods/preprocess/preprocess_split_main.cpp @@ -68,7 +68,7 @@ int main(int argc, char** argv) const double testRatio = CLI::GetParam("test_ratio"); // Check on label parameters. - if (CLI::HasParam("input_labels")) + if (CLI::HasParam("input_labels_file")) { if (!CLI::HasParam("training_labels_file")) { From e6bc4b41704e546a7495fcca90db7cd0919ca189 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Tue, 28 Jun 2016 18:57:48 -0400 Subject: [PATCH 38/38] Fix error with casting negative numbers to size_t. --- src/mlpack/methods/lsh/lsh_search_impl.hpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/mlpack/methods/lsh/lsh_search_impl.hpp b/src/mlpack/methods/lsh/lsh_search_impl.hpp index 149beaba97..64ad80ab34 100644 --- a/src/mlpack/methods/lsh/lsh_search_impl.hpp +++ b/src/mlpack/methods/lsh/lsh_search_impl.hpp @@ -166,7 +166,8 @@ void LSHSearch::Train(const arma::mat& referenceSet, } // We will store the second hash vectors in this matrix; the second hash - // vector for table i will be held in row i. + // vector for table i will be held in row i. We have to use int and not + // size_t, otherwise negative numbers are cast to 0. arma::Mat secondHashVectors(numTables, referenceSet.n_cols); for (size_t i = 0; i < numTables; i++) @@ -189,15 +190,20 @@ void LSHSearch::Train(const arma::mat& referenceSet, hashMat /= hashWidth; // Step V: Putting the points in the 'secondHashTable' by hashing the key. - // Now we hash every key, point ID to its corresponding bucket. - secondHashVectors.row(i) = arma::conv_to>::from( - secondHashWeights.t() * arma::floor(hashMat)); + // Now we hash every key, point ID to its corresponding bucket. We must + // also normalize the hashes to the range [0, secondHashSize). + arma::rowvec unmodVector = secondHashWeights.t() * arma::floor(hashMat); + for (size_t j = 0; j < secondHashVectors.n_cols; ++j) + { + double shs = (double) secondHashSize; // Convenience cast. + if (unmodVector[j] >= 0.0) + secondHashVectors[j] = size_t(fmod(unmodVector[j], shs)); + else + secondHashVectors[j] = secondHashSize - + size_t(fmod(-unmodVector[j], shs)); + } } - // Normalize hashes (take modulus with secondHashSize). - secondHashVectors.transform([secondHashSize](size_t val) - { return val % secondHashSize; }); - // Now, using the hash vectors for each table, count the number of rows we // have in the second hash table. arma::Row secondHashBinCounts(secondHashSize, arma::fill::zeros);