Merge pull request #699 from lozhnikov/r_plus_tree-cherry_pick

R+ and R++ trees implementation
This commit is contained in:
Ryan Curtin
2016-07-07 17:27:33 -04:00
committed by GitHub
43 changed files with 2371 additions and 63 deletions
+14
View File
@@ -61,6 +61,20 @@ set(SOURCES
rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp
rectangle_tree/discrete_hilbert_value.hpp
rectangle_tree/discrete_hilbert_value_impl.hpp
rectangle_tree/r_plus_tree_descent_heuristic.hpp
rectangle_tree/r_plus_tree_descent_heuristic_impl.hpp
rectangle_tree/minimal_coverage_sweep.hpp
rectangle_tree/minimal_coverage_sweep_impl.hpp
rectangle_tree/minimal_splits_number_sweep.hpp
rectangle_tree/minimal_splits_number_sweep_impl.hpp
rectangle_tree/r_plus_tree_split.hpp
rectangle_tree/r_plus_tree_split_impl.hpp
rectangle_tree/r_plus_tree_split_policy.hpp
rectangle_tree/r_plus_plus_tree_descent_heuristic.hpp
rectangle_tree/r_plus_plus_tree_descent_heuristic_impl.hpp
rectangle_tree/r_plus_plus_tree_split_policy.hpp
rectangle_tree/r_plus_plus_tree_auxiliary_information.hpp
rectangle_tree/r_plus_plus_tree_auxiliary_information_impl.hpp
statistic.hpp
traversal_info.hpp
tree_traits.hpp
+20
View File
@@ -182,6 +182,26 @@ class HRectBound
template<typename VecType>
bool Contains(const VecType& point) const;
/**
* Determines if this bound partially contains a bound.
*/
bool Contains(const HRectBound& bound) const;
/**
* Returns the intersection of this bound and another.
*/
HRectBound operator&(const HRectBound& bound) const;
/**
* Intersects this bound with another.
*/
HRectBound& operator&=(const HRectBound& bound);
/**
* Returns the volume of overlap of this bound and another.
*/
ElemType Overlap(const HRectBound& bound) const;
/**
* Returns the diameter of the hyperrectangle (that is, the longest diagonal).
*/
+78
View File
@@ -143,7 +143,12 @@ inline ElemType HRectBound<MetricType, ElemType>::Volume() const
{
ElemType volume = 1.0;
for (size_t i = 0; i < dim; ++i)
{
if (bounds[i].Lo() >= bounds[i].Hi())
return 0;
volume *= (bounds[i].Hi() - bounds[i].Lo());
}
return volume;
}
@@ -430,6 +435,79 @@ inline bool HRectBound<MetricType, ElemType>::Contains(const VecType& point) con
return true;
}
/**
* Determines if this bound partially contains a bound.
*/
template<typename MetricType, typename ElemType>
inline bool HRectBound<MetricType, ElemType>::Contains(
const HRectBound& bound) const
{
for (size_t i = 0; i < dim; i++)
{
const math::RangeType<ElemType>& r_a = bounds[i];
const math::RangeType<ElemType>& r_b = bound.bounds[i];
if (r_a.Hi() <= r_b.Lo() || r_a.Lo() >= r_b.Hi()) // If a does not overlap b at all.
return false;
}
return true;
}
/**
* Returns the intersection of this bound and another.
*/
template<typename MetricType, typename ElemType>
inline HRectBound<MetricType, ElemType> HRectBound<MetricType, ElemType>::
operator&(const HRectBound& bound) const
{
HRectBound<MetricType, ElemType> result(dim);
for (size_t k = 0; k < dim; k++)
{
result[k].Lo() = std::max(bounds[k].Lo(), bound.bounds[k].Lo());
result[k].Hi() = std::min(bounds[k].Hi(), bound.bounds[k].Hi());
}
return result;
}
/**
* Intersects this bound with another.
*/
template<typename MetricType, typename ElemType>
inline HRectBound<MetricType, ElemType>& HRectBound<MetricType, ElemType>::
operator&=(const HRectBound& bound)
{
for (size_t k = 0; k < dim; k++)
{
bounds[k].Lo() = std::max(bounds[k].Lo(), bound.bounds[k].Lo());
bounds[k].Hi() = std::min(bounds[k].Hi(), bound.bounds[k].Hi());
}
return *this;
}
/**
* Returns the volume of overlap of this bound and another.
*/
template<typename MetricType, typename ElemType>
inline ElemType HRectBound<MetricType, ElemType>::Overlap(
const HRectBound& bound) const
{
ElemType volume = 1.0;
for (size_t k = 0; k < dim; k++)
{
ElemType lo = std::max(bounds[k].Lo(), bound.bounds[k].Lo());
ElemType hi = std::min(bounds[k].Hi(), bound.bounds[k].Hi());
if ( hi <= lo)
return 0;
volume *= hi - lo;
}
return volume;
}
/**
* Returns the diameter of the hyperrectangle (that is, the longest diagonal).
*/
+8
View File
@@ -29,6 +29,14 @@
#include "rectangle_tree/hilbert_r_tree_split.hpp"
#include "rectangle_tree/hilbert_r_tree_auxiliary_information.hpp"
#include "rectangle_tree/discrete_hilbert_value.hpp"
#include "rectangle_tree/r_plus_tree_descent_heuristic.hpp"
#include "rectangle_tree/r_plus_tree_split_policy.hpp"
#include "rectangle_tree/minimal_coverage_sweep.hpp"
#include "rectangle_tree/minimal_splits_number_sweep.hpp"
#include "rectangle_tree/r_plus_tree_split.hpp"
#include "rectangle_tree/r_plus_plus_tree_auxiliary_information.hpp"
#include "rectangle_tree/r_plus_plus_tree_descent_heuristic.hpp"
#include "rectangle_tree/r_plus_plus_tree_split_policy.hpp"
#include "rectangle_tree/typedef.hpp"
#endif
@@ -19,6 +19,8 @@ template<typename TreeType>
void HilbertRTreeSplit<splitOrder>::SplitLeafNode(TreeType* tree,
std::vector<bool>& relevels)
{
if (tree->Count() <= tree->MaxLeafSize())
return;
// 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.
@@ -0,0 +1,97 @@
/**
* @file minimal_coverage_sweep.hpp
* @author Mikhail Lozhnikov
*
* Definition of the MinimalCoverageSweep class, a class that finds a partition
* of a node along an axis.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_COVERAGE_SWEEP_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_COVERAGE_SWEEP_HPP
namespace mlpack {
namespace tree {
/**
* The MinimalCoverageSweep class finds a partition along which we
* can split a node according to the coverage of two resulting nodes. The class
* finds a partition along a given axis. Moreover, the class evaluates the cost
* of each split. The cost is proportional to the total coverage of resulting
* nodes. If the resulting nodes are overflowed the maximum cost is returned.
*
* @tparam SplitPolicy The class that provides rules for inserting children of
* a node that is being split into two new subtrees.
*/
template<typename SplitPolicy>
class MinimalCoverageSweep
{
public:
//! A struct that provides the type of the sweep cost.
template<typename TreeType>
struct SweepCost
{
typedef typename TreeType::ElemType type;
};
/**
* Find a suitable partition of a non-leaf node along the provided axis.
* The method returns the cost of the split.
*
* @param axis The axis along which we are finding a partition.
* @param node The node that is being split.
* @param axisCut The coordinate at which the node may be split.
*/
template<typename TreeType>
static typename TreeType::ElemType SweepNonLeafNode(
const size_t axis,
const TreeType* node,
typename TreeType::ElemType& axisCut);
/**
* Find a suitable partition of a leaf node along the provided axis.
* The method returns the cost of the split.
*
* @param axis The axis along which we are finding a partition.
* @param node The node that is being split.
* @param axisCut The coordinate at which the node may be split.
*/
template<typename TreeType>
static typename TreeType::ElemType SweepLeafNode(
const size_t axis,
const TreeType* node,
typename TreeType::ElemType& axisCut);
/**
* Check if an intermediate node can be split along the axis at the provided
* coordinate.
*
* @param node The node that is being split.
* @param cutAxis The axis that we want to check.
* @param cut The coordinate that we want to check.
*/
template<typename TreeType, typename ElemType>
static bool CheckNonLeafSweep(const TreeType* node,
const size_t cutAxis,
const ElemType cut);
/**
* Check if a leaf node can be split along the axis at the provided
* coordinate.
*
* @param node The node that is being split.
* @param cutAxis The axis that we want to check.
* @param cut The coordinate that we want to check.
*/
template<typename TreeType, typename ElemType>
static bool CheckLeafSweep(const TreeType* node,
const size_t cutAxis,
const ElemType cut);
};
} // namespace tree
} // namespace mlpack
// Include implementation
#include "minimal_coverage_sweep_impl.hpp"
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_COVERAGE_SWEEP_HPP
@@ -0,0 +1,194 @@
/**
* @file minimal_coverage_sweep_impl.hpp
* @author Mikhail Lozhnikov
*
* Implementation of the MinimalCoverageSweep class, a class that finds a
* partition of a node along an axis.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_COVERAGE_SWEEP_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_COVERAGE_SWEEP_IMPL_HPP
#include "minimal_coverage_sweep.hpp"
namespace mlpack {
namespace tree {
template<typename SplitPolicy>
template<typename TreeType>
typename TreeType::ElemType MinimalCoverageSweep<SplitPolicy>::
SweepNonLeafNode(const size_t axis,
const TreeType* node,
typename TreeType::ElemType& axisCut)
{
typedef typename TreeType::ElemType ElemType;
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> BoundType;
std::vector<std::pair<ElemType, size_t>> sorted(node->NumChildren());
for (size_t i = 0; i < node->NumChildren(); i++)
{
sorted[i].first = SplitPolicy::Bound(node->Child(i))[axis].Hi();
sorted[i].second = i;
}
// Sort high bounds of children.
std::sort(sorted.begin(), sorted.end(),
[] (const std::pair<ElemType, size_t>& s1,
const std::pair<ElemType, size_t>& s2)
{
return s1.first < s2.first;
});
size_t splitPointer = node->NumChildren() / 2;
axisCut = sorted[splitPointer - 1].first;
// Check if the midpoint split is suitable.
if (!CheckNonLeafSweep(node, axis, axisCut))
{
// Find any suitable partition if the default partition is not acceptable.
for (splitPointer = 1; splitPointer < sorted.size(); splitPointer++)
{
axisCut = sorted[splitPointer - 1].first;
if (CheckNonLeafSweep(node, axis, axisCut))
break;
}
if (splitPointer == node->NumChildren())
return std::numeric_limits<ElemType>::max();
}
BoundType bound1(node->Bound().Dim());
BoundType bound2(node->Bound().Dim());
// Find bounds of two resulting nodes.
for (size_t i = 0; i < splitPointer; i++)
bound1 |= node->Child(sorted[i].second).Bound();
for (size_t i = splitPointer; i < node->NumChildren(); i++)
bound2 |= node->Child(sorted[i].second).Bound();
// Evaluate the cost of the split i.e. calculate the total coverage
// of two resulting nodes.
ElemType area1 = bound1.Volume();
ElemType area2 = bound2.Volume();
return area1 + area2;
}
template<typename SplitPolicy>
template<typename TreeType>
typename TreeType::ElemType MinimalCoverageSweep<SplitPolicy>::
SweepLeafNode(const size_t axis,
const TreeType* node,
typename TreeType::ElemType& axisCut)
{
typedef typename TreeType::ElemType ElemType;
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> BoundType;
std::vector<std::pair<ElemType, size_t>> sorted(node->Count());
sorted.resize(node->Count());
for (size_t i = 0; i < node->NumPoints(); i++)
{
sorted[i].first = node->Dataset().col(node->Point(i))[axis];
sorted[i].second = i;
}
// Sort high bounds of children.
std::sort(sorted.begin(), sorted.end(),
[] (const std::pair<ElemType, size_t>& s1,
const std::pair<ElemType, size_t>& s2)
{
return s1.first < s2.first;
});
size_t splitPointer = node->Count() / 2;
axisCut = sorted[splitPointer - 1].first;
// Check if the partition is suitable.
if (!CheckLeafSweep(node, axis, axisCut))
return std::numeric_limits<ElemType>::max();
BoundType bound1(node->Bound().Dim());
BoundType bound2(node->Bound().Dim());
// Find bounds of two resulting nodes.
for (size_t i = 0; i < splitPointer; i++)
bound1 |= node->Dataset().col(node->Point(sorted[i].second));
for (size_t i = splitPointer; i < node->NumChildren(); i++)
bound2 |= node->Dataset().col(node->Point(sorted[i].second));
// Evaluate the cost of the split i.e. calculate the total coverage
// of two resulting nodes.
return bound1.Volume() + bound2.Volume();
}
template<typename SplitPolicy>
template<typename TreeType, typename ElemType>
bool MinimalCoverageSweep<SplitPolicy>::
CheckNonLeafSweep(const TreeType* node,
const size_t cutAxis,
const ElemType cut)
{
size_t numTreeOneChildren = 0;
size_t numTreeTwoChildren = 0;
// Calculate the number of children in the resulting nodes.
for (size_t i = 0; i < node->NumChildren(); i++)
{
const TreeType& child = node->Child(i);
int policy = SplitPolicy::GetSplitPolicy(child, cutAxis, cut);
if (policy == SplitPolicy::AssignToFirstTree)
numTreeOneChildren++;
else if (policy == SplitPolicy::AssignToSecondTree)
numTreeTwoChildren++;
else
{
// The split is required.
numTreeOneChildren++;
numTreeTwoChildren++;
}
}
if (numTreeOneChildren <= node->MaxNumChildren() && numTreeOneChildren > 0 &&
numTreeTwoChildren <= node->MaxNumChildren() && numTreeTwoChildren > 0)
return true;
return false;
}
template<typename SplitPolicy>
template<typename TreeType, typename ElemType>
bool MinimalCoverageSweep<SplitPolicy>::
CheckLeafSweep(const TreeType* node,
const size_t cutAxis,
const ElemType cut)
{
size_t numTreeOnePoints = 0;
size_t numTreeTwoPoints = 0;
// Calculate the number of points in the resulting nodes.
for (size_t i = 0; i < node->NumPoints(); i++)
{
if (node->Dataset().col(node->Point(i))[cutAxis] <= cut)
numTreeOnePoints++;
else
numTreeTwoPoints++;
}
if (numTreeOnePoints <= node->MaxLeafSize() && numTreeOnePoints > 0 &&
numTreeTwoPoints <= node->MaxLeafSize() && numTreeTwoPoints > 0)
return true;
return false;
}
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_COVERAGE_SWEEP_IMPL_HPP
@@ -0,0 +1,73 @@
/**
* @file minimal_splits_number_sweep.hpp
* @author Mikhail Lozhnikov
*
* Definition of the MinimalSplitsNumberSweep class, a class that finds a
* partition of a node along an axis.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_SPLITS_NUMBER_SWEEP_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_SPLITS_NUMBER_SWEEP_HPP
namespace mlpack {
namespace tree {
/**
* The MinimalSplitsNumberSweep class finds a partition along which we
* can split a node according to the number of required splits of the node.
* The class finds a partition along a given axis. Moreover, the class evaluates
* the cost of each split. The cost is proportional to the number of required
* splits and the difference of sizes of resulting nodes. If the resulting nodes
* are overflowed the maximum cost is returned.
*
* @tparam SplitPolicy The class that provides rules for inserting children of
* a node that is being split into two new subtrees.
*/
template<typename SplitPolicy>
class MinimalSplitsNumberSweep
{
public:
//! A struct that provides the type of the sweep cost.
template<typename>
struct SweepCost
{
typedef size_t type;
};
/**
* Find a suitable partition of a non-leaf node along the provided axis.
* The method returns the cost of the split.
*
* @param axis The axis along which we are finding a partition.
* @param node The node that is being split.
* @param axisCut The coordinate at which the node may be split.
*/
template<typename TreeType>
static size_t SweepNonLeafNode(
const size_t axis,
const TreeType* node,
typename TreeType::ElemType& axisCut);
/**
* Find a suitable partition of a leaf node along the provided axis.
* The method returns the cost of the split.
*
* @param axis The axis along which we are finding a partition.
* @param node The node that is being split.
* @param axisCut The coordinate at which the node may be split.
*/
template<typename TreeType>
static size_t SweepLeafNode(
const size_t axis,
const TreeType* node,
typename TreeType::ElemType& axisCut);
};
} // namespace tree
} // namespace mlpack
// Include implementation
#include "minimal_splits_number_sweep_impl.hpp"
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_SPLITS_NUMBER_SWEEP_HPP
@@ -0,0 +1,112 @@
/**
* @file minimal_splits_number_sweep_impl.hpp
* @author Mikhail Lozhnikov
*
* Implementation of the MinimalSplitsNumberSweep class, a class that finds a
* partition of a node along an axis.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_SPLITS_NUMBER_SWEEP_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_SPLITS_NUMBER_SWEEP_IMPL_HPP
#include "minimal_splits_number_sweep.hpp"
namespace mlpack {
namespace tree {
template<typename SplitPolicy>
template<typename TreeType>
size_t MinimalSplitsNumberSweep<SplitPolicy>::SweepNonLeafNode(
const size_t axis,
const TreeType* node,
typename TreeType::ElemType& axisCut)
{
typedef typename TreeType::ElemType ElemType;
std::vector<std::pair<ElemType, size_t>> sorted(node->NumChildren());
for (size_t i = 0; i < node->NumChildren(); i++)
{
sorted[i].first = SplitPolicy::Bound(node->Child(i))[axis].Hi();
sorted[i].second = i;
}
// Sort candidates in order to check balancing.
std::sort(sorted.begin(), sorted.end(),
[] (const std::pair<ElemType, size_t>& s1,
const std::pair<ElemType, size_t>& s2)
{
return s1.first < s2.first;
});
size_t minCost = SIZE_MAX;
// Find a split with the minimal cost.
for (size_t i = 0; i < sorted.size(); i++)
{
size_t numTreeOneChildren = 0;
size_t numTreeTwoChildren = 0;
size_t numSplits = 0;
// Calculate the number of splits.
for (size_t j = 0; j < node->NumChildren(); j++)
{
const TreeType& child = node->Child(j);
int policy = SplitPolicy::GetSplitPolicy(child, axis, sorted[i].first);
if (policy == SplitPolicy::AssignToFirstTree)
numTreeOneChildren++;
else if (policy == SplitPolicy::AssignToSecondTree)
numTreeTwoChildren++;
else
{
numTreeOneChildren++;
numTreeTwoChildren++;
numSplits++;
}
}
// Check if the split is possible.
if (numTreeOneChildren <= node->MaxNumChildren() && numTreeOneChildren > 0 &&
numTreeTwoChildren <= node->MaxNumChildren() && numTreeTwoChildren > 0)
{
// Evaluate the cost using the number of splits and balancing.
size_t balance;
if (sorted.size() / 2 > i )
balance = sorted.size() / 2 - i;
else
balance = i - sorted.size() / 2;
size_t cost = numSplits * balance;
if (cost < minCost)
{
minCost = cost;
axisCut = sorted[i].first;
}
}
}
return minCost;
}
template<typename SplitPolicy>
template<typename TreeType>
size_t MinimalSplitsNumberSweep<SplitPolicy>::SweepLeafNode(
const size_t axis,
const TreeType* node,
typename TreeType::ElemType& axisCut)
{
// Split along the median.
axisCut = (node->Bound()[axis].Lo() + node->Bound()[axis].Hi()) * 0.5;
if (node->Bound()[axis].Lo() == axisCut)
return SIZE_MAX;
return 0;
}
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_MINIMAL_SPLITS_NUMBER_SWEEP_IMPL_HPP
@@ -32,7 +32,7 @@ class NoAuxiliaryInformation
* @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;
}
@@ -98,6 +98,24 @@ class NoAuxiliaryInformation
return false;
}
/**
* The R++ tree requires to split the maximum bounding rectangle of a node
* that is being split. This method is intended for that. This method is only
* necessary for an AuxiliaryInformationType that is being used in conjunction
* with RPlusTreeSplit.
*
* @param treeOne The first subtree.
* @param treeTwo The second subtree.
* @param axis The axis along which the split is performed.
* @param cut The coordinate at which the node is split.
*/
void SplitAuxiliaryInfo(TreeType* /* treeOne */,
TreeType* /* treeTwo */,
size_t /* axis */,
typename TreeType::ElemType /* cut */)
{ }
/**
* Nullify the auxiliary information in order to prevent an invalid free.
*/
@@ -0,0 +1,149 @@
/**
* @file r_plus_plus_tree_auxiliary_information.hpp
* @author Mikhail Lozhnikov
*
* Definition of the RPlusPlusTreeAuxiliaryInformation class,
* a class that provides some r++-tree specific information
* about the nodes.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_AUXILIARY_INFORMATION_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_AUXILIARY_INFORMATION_HPP
#include <mlpack/core.hpp>
#include "../hrectbound.hpp"
namespace mlpack {
namespace tree {
template<typename TreeType>
class RPlusPlusTreeAuxiliaryInformation
{
public:
//! The element type held by the tree.
typedef typename TreeType::ElemType ElemType;
//! The bound type held by the auxiliary information.
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> BoundType;
//! Construct the auxiliary information object.
RPlusPlusTreeAuxiliaryInformation();
/**
* Construct this as an auxiliary information for the given node.
*
* @param node The node that stores this auxiliary information.
*/
RPlusPlusTreeAuxiliaryInformation(const TreeType* /* node */);
/**
* Create an auxiliary information object by copying from another node.
*
* @param other The auxiliary information object from which the information
* will be copied.
*/
RPlusPlusTreeAuxiliaryInformation(
const RPlusPlusTreeAuxiliaryInformation& other);
/**
* 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
* 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* /* node */, const size_t /* point */);
/**
* 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
* 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* /* node */,
TreeType* /* nodeToInsert */,
bool /* insertionLevel */);
/**
* Some tree types require to save some properties at the deletion 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* /* node */, const size_t /* localIndex */);
/**
* Some tree types require to save some properties at the deletion 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* /* node */, const size_t /* nodeIndex */);
/**
* 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* /* node */);
/**
* The R++ tree requires to split the maximum bounding rectangle of a node
* that is being split. This method is intended for that.
*
* @param treeOne The first subtree.
* @param treeTwo The second subtree.
* @param axis The axis along which the split is performed.
* @param cut The coordinate at which the node is split.
*/
void SplitAuxiliaryInfo(TreeType* treeOne,
TreeType* treeTwo,
const size_t axis,
const ElemType cut);
/**
* Nullify the auxiliary information in order to prevent an invalid free.
*/
void NullifyData();
//! Return the maximum bounding rectangle.
BoundType& OuterBound() { return outerBound; }
//! Modify the maximum bounding rectangle.
const BoundType& OuterBound() const { return outerBound; }
private:
//! The maximum bounding rectangle.
BoundType outerBound;
public:
/**
* Serialize the information.
*/
template<typename Archive>
void Serialize(Archive &, const unsigned int /* version */);
};
} // namespace tree
} // namespace mlpack
#include "r_plus_plus_tree_auxiliary_information_impl.hpp"
#endif//MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_AUXILIARY_INFORMATION_HPP
@@ -0,0 +1,130 @@
/**
* @file r_plus_plus_tree_auxiliary_information.hpp
* @author Mikhail Lozhnikov
*
* Implementation of the RPlusPlusTreeAuxiliaryInformation class,
* a class that provides some r++-tree specific information
* about the nodes.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_AUXILIARY_INFORMATION_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_AUXILIARY_INFORMATION_IMPL_HPP
#include "r_plus_plus_tree_auxiliary_information.hpp"
namespace mlpack {
namespace tree {
template<typename TreeType>
RPlusPlusTreeAuxiliaryInformation<TreeType>::
RPlusPlusTreeAuxiliaryInformation() :
outerBound(0)
{
}
template<typename TreeType>
RPlusPlusTreeAuxiliaryInformation<TreeType>::
RPlusPlusTreeAuxiliaryInformation(const TreeType* tree) :
outerBound(tree->Parent() ?
tree->Parent()->AuxiliaryInfo().OuterBound() :
tree->Bound().Dim())
{
// Initialize the maximum bounding rectangle if the node is the root
if (!tree->Parent())
for (size_t k = 0; k < outerBound.Dim(); k++)
{
outerBound[k].Lo() = std::numeric_limits<ElemType>::lowest();
outerBound[k].Hi() = std::numeric_limits<ElemType>::max();
}
}
template<typename TreeType>
RPlusPlusTreeAuxiliaryInformation<TreeType>::
RPlusPlusTreeAuxiliaryInformation(
const RPlusPlusTreeAuxiliaryInformation& other) :
outerBound(other.OuterBound())
{
}
template<typename TreeType>
bool RPlusPlusTreeAuxiliaryInformation<TreeType>::HandlePointInsertion(
TreeType* /* node */, const size_t /* point */)
{
return false;
}
template<typename TreeType>
bool RPlusPlusTreeAuxiliaryInformation<TreeType>::HandleNodeInsertion(
TreeType* /* node */,
TreeType* /* nodeToInsert */,
bool /* insertionLevel */)
{
assert(false);
return false;
}
template<typename TreeType>
bool RPlusPlusTreeAuxiliaryInformation<TreeType>::HandlePointDeletion(
TreeType* /* node */, const size_t /* localIndex */)
{
return false;
}
template<typename TreeType>
bool RPlusPlusTreeAuxiliaryInformation<TreeType>::HandleNodeRemoval(
TreeType* /* node */, const size_t /* nodeIndex */)
{
return false;
}
template<typename TreeType>
bool RPlusPlusTreeAuxiliaryInformation<TreeType>::UpdateAuxiliaryInfo(
TreeType* /* node */)
{
return false;
}
template<typename TreeType>
void RPlusPlusTreeAuxiliaryInformation<TreeType>::SplitAuxiliaryInfo(
TreeType* treeOne,
TreeType* treeTwo,
const size_t axis,
const typename TreeType::ElemType cut)
{
typedef bound::HRectBound<metric::EuclideanDistance, ElemType> Bound;
Bound& treeOneBound = treeOne->AuxiliaryInfo().OuterBound();
Bound& treeTwoBound = treeTwo->AuxiliaryInfo().OuterBound();
// Copy the maximum bounding rectangle
treeOneBound = outerBound;
treeTwoBound = outerBound;
// Set proper limits
treeOneBound[axis].Hi() = cut;
treeTwoBound[axis].Lo() = cut;
}
template<typename TreeType>
void RPlusPlusTreeAuxiliaryInformation<TreeType>::NullifyData()
{
}
/**
* Serialize the information.
*/
template<typename TreeType>
template<typename Archive>
void RPlusPlusTreeAuxiliaryInformation<TreeType>::
Serialize(Archive& ar, const unsigned int /* version */)
{
using data::CreateNVP;
ar & CreateNVP(outerBound, "outerBound");
}
} // namespace tree
} // namespace mlpack
#endif//MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_AUXILIARY_INFORMATION_IMPL_HPP
@@ -0,0 +1,49 @@
/**
* @file r_plus_plus_tree_descent_heuristic.hpp
* @author Mikhail Lozhnikov
*
* Definition of RPlusPlusTreeDescentHeuristic, a class that chooses the best child of a
* node in an R++ tree when inserting a new point.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_DESCENT_HEURISTIC_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_DESCENT_HEURISTIC_HPP
#include <mlpack/core.hpp>
namespace mlpack {
namespace tree {
class RPlusPlusTreeDescentHeuristic
{
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 number of the point that is being inserted.
*/
template<typename TreeType>
static size_t ChooseDescentNode(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<typename TreeType>
static size_t ChooseDescentNode(const TreeType* node,
const TreeType* insertedNode);
};
} // namespace tree
} // namespace mlpack
#include "r_plus_plus_tree_descent_heuristic_impl.hpp"
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_DESCENT_HEURISTIC_HPP
@@ -0,0 +1,49 @@
/**
* @file r_plus_plus_tree_descent_heuristic_impl.hpp
* @author Mikhail Lozhnikov
*
* Implementation of RPlusPlusTreeDescentHeuristic, a class that chooses the
* best child of a node in an R++ tree when inserting a new point.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_DESCENT_HEURISTIC_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_DESCENT_HEURISTIC_IMPL_HPP
#include "r_plus_plus_tree_descent_heuristic.hpp"
#include "../hrectbound.hpp"
namespace mlpack {
namespace tree {
template<typename TreeType>
size_t RPlusPlusTreeDescentHeuristic::ChooseDescentNode(
TreeType* node, const size_t point)
{
// Find the node whose maximum bounding rectangle contains the point.
for (size_t bestIndex = 0; bestIndex < node->NumChildren(); bestIndex++)
{
if (node->Child(bestIndex).AuxiliaryInfo().OuterBound().Contains(
node->Dataset().col(point)))
return bestIndex;
}
// We should never reach this point.
assert(false);
return 0;
}
template<typename TreeType>
size_t RPlusPlusTreeDescentHeuristic::ChooseDescentNode(
const TreeType* /* node */, const TreeType* /* insertedNode */)
{
// Should never be used.
assert(false);
return 0;
}
} // namespace tree
} // namespace mlpack
#endif //MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_DESCENT_HEURISTIC_IMPL_HPP
@@ -0,0 +1,75 @@
/**
* @file r_plus_plus_tree_split_policy.hpp
* @author Mikhail Lozhnikov
*
* Defintion and implementation of the RPlusPlusTreeSplitPolicy class, a class
* that helps to determine the subtree into which we should insert an
* intermediate node.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_SPLIT_POLICY_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_SPLIT_POLICY_HPP
namespace mlpack {
namespace tree {
/**
* The RPlusPlusTreeSplitPolicy helps to determine the subtree into which
* we should insert a child of an intermediate node that is being split.
* This class is designed for the R++ tree.
*/
class RPlusPlusTreeSplitPolicy
{
public:
//! Indicate that the child should be split.
static const int SplitRequired = 0;
//! Indicate that the child should be inserted to the first subtree.
static const int AssignToFirstTree = 1;
//! Indicate that the child should be inserted to the second subtree.
static const int AssignToSecondTree = 2;
/**
* This method returns SplitRequired if a child of an intermediate node should
* be split, AssignToFirstTree if the child should be inserted to the first
* subtree, AssignToSecondTree if the child should be inserted to the second
* subtree. The method makes desicion according to the maximum bounding
* rectangle of the child, the axis along which the intermediate node is being
* split and the coordinate at which the node is being split.
*
* @param child A child of the node that is being split.
* @param axis The axis along which the node is being split.
* @param cut The coordinate at which the node is being split.
*/
template<typename TreeType>
static int GetSplitPolicy(const TreeType& child,
const size_t axis,
const typename TreeType::ElemType cut)
{
if (child.AuxiliaryInfo().OuterBound()[axis].Hi() <= cut)
return AssignToFirstTree;
else if (child.AuxiliaryInfo().OuterBound()[axis].Lo() >= cut)
return AssignToSecondTree;
return SplitRequired;
}
/**
* Return the maximum bounding rectangle of the node.
* This method should always return the bound that is used for the
* desicion-making in GetSplitPolicy().
*
* @param node The node whose bound is requested.
*/
template<typename TreeType>
static const
bound::HRectBound<metric::EuclideanDistance, typename TreeType::ElemType>&
Bound(const TreeType& node)
{
return node.AuxiliaryInfo().OuterBound();
}
};
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_PLUS_TREE_SPLIT_POLICY_HPP
@@ -0,0 +1,49 @@
/**
* @file r_plus_tree_descent_heuristic.hpp
* @author Mikhail Lozhnikov
*
* Definition of RPlusTreeDescentHeuristic, a class that chooses the best child
* of a node in an R+ tree when inserting a new point.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_DESCENT_HEURISTIC_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_DESCENT_HEURISTIC_HPP
#include <mlpack/core.hpp>
namespace mlpack {
namespace tree {
class RPlusTreeDescentHeuristic
{
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 number of the point that is being inserted.
*/
template<typename TreeType>
static size_t ChooseDescentNode(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<typename TreeType>
static size_t ChooseDescentNode(const TreeType* /* node */,
const TreeType* /*insertedNode */);
};
} // namespace tree
} // namespace mlpack
#include "r_plus_tree_descent_heuristic_impl.hpp"
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_DESCENT_HEURISTIC_HPP
@@ -0,0 +1,104 @@
/**
* @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_R_PLUS_TREE_DESCENT_HEURISTIC_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_DESCENT_HEURISTIC_IMPL_HPP
#include "r_plus_tree_descent_heuristic.hpp"
#include "../hrectbound.hpp"
namespace mlpack {
namespace tree {
template<typename TreeType>
size_t RPlusTreeDescentHeuristic::
ChooseDescentNode(TreeType* node, const size_t point)
{
typedef typename TreeType::ElemType ElemType;
size_t bestIndex = 0;
bool success;
// Try to find a node that contains the point.
for (bestIndex = 0; bestIndex < node->NumChildren(); bestIndex++)
{
if (node->Child(bestIndex).Bound().Contains(
node->Dataset().col(point)))
return bestIndex;
}
// No one node contains the point. Try to enlarge a node in such a way, that
// the resulting node do not overlap other nodes.
for (bestIndex = 0; bestIndex < node->NumChildren(); bestIndex++)
{
bound::HRectBound<metric::EuclideanDistance, ElemType> bound =
node->Child(bestIndex).Bound();
bound |= node->Dataset().col(point);
success = true;
for (size_t j = 0; j < node->NumChildren(); j++)
{
if (j == bestIndex)
continue;
success = false;
// Two nodes overlap if and only if there are no dimension in which
// they do not overlap each other.
for (size_t k = 0; k < node->Bound().Dim(); k++)
{
if (bound[k].Lo() >= node->Child(j).Bound()[k].Hi() ||
node->Child(j).Bound()[k].Lo() >= bound[k].Hi())
{
// We found the dimension in which these nodes do not overlap
// each other.
success = true;
break;
}
}
if (!success) // These two nodes overlap each other.
break;
}
if (success) // We found two nodes that do no overlap each other.
break;
}
if (!success) // We could not find two nodes that do no overlap each other.
{
size_t depth = node->TreeDepth();
// Create a new node into which we will insert the point.
TreeType* tree = node;
while (depth > 1)
{
TreeType* child = new TreeType(tree);
tree->children[tree->NumChildren()++] = child;
tree = child;
depth--;
}
return node->NumChildren()-1;
}
assert(bestIndex < node->NumChildren());
return bestIndex;
}
template<typename TreeType>
size_t RPlusTreeDescentHeuristic::ChooseDescentNode(
const TreeType* /* node */, const TreeType* /*insertedNode */)
{
// Should never be used.
assert(false);
return 0;
}
} // namespace tree
} // namespace mlpack
#endif //MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_DESCENT_HEURISTIC_IMPL_HPP
@@ -0,0 +1,131 @@
/**
* @file r_plus_tree_split.hpp
* @author Mikhail Lozhnikov
*
* Defintion of the RPlusTreeSplit class, a class that splits the nodes of an R
* tree, starting at a leaf node and moving upwards if necessary.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_SPLIT_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_SPLIT_HPP
#include <mlpack/core.hpp>
namespace mlpack {
namespace tree /** Trees and tree-building procedures. */ {
/**
* The RPlusTreeSplit class performs the split process of a node on overflow.
*
* @tparam SplitPolicyType The class that helps to determine the subtree into
* which we should insert a child node.
* @tparam SweepType The class that finds the partition of a node along a
* given axis. The partition algorithm tries to find a partition along each
* axis, evaluates each partition and chooses the best one.
*/
template<typename SplitPolicyType,
template<typename> class SweepType>
class RPlusTreeSplit
{
public:
typedef SplitPolicyType SplitPolicy;
/**
* 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<typename TreeType>
static void SplitLeafNode(TreeType *tree,std::vector<bool>& 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<typename TreeType>
static bool SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels);
private:
/**
* Split a leaf node along an axis.
*
* @param tree The node that is being split into two new nodes.
* @param treeOne The first subtree of two resulting subtrees.
* @param treeOne The second subtree of two resulting subtrees.
* @param cutAxis The axis along which the node is being split.
* @param cut The coordinate at which the node is being split.
*/
template<typename TreeType>
static void SplitLeafNodeAlongPartition(
TreeType* tree,
TreeType* treeOne,
TreeType* treeTwo,
const size_t cutAxis,
const typename TreeType::ElemType cut);
/**
* Split a non-leaf node along an axis. This method propagates the split
* downward up to a leaf node if necessary.
*
* @param tree The node that is being split into two new nodes.
* @param treeOne The first subtree of two resulting subtrees.
* @param treeOne The second subtree of two resulting subtrees.
* @param cutAxis The axis along which the node is being split.
* @param cut The coordinate at which the node is being split.
*/
template<typename TreeType>
static void SplitNonLeafNodeAlongPartition(
TreeType* tree,
TreeType* treeOne,
TreeType* treeTwo,
const size_t cutAxis,
const typename TreeType::ElemType cut);
/**
* This method is used to make sure that the tree has equivalent maximum depth
* in every branch. The method should be invoked if one of two resulting
* subtrees is empty after the split process
* (i.e. the subtree contains no children).
* The method convert the empty node into an empty subtree (increase the node
* in depth).
*
* @param tree One of two subtrees that is not empty.
* @param emptyTree The empty subtree.
*/
template<typename TreeType>
static void AddFakeNodes(const TreeType* tree, TreeType* emptyTree);
/**
* Partition a node using SweepType. This method invokes
* SweepType::Sweep(Non)LeafNode() for each dimension and chooses the
* best one. The method returns false if the node needn't partitioning.
* Overwise, the method returns true. If the method failed in finding
* an acceptable partition, the minCutAxis will be equal to the number of
* dimensions.
*
* @param node The node that is being split.
* @param minCutAxis The axis along which the node will be split.
* @param minCut The coordinate at which the node will be split.
*/
template<typename TreeType>
static bool PartitionNode(const TreeType* node,
size_t& minCutAxis,
typename TreeType::ElemType& minCut);
/**
* Insert a node into another node.
*/
template<typename TreeType>
static void InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode);
};
} // namespace tree
} // namespace mlpack
// Include implementation
#include "r_plus_tree_split_impl.hpp"
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_SPLIT_HPP
@@ -0,0 +1,358 @@
/**
* @file r_plus_tree_split_impl.hpp
* @author Mikhail Lozhnikov
*
* Implementation of class (RPlusTreeSplit) to split a RectangleTree.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_SPLIT_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_SPLIT_IMPL_HPP
#include "r_plus_tree_split.hpp"
#include "rectangle_tree.hpp"
#include "r_plus_plus_tree_auxiliary_information.hpp"
#include "r_plus_tree_split_policy.hpp"
#include "r_plus_plus_tree_split_policy.hpp"
namespace mlpack {
namespace tree {
template<typename SplitPolicyType,
template<typename> class SweepType>
template<typename TreeType>
void RPlusTreeSplit<SplitPolicyType, SweepType>::
SplitLeafNode(TreeType* tree, std::vector<bool>& relevels)
{
if (tree->Count() == 1)
{
// Check if an intermediate node was added during the insertion process.
// i.e. we couldn't enlarge a node of the R+ tree. So, one of intermediate
// nodes may be overflowed.
TreeType* node = tree->Parent();
while (node != NULL)
{
if (node->NumChildren() == node->MaxNumChildren() + 1)
{
// Split the overflowed node.
RPlusTreeSplit::SplitNonLeafNode(node,relevels);
return;
}
node = node->Parent();
}
return;
}
else if (tree->Count() <= tree->MaxLeafSize())
return;
// 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;
assert(tree->NumChildren() == 1);
RPlusTreeSplit::SplitLeafNode(copy,relevels);
return;
}
size_t cutAxis;
typename TreeType::ElemType cut;
// Try to find a partiotion of the node.
if ( !PartitionNode(tree, cutAxis, cut))
return;
// If we could not find a suitable partition.
if (cutAxis == tree->Bound().Dim())
{
tree->MaxLeafSize()++;
tree->points.resize(tree->MaxLeafSize() + 1);
Log::Warn << "Could not find an acceptable partition."
"The size of the node will be increased.";
return;
}
TreeType* treeOne = new TreeType(tree->Parent());
TreeType* treeTwo = new TreeType(tree->Parent());
treeOne->MinLeafSize() = 0;
treeOne->MinNumChildren() = 0;
treeTwo->MinLeafSize() = 0;
treeTwo->MinNumChildren() = 0;
// Split the node into two new nodes.
SplitLeafNodeAlongPartition(tree, treeOne, treeTwo, cutAxis, cut);
TreeType* parent = tree->Parent();
size_t i = 0;
while (parent->children[i] != tree)
i++;
assert(i < parent->NumChildren());
// Insert two new nodes to the tree.
parent->children[i] = treeOne;
parent->children[parent->NumChildren()++] = treeTwo;
assert(parent->NumChildren() <= parent->MaxNumChildren() + 1);
// Propagate the split upward if necessary.
if (parent->NumChildren() == parent->MaxNumChildren() + 1)
RPlusTreeSplit::SplitNonLeafNode(parent, relevels);
tree->SoftDelete();
}
template<typename SplitPolicyType,
template<typename> class SweepType>
template<typename TreeType>
bool RPlusTreeSplit<SplitPolicyType, SweepType>::
SplitNonLeafNode(TreeType* tree, std::vector<bool>& 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->NumChildren() = 0;
tree->NullifyData();
tree->children[(tree->NumChildren())++] = copy;
RPlusTreeSplit::SplitNonLeafNode(copy,relevels);
return true;
}
size_t cutAxis;
typename TreeType::ElemType cut;
// Try to find a partiotion of the node.
if ( !PartitionNode(tree, cutAxis, cut))
return false;
// If we could not find a suitable partition.
if (cutAxis == tree->Bound().Dim())
{
tree->MaxNumChildren()++;
tree->children.resize(tree->MaxNumChildren() + 1);
Log::Warn << "Could not find an acceptable partition."
"The size of the node will be increased.";
return false;
}
TreeType* treeOne = new TreeType(tree->Parent());
TreeType* treeTwo = new TreeType(tree->Parent());
treeOne->MinLeafSize() = 0;
treeOne->MinNumChildren() = 0;
treeTwo->MinLeafSize() = 0;
treeTwo->MinNumChildren() = 0;
// Split the node into two new nodes.
SplitNonLeafNodeAlongPartition(tree, treeOne, treeTwo, cutAxis, cut);
TreeType* parent = tree->Parent();
size_t i = 0;
while (parent->children[i] != tree)
i++;
assert(i < parent->NumChildren());
// Insert two new nodes to the tree.
parent->children[i] = treeOne;
parent->children[parent->NumChildren()++] = treeTwo;
tree->SoftDelete();
assert(parent->NumChildren() <= parent->MaxNumChildren() + 1);
// Propagate the split upward if necessary.
if (parent->NumChildren() == parent->MaxNumChildren() + 1)
RPlusTreeSplit::SplitNonLeafNode(parent, relevels);
return false;
}
template<typename SplitPolicyType,
template<typename> class SweepType>
template<typename TreeType>
void RPlusTreeSplit<SplitPolicyType, SweepType>::SplitLeafNodeAlongPartition(
TreeType* tree,
TreeType* treeOne,
TreeType* treeTwo,
const size_t cutAxis,
const typename TreeType::ElemType cut)
{
// Split the auxiliary information.
tree->AuxiliaryInfo().SplitAuxiliaryInfo(treeOne, treeTwo, cutAxis, cut);
// Insert points into the corresponding subtree.
for (size_t i = 0; i < tree->NumPoints(); i++)
{
if (tree->Dataset().col(tree->Point(i))[cutAxis] <= cut)
{
treeOne->Point(treeOne->Count()++) = tree->Point(i);
treeOne->Bound() |= tree->Dataset().col(tree->Point(i));
}
else
{
treeTwo->Point(treeTwo->Count()++) = tree->Point(i);
treeTwo->Bound() |= tree->Dataset().col(tree->Point(i));
}
}
// Update the number of descandants.
treeOne->numDescendants = treeOne->Count();
treeTwo->numDescendants = treeTwo->Count();
assert(treeOne->Count() <= treeOne->MaxLeafSize());
assert(treeTwo->Count() <= treeTwo->MaxLeafSize());
assert(tree->Count() == treeOne->Count() + treeTwo->Count());
assert(treeOne->Bound()[cutAxis].Hi() < treeTwo->Bound()[cutAxis].Lo());
}
template<typename SplitPolicyType,
template<typename> class SweepType>
template<typename TreeType>
void RPlusTreeSplit<SplitPolicyType, SweepType>::SplitNonLeafNodeAlongPartition(
TreeType* tree,
TreeType* treeOne,
TreeType* treeTwo,
const size_t cutAxis,
const typename TreeType::ElemType cut)
{
// Split the auxiliary information.
tree->AuxiliaryInfo().SplitAuxiliaryInfo(treeOne, treeTwo, cutAxis, cut);
// Insert children into the corresponding subtree.
for (size_t i = 0; i < tree->NumChildren(); i++)
{
TreeType* child = tree->children[i];
int policy = SplitPolicyType::GetSplitPolicy(*child, cutAxis, cut);
if (policy == SplitPolicyType::AssignToFirstTree)
{
InsertNodeIntoTree(treeOne, child);
child->Parent() = treeOne;
}
else if (policy == SplitPolicyType::AssignToSecondTree)
{
InsertNodeIntoTree(treeTwo, child);
child->Parent() = treeTwo;
}
else
{
// The child should be split (i.e. the partition divides its bound).
TreeType* childOne = new TreeType(treeOne);
TreeType* childTwo = new TreeType(treeTwo);
treeOne->MinLeafSize() = 0;
treeOne->MinNumChildren() = 0;
treeTwo->MinLeafSize() = 0;
treeTwo->MinNumChildren() = 0;
// Propagate the split downward.
if (child->IsLeaf())
SplitLeafNodeAlongPartition(child, childOne, childTwo, cutAxis, cut);
else
SplitNonLeafNodeAlongPartition(child, childOne, childTwo, cutAxis, cut);
InsertNodeIntoTree(treeOne, childOne);
InsertNodeIntoTree(treeTwo, childTwo);
child->SoftDelete();
}
}
assert(treeOne->NumChildren() + treeTwo->NumChildren() != 0);
// Add a fake subtree if one of the subtrees is empty.
if (treeOne->NumChildren() == 0)
AddFakeNodes(treeTwo, treeOne);
else if (treeTwo->NumChildren() == 0)
AddFakeNodes(treeOne, treeTwo);
assert(treeOne->NumChildren() <= treeOne->MaxNumChildren());
assert(treeTwo->NumChildren() <= treeTwo->MaxNumChildren());
}
template<typename SplitPolicyType,
template<typename> class SweepType>
template<typename TreeType>
void RPlusTreeSplit<SplitPolicyType, SweepType>::
AddFakeNodes(const TreeType* tree, TreeType* emptyTree)
{
size_t numDescendantNodes = tree->TreeDepth() - 1;
TreeType* node = emptyTree;
for (size_t i = 0; i < numDescendantNodes; i++)
{
TreeType* child = new TreeType(node);
node->children[node->NumChildren()++] = child;
node = child;
}
}
template<typename SplitPolicyType,
template<typename> class SweepType>
template<typename TreeType>
bool RPlusTreeSplit<SplitPolicyType, SweepType>::
PartitionNode(const TreeType* node, size_t& minCutAxis,
typename TreeType::ElemType& minCut)
{
if ((node->NumChildren() <= node->MaxNumChildren() && !node->IsLeaf()) ||
(node->Count() <= node->MaxLeafSize() && node->IsLeaf()))
return false; // No partition required.
// Define the type of the sweep cost.
typedef typename
SweepType<SplitPolicyType>::template SweepCost<TreeType>::type
SweepCostType;
SweepCostType minCost = std::numeric_limits<SweepCostType>::max();
minCutAxis = node->Bound().Dim();
// Find the sweep with a minimal cost.
for (size_t k = 0; k < node->Bound().Dim(); k++)
{
typename TreeType::ElemType cut;
SweepCostType cost;
if (node->IsLeaf())
cost = SweepType<SplitPolicyType>::SweepLeafNode(k, node, cut);
else
cost = SweepType<SplitPolicyType>::SweepNonLeafNode(k, node, cut);
if (cost < minCost)
{
minCost = cost;
minCutAxis = k;
minCut = cut;
}
}
return true;
}
template<typename SplitPolicyType,
template<typename> class SweepType>
template<typename TreeType>
void RPlusTreeSplit<SplitPolicyType, SweepType>::
InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode)
{
destTree->Bound() |= srcNode->Bound();
destTree->numDescendants += srcNode->numDescendants;
destTree->children[destTree->NumChildren()++] = srcNode;
}
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_IMPL_HPP
@@ -0,0 +1,75 @@
/**
* @file r_plus_tree_split_policy.hpp
* @author Mikhail Lozhnikov
*
* Defintion and implementation of the RPlusTreeSplitPolicy class, a class that
* helps to determine the subtree into which we should insert an intermediate
* node.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_SPLIT_POLICY_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_SPLIT_POLICY_HPP
namespace mlpack {
namespace tree {
/**
* The RPlusPlusTreeSplitPolicy helps to determine the subtree into which
* we should insert a child of an intermediate node that is being split.
* This class is designed for the R+ tree.
*/
class RPlusTreeSplitPolicy
{
public:
//! Indicate that the child should be split.
static const int SplitRequired = 0;
//! Indicate that the child should be inserted to the first subtree.
static const int AssignToFirstTree = 1;
//! Indicate that the child should be inserted to the second subtree.
static const int AssignToSecondTree = 2;
/**
* This method returns SplitRequired if a child of an intermediate node should
* be split, AssignToFirstTree if the child should be inserted to the first
* subtree, AssignToSecondTree if the child should be inserted to the second
* subtree. The method makes desicion according to the minimum bounding
* rectangle of the child, the axis along which the intermediate node is being
* split and the coordinate at which the node is being split.
*
* @param child A child of the node that is being split.
* @param axis The axis along which the node is being split.
* @param cut The coordinate at which the node is being split.
*/
template<typename TreeType>
static int GetSplitPolicy(const TreeType& child,
const size_t axis,
const typename TreeType::ElemType cut)
{
if (child.Bound()[axis].Hi() <= cut)
return AssignToFirstTree;
else if (child.Bound()[axis].Lo() >= cut)
return AssignToSecondTree;
return SplitRequired;
}
/**
* Return the minimum bounding rectangle of the node.
* This method should always return the bound that is used for the
* desicion-making in GetSplitPolicy().
*
* @param node The node whose bound is requested.
*/
template<typename TreeType>
static const
bound::HRectBound<metric::EuclideanDistance, typename TreeType::ElemType>&
Bound(const TreeType& node)
{
return node.Bound();
}
};
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_R_PLUS_TREE_SPLIT_POLICY_HPP
@@ -27,6 +27,9 @@ void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector<bool>& relevels)
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
if (tree->Count() <= tree->MaxLeafSize())
return;
// 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.
@@ -23,6 +23,8 @@ namespace tree {
template<typename TreeType>
void RTreeSplit::SplitLeafNode(TreeType *tree,std::vector<bool>& relevels)
{
if (tree->Count() <= tree->MaxLeafSize())
return;
// 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.
@@ -512,6 +512,9 @@ class RectangleTree
//! Friend access is given for the default constructor.
friend class boost::serialization::access;
//! Give friend access for DescentType.
friend DescentType;
//! Give friend access for SplitType.
friend SplitType;
@@ -692,12 +692,11 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
{
if (numChildren == 0)
{
// Check to see if we are full.
if (count <= maxLeafSize)
return; // We don't need to split.
// We let the SplitType check if the node if overflowed
// since an intermediate node of the R+ tree may be overflowed if the leaf
// node contains only one point.
// 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.
// The SplitType takes care of this and of moving up the tree if necessary.
SplitType::SplitLeafNode(this,relevels);
}
else
@@ -130,7 +130,65 @@ using HilbertRTree = RectangleTree<MetricType,
HilbertRTreeDescentHeuristic,
DiscreteHilbertRTreeAuxiliaryInformation>;
/**
* The R+ tree, a variant of the R tree that avoids overlapping rectangles.
* The implementation is modified from the original paper implementation.
* This template typedef satisfies the TreeType policy API.
*
* @code
* @inproceedings{sellis1987r,
* author = {Sellis, Timos K. and Roussopoulos, Nick and Faloutsos, Christos},
* title = {The R+-Tree: A Dynamic Index for Multi-Dimensional Objects},
* booktitle = {Proceedings of the 13th International Conference on Very
* Large Data Bases},
* series = {VLDB '87},
* year = {1987},
* isbn = {0-934613-46-X},
* pages = {507--518},
* numpages = {12},
* publisher = {Morgan Kaufmann Publishers Inc.},
* address = {San Francisco, CA, USA},
* }
* @endcode
*
* @see @ref trees, RTree, RTree, RPlusTree
*/
template<typename MetricType, typename StatisticType, typename MatType>
using RPlusTree = RectangleTree<MetricType,
StatisticType,
MatType,
RPlusTreeSplit<RPlusTreeSplitPolicy,
MinimalCoverageSweep>,
RPlusTreeDescentHeuristic,
NoAuxiliaryInformation>;
/**
* The R++ tree, a variant of the R+ tree with maximum buonding rectangles.
* This template typedef satisfies the TreeType policy API.
*
* @code
* @inproceedings{sumak2014r,
* author = {{\v{S}}um{\'a}k, Martin and Gursk{\'y}, Peter},
* title = {R++-Tree: An Efficient Spatial Access Method for Highly Redundant
* Point Data},
* booktitle = {New Trends in Databases and Information Systems: 17th East
* European Conference on Advances in Databases and Information Systems},
* year = {2014},
* isbn = {978-3-319-01863-8},
* pages = {37--44},
* publisher = {Springer International Publishing},
* }
* @endcode
*
* @see @ref trees, RTree, RTree, RPlusTree, RPlusPlusTree
*/template<typename MetricType, typename StatisticType, typename MatType>
using RPlusPlusTree = RectangleTree<MetricType,
StatisticType,
MatType,
RPlusTreeSplit<RPlusPlusTreeSplitPolicy,
MinimalSplitsNumberSweep>,
RPlusPlusTreeDescentHeuristic,
RPlusPlusTreeAuxiliaryInformation>;
} // namespace tree
} // namespace mlpack
@@ -26,6 +26,9 @@ void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector<bool>& relevels)
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
if (tree->Count() <= tree->MaxLeafSize())
return;
// 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.
@@ -62,8 +62,10 @@ 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', 'hilbert-r'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building.", "l", 20);
"'x', 'ball', 'hilbert-r', 'r-plus', 'r-plus-plus'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building (used for kd-trees, R "
"trees, R* trees, X trees, Hilbert R trees, R+ trees and R++ trees).", "l",
20);
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
"random orthogonal basis.", "R");
PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
@@ -188,9 +190,14 @@ int main(int argc, char *argv[])
tree = KFNModel::X_TREE;
else if (treeType == "hilbert-r")
tree = KFNModel::HILBERT_R_TREE;
else if (treeType == "r-plus")
tree = KFNModel::R_PLUS_TREE;
else if (treeType == "r-plus-plus")
tree = KFNModel::R_PLUS_PLUS_TREE;
else
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << endl;
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball', 'hilbert-r', "
<< "'r-plus' and 'r-plus-plus'." << endl;
kfn.TreeType() = tree;
kfn.RandomBasis() = randomBasis;
@@ -63,9 +63,10 @@ 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', 'hilbert-r'.", "t", "kd");
"'x', 'ball', 'hilbert-r', 'r-plus', 'r-plus-plus'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building (used for kd-trees, R "
"trees, and R* trees).", "l", 20);
"trees, R* trees, X trees, Hilbert R trees, R+ trees and R++ trees).", "l",
20);
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
"random orthogonal basis.", "R");
PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
@@ -174,9 +175,14 @@ int main(int argc, char *argv[])
tree = KNNModel::X_TREE;
else if (treeType == "hilbert-r")
tree = KNNModel::HILBERT_R_TREE;
else if (treeType == "r-plus")
tree = KNNModel::R_PLUS_TREE;
else if (treeType == "r-plus-plus")
tree = KNNModel::R_PLUS_PLUS_TREE;
else
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << endl;
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball', 'hilbert-r', "
<< "'r-plus' and 'r-plus-plus'." << endl;
knn.TreeType() = tree;
knn.RandomBasis() = randomBasis;
@@ -254,7 +254,9 @@ class NSModel
R_STAR_TREE,
BALL_TREE,
X_TREE,
HILBERT_R_TREE
HILBERT_R_TREE,
R_PLUS_TREE,
R_PLUS_PLUS_TREE
};
private:
@@ -280,7 +282,9 @@ class NSModel
NSType<SortPolicy, tree::RStarTree>*,
NSType<SortPolicy, tree::BallTree>*,
NSType<SortPolicy, tree::XTree>*,
NSType<SortPolicy, tree::HilbertRTree>*> nSearch;
NSType<SortPolicy, tree::HilbertRTree>*,
NSType<SortPolicy, tree::RPlusTree>*,
NSType<SortPolicy, tree::RPlusPlusTree>*> nSearch;
public:
/**
@@ -386,6 +386,14 @@ void NSModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
nSearch = new NSType<SortPolicy, tree::HilbertRTree>(naive, singleMode,
epsilon);
break;
case R_PLUS_TREE:
nSearch = new NSType<SortPolicy, tree::RPlusTree>(naive, singleMode,
epsilon);
break;
case R_PLUS_PLUS_TREE:
nSearch = new NSType<SortPolicy, tree::RPlusPlusTree>(naive, singleMode,
epsilon);
break;
}
TrainVisitor<SortPolicy> tn(std::move(referenceSet), leafSize);
@@ -466,6 +474,10 @@ std::string NSModel<SortPolicy>::TreeName() const
return "X tree";
case HILBERT_R_TREE:
return "Hilbert R tree";
case R_PLUS_TREE:
return "R+ tree";
case R_PLUS_PLUS_TREE:
return "R++ tree";
default:
return "unknown tree";
}
@@ -70,8 +70,10 @@ 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', 'hilbert-r'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building.", "l", 20);
"'x', 'ball', 'hilbert-r', 'r-plus', 'r-plus-plus'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building (used for kd-trees, R "
"trees, R* trees, X trees, Hilbert R trees, R+ trees and R++ trees).", "l",
20);
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
"random orthogonal basis.", "R");
PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
@@ -175,9 +177,14 @@ int main(int argc, char *argv[])
tree = RSModel::X_TREE;
else if (treeType == "hilbert-r")
tree = RSModel::HILBERT_R_TREE;
else if (treeType == "r-plus")
tree = RSModel::R_PLUS_TREE;
else if (treeType == "r-plus-plus")
tree = RSModel::R_PLUS_PLUS_TREE;
else
Log::Fatal << "Unknown tree type '" << treeType << "; valid choices are "
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << endl;
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball', 'hilbert-r', "
<< "'r-plus' and 'r-plus-plus'." << endl;
rs.TreeType() = tree;
rs.RandomBasis() = randomBasis;
+39 -1
View File
@@ -23,7 +23,9 @@ RSModel::RSModel(TreeTypes treeType, bool randomBasis) :
rStarTreeRS(NULL),
ballTreeRS(NULL),
xTreeRS(NULL),
hilbertRTreeRS(NULL)
hilbertRTreeRS(NULL),
rPlusTreeRS(NULL),
rPlusPlusTreeRS(NULL)
{
// Nothing to do.
}
@@ -128,6 +130,16 @@ void RSModel::BuildModel(arma::mat&& referenceSet,
hilbertRTreeRS = new RSType<tree::HilbertRTree>(move(referenceSet), naive,
singleMode);
break;
case R_PLUS_TREE:
rPlusTreeRS = new RSType<tree::RPlusTree>(move(referenceSet), naive,
singleMode);
break;
case R_PLUS_PLUS_TREE:
rPlusPlusTreeRS = new RSType<tree::RPlusPlusTree>(move(referenceSet), naive,
singleMode);
break;
}
if (!naive)
@@ -241,6 +253,14 @@ void RSModel::Search(arma::mat&& querySet,
case HILBERT_R_TREE:
hilbertRTreeRS->Search(querySet, range, neighbors, distances);
break;
case R_PLUS_TREE:
rPlusTreeRS->Search(querySet, range, neighbors, distances);
break;
case R_PLUS_PLUS_TREE:
rPlusPlusTreeRS->Search(querySet, range, neighbors, distances);
break;
}
}
@@ -287,6 +307,14 @@ void RSModel::Search(const math::Range& range,
case HILBERT_R_TREE:
hilbertRTreeRS->Search(range, neighbors, distances);
break;
case R_PLUS_TREE:
rPlusTreeRS->Search(range, neighbors, distances);
break;
case R_PLUS_PLUS_TREE:
rPlusPlusTreeRS->Search(range, neighbors, distances);
break;
}
}
@@ -309,6 +337,10 @@ std::string RSModel::TreeName() const
return "X tree";
case HILBERT_R_TREE:
return "Hilbert R tree";
case R_PLUS_TREE:
return "R+ tree";
case R_PLUS_PLUS_TREE:
return "R++ tree";
default:
return "unknown tree";
}
@@ -331,6 +363,10 @@ void RSModel::CleanMemory()
delete xTreeRS;
if (hilbertRTreeRS)
delete hilbertRTreeRS;
if (rPlusTreeRS)
delete rPlusTreeRS;
if (rPlusPlusTreeRS)
delete rPlusPlusTreeRS;
kdTreeRS = NULL;
coverTreeRS = NULL;
@@ -339,4 +375,6 @@ void RSModel::CleanMemory()
ballTreeRS = NULL;
xTreeRS = NULL;
hilbertRTreeRS = NULL;
rPlusTreeRS = NULL;
rPlusPlusTreeRS = NULL;
}
+7 -1
View File
@@ -30,7 +30,9 @@ class RSModel
R_STAR_TREE,
BALL_TREE,
X_TREE,
HILBERT_R_TREE
HILBERT_R_TREE,
R_PLUS_TREE,
R_PLUS_PLUS_TREE
};
private:
@@ -63,6 +65,10 @@ class RSModel
RSType<tree::XTree>* xTreeRS;
//! Hilbert R tree based range search object (NULL if not in use).
RSType<tree::HilbertRTree>* hilbertRTreeRS;
//! R+ tree based range search object (NULL if not in use).
RSType<tree::RPlusTree>* rPlusTreeRS;
//! R++ tree based range search object (NULL if not in use).
RSType<tree::RPlusPlusTree>* rPlusPlusTreeRS;
public:
/**
@@ -57,6 +57,14 @@ void RSModel::Serialize(Archive& ar, const unsigned int /* version */)
case HILBERT_R_TREE:
ar & CreateNVP(hilbertRTreeRS, "range_search_model");
break;
case R_PLUS_TREE:
ar & CreateNVP(rPlusTreeRS, "range_search_model");
break;
case R_PLUS_PLUS_TREE:
ar & CreateNVP(rPlusPlusTreeRS, "range_search_model");
break;
}
}
@@ -76,6 +84,10 @@ inline const arma::mat& RSModel::Dataset() const
return xTreeRS->ReferenceSet();
else if (hilbertRTreeRS)
return hilbertRTreeRS->ReferenceSet();
else if (rPlusTreeRS)
return rPlusTreeRS->ReferenceSet();
else if (rPlusPlusTreeRS)
return rPlusPlusTreeRS->ReferenceSet();
throw std::runtime_error("no range search model initialized");
}
@@ -96,6 +108,10 @@ inline bool RSModel::SingleMode() const
return xTreeRS->SingleMode();
else if (hilbertRTreeRS)
return hilbertRTreeRS->SingleMode();
else if (rPlusTreeRS)
return rPlusTreeRS->SingleMode();
else if (rPlusPlusTreeRS)
return rPlusPlusTreeRS->SingleMode();
throw std::runtime_error("no range search model initialized");
}
@@ -116,6 +132,10 @@ inline bool& RSModel::SingleMode()
return xTreeRS->SingleMode();
else if (hilbertRTreeRS)
return hilbertRTreeRS->SingleMode();
else if (rPlusTreeRS)
return rPlusTreeRS->SingleMode();
else if (rPlusPlusTreeRS)
return rPlusPlusTreeRS->SingleMode();
throw std::runtime_error("no range search model initialized");
}
@@ -136,6 +156,10 @@ inline bool RSModel::Naive() const
return xTreeRS->Naive();
else if (hilbertRTreeRS)
return hilbertRTreeRS->Naive();
else if (rPlusTreeRS)
return rPlusTreeRS->Naive();
else if (rPlusPlusTreeRS)
return rPlusPlusTreeRS->Naive();
throw std::runtime_error("no range search model initialized");
}
@@ -156,6 +180,10 @@ inline bool& RSModel::Naive()
return xTreeRS->Naive();
else if (hilbertRTreeRS)
return hilbertRTreeRS->Naive();
else if (rPlusTreeRS)
return rPlusTreeRS->Naive();
else if (rPlusPlusTreeRS)
return rPlusPlusTreeRS->Naive();
throw std::runtime_error("no range search model initialized");
}
+9 -3
View File
@@ -64,9 +64,10 @@ 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', 'hilbert-r'.", "t", "kd");
"'x', 'r-star', 'hilbert-r', 'r-plus', 'r-plus-plus'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building (used for kd-trees, R "
"trees, and R* trees).", "l", 20);
"trees, R* trees, X trees, Hilbert R trees, R+ trees and R++ trees).", "l",
20);
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
"random orthogonal basis.", "R");
PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
@@ -174,9 +175,14 @@ int main(int argc, char *argv[])
tree = RANNModel::X_TREE;
else if (treeType == "hilbert-r")
tree = RANNModel::HILBERT_R_TREE;
else if (treeType == "r-plus")
tree = RANNModel::R_PLUS_TREE;
else if (treeType == "r-plus-plus")
tree = RANNModel::R_PLUS_PLUS_TREE;
else
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
<< "'kd', 'cover', 'r', 'r-star', 'x' and 'hilbert-r'." << endl;
<< "'kd', 'cover', 'r', 'r-star', 'x', 'hilbert-r', "
<< "'r-plus' and 'r-plus-plus'." << endl;
rann.TreeType() = tree;
rann.RandomBasis() = randomBasis;
+7 -1
View File
@@ -41,7 +41,9 @@ class RAModel
R_TREE,
R_STAR_TREE,
X_TREE,
HILBERT_R_TREE
HILBERT_R_TREE,
R_PLUS_TREE,
R_PLUS_PLUS_TREE
};
private:
@@ -76,6 +78,10 @@ class RAModel
RAType<tree::XTree>* xTreeRA;
//! Non-NULL if the Hilbert R tree is used.
RAType<tree::HilbertRTree>* hilbertRTreeRA;
//! Non-NULL if the R+ tree is used.
RAType<tree::RPlusTree>* rPlusTreeRA;
//! Non-NULL if the R++ tree is used.
RAType<tree::RPlusPlusTree>* rPlusPlusTreeRA;
public:
/**
+109 -1
View File
@@ -23,7 +23,9 @@ RAModel<SortPolicy>::RAModel(const TreeTypes treeType, const bool randomBasis) :
rTreeRA(NULL),
rStarTreeRA(NULL),
xTreeRA(NULL),
hilbertRTreeRA(NULL)
hilbertRTreeRA(NULL),
rPlusTreeRA(NULL),
rPlusPlusTreeRA(NULL)
{
// Nothing to do.
}
@@ -43,6 +45,10 @@ RAModel<SortPolicy>::~RAModel()
delete xTreeRA;
if (hilbertRTreeRA)
delete hilbertRTreeRA;
if (rPlusTreeRA)
delete rPlusTreeRA;
if (rPlusPlusTreeRA)
delete rPlusPlusTreeRA;
}
template<typename SortPolicy>
@@ -69,6 +75,10 @@ void RAModel<SortPolicy>::Serialize(Archive& ar,
delete xTreeRA;
if (hilbertRTreeRA)
delete hilbertRTreeRA;
if (rPlusTreeRA)
delete rPlusTreeRA;
if (rPlusPlusTreeRA)
delete rPlusPlusTreeRA;
// Set all the pointers to NULL.
kdTreeRA = NULL;
@@ -77,6 +87,8 @@ void RAModel<SortPolicy>::Serialize(Archive& ar,
rStarTreeRA = NULL;
xTreeRA = NULL;
hilbertRTreeRA = NULL;
rPlusPlusTreeRA = NULL;
rPlusTreeRA = NULL;
}
// We only need to serialize one of the kRANN objects.
@@ -100,6 +112,12 @@ void RAModel<SortPolicy>::Serialize(Archive& ar,
case HILBERT_R_TREE:
ar & data::CreateNVP(hilbertRTreeRA, "ra_model");
break;
case R_PLUS_TREE:
ar & data::CreateNVP(rPlusTreeRA, "ra_model");
break;
case R_PLUS_PLUS_TREE:
ar & data::CreateNVP(rPlusPlusTreeRA, "ra_model");
break;
}
}
@@ -118,6 +136,10 @@ const arma::mat& RAModel<SortPolicy>::Dataset() const
return xTreeRA->ReferenceSet();
else if (hilbertRTreeRA)
return hilbertRTreeRA->ReferenceSet();
else if (rPlusTreeRA)
return rPlusTreeRA->ReferenceSet();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->ReferenceSet();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -138,6 +160,10 @@ bool RAModel<SortPolicy>::Naive() const
return xTreeRA->Naive();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Naive();
else if (rPlusTreeRA)
return rPlusTreeRA->Naive();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->Naive();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -158,6 +184,10 @@ bool& RAModel<SortPolicy>::Naive()
return xTreeRA->Naive();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Naive();
else if (rPlusTreeRA)
return rPlusTreeRA->Naive();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->Naive();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -178,6 +208,10 @@ bool RAModel<SortPolicy>::SingleMode() const
return xTreeRA->SingleMode();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SingleMode();
else if (rPlusTreeRA)
return rPlusTreeRA->SingleMode();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->SingleMode();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -198,6 +232,10 @@ bool& RAModel<SortPolicy>::SingleMode()
return xTreeRA->SingleMode();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SingleMode();
else if (rPlusTreeRA)
return rPlusTreeRA->SingleMode();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->SingleMode();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -218,6 +256,10 @@ double RAModel<SortPolicy>::Tau() const
return xTreeRA->Tau();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Tau();
else if (rPlusTreeRA)
return rPlusTreeRA->Tau();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->Tau();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -238,6 +280,10 @@ double& RAModel<SortPolicy>::Tau()
return xTreeRA->Tau();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Tau();
else if (rPlusTreeRA)
return rPlusTreeRA->Tau();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->Tau();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -258,6 +304,10 @@ double RAModel<SortPolicy>::Alpha() const
return xTreeRA->Alpha();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Alpha();
else if (rPlusTreeRA)
return rPlusTreeRA->Alpha();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->Alpha();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -278,6 +328,10 @@ double& RAModel<SortPolicy>::Alpha()
return xTreeRA->Alpha();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Alpha();
else if (rPlusTreeRA)
return rPlusTreeRA->Alpha();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->Alpha();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -298,6 +352,10 @@ bool RAModel<SortPolicy>::SampleAtLeaves() const
return xTreeRA->SampleAtLeaves();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SampleAtLeaves();
else if (rPlusTreeRA)
return rPlusTreeRA->SampleAtLeaves();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->SampleAtLeaves();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -318,6 +376,10 @@ bool& RAModel<SortPolicy>::SampleAtLeaves()
return xTreeRA->SampleAtLeaves();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SampleAtLeaves();
else if (rPlusTreeRA)
return rPlusTreeRA->SampleAtLeaves();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->SampleAtLeaves();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -338,6 +400,10 @@ bool RAModel<SortPolicy>::FirstLeafExact() const
return xTreeRA->FirstLeafExact();
else if (hilbertRTreeRA)
return hilbertRTreeRA->FirstLeafExact();
else if (rPlusTreeRA)
return rPlusTreeRA->FirstLeafExact();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->FirstLeafExact();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -358,6 +424,10 @@ bool& RAModel<SortPolicy>::FirstLeafExact()
return xTreeRA->FirstLeafExact();
else if (hilbertRTreeRA)
return hilbertRTreeRA->FirstLeafExact();
else if (rPlusTreeRA)
return rPlusTreeRA->FirstLeafExact();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->FirstLeafExact();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -378,6 +448,10 @@ size_t RAModel<SortPolicy>::SingleSampleLimit() const
return xTreeRA->SingleSampleLimit();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SingleSampleLimit();
else if (rPlusTreeRA)
return rPlusTreeRA->SingleSampleLimit();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->SingleSampleLimit();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -398,6 +472,10 @@ size_t& RAModel<SortPolicy>::SingleSampleLimit()
return xTreeRA->SingleSampleLimit();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SingleSampleLimit();
else if (rPlusTreeRA)
return rPlusTreeRA->SingleSampleLimit();
else if (rPlusPlusTreeRA)
return rPlusPlusTreeRA->SingleSampleLimit();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -465,6 +543,10 @@ void RAModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
delete xTreeRA;
if (hilbertRTreeRA)
delete hilbertRTreeRA;
if (rPlusTreeRA)
delete rPlusTreeRA;
if (rPlusPlusTreeRA)
delete rPlusPlusTreeRA;
if (randomBasis)
referenceSet = q * referenceSet;
@@ -517,6 +599,14 @@ void RAModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
hilbertRTreeRA = new RAType<tree::HilbertRTree>(std::move(referenceSet),
naive, singleMode);
break;
case R_PLUS_TREE:
rPlusTreeRA = new RAType<tree::RPlusTree>(std::move(referenceSet),
naive, singleMode);
break;
case R_PLUS_PLUS_TREE:
rPlusPlusTreeRA = new RAType<tree::RPlusPlusTree>(std::move(referenceSet),
naive, singleMode);
break;
}
if (!naive)
@@ -598,6 +688,14 @@ void RAModel<SortPolicy>::Search(arma::mat&& querySet,
// No mapping necessary.
hilbertRTreeRA->Search(querySet, k, neighbors, distances);
break;
case R_PLUS_TREE:
// No mapping necessary.
rPlusTreeRA->Search(querySet, k, neighbors, distances);
break;
case R_PLUS_PLUS_TREE:
// No mapping necessary.
rPlusPlusTreeRA->Search(querySet, k, neighbors, distances);
break;
}
}
@@ -635,6 +733,12 @@ void RAModel<SortPolicy>::Search(const size_t k,
case HILBERT_R_TREE:
hilbertRTreeRA->Search(k, neighbors, distances);
break;
case R_PLUS_TREE:
rPlusTreeRA->Search(k, neighbors, distances);
break;
case R_PLUS_PLUS_TREE:
rPlusPlusTreeRA->Search(k, neighbors, distances);
break;
}
}
@@ -655,6 +759,10 @@ std::string RAModel<SortPolicy>::TreeName() const
return "X tree";
case HILBERT_R_TREE:
return "Hilbert R tree";
case R_PLUS_TREE:
return "R+ tree";
case R_PLUS_PLUS_TREE:
return "R++ tree";
default:
return "unknown tree";
}
+6 -2
View File
@@ -352,7 +352,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
KNNModel models[14];
KNNModel models[18];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
@@ -367,6 +367,10 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
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);
models[14] = KNNModel(KNNModel::TreeTypes::R_PLUS_TREE, true);
models[15] = KNNModel(KNNModel::TreeTypes::R_PLUS_TREE, false);
models[16] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, true);
models[17] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, false);
for (size_t j = 0; j < 2; ++j)
{
@@ -376,7 +380,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
arma::mat distancesExact;
exact.Search(3, neighborsExact, distancesExact);
for (size_t i = 0; i < 14; ++i)
for (size_t i = 0; i < 18; ++i)
{
// We only have a std::move() constructor... so copy the data.
arma::mat referenceCopy(referenceData);
+12 -4
View File
@@ -977,7 +977,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
KNNModel models[14];
KNNModel models[18];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
@@ -992,6 +992,10 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
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);
models[14] = KNNModel(KNNModel::TreeTypes::R_PLUS_TREE, true);
models[15] = KNNModel(KNNModel::TreeTypes::R_PLUS_TREE, false);
models[16] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, true);
models[17] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, false);
for (size_t j = 0; j < 2; ++j)
{
@@ -1001,7 +1005,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
arma::mat baselineDistances;
knn.Search(queryData, 3, baselineNeighbors, baselineDistances);
for (size_t i = 0; i < 14; ++i)
for (size_t i = 0; i < 18; ++i)
{
// We only have std::move() constructors so make a copy of our data.
arma::mat referenceCopy(referenceData);
@@ -1045,7 +1049,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
KNNModel models[14];
KNNModel models[18];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
@@ -1060,6 +1064,10 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
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);
models[14] = KNNModel(KNNModel::TreeTypes::R_PLUS_TREE, true);
models[15] = KNNModel(KNNModel::TreeTypes::R_PLUS_TREE, false);
models[16] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, true);
models[17] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, false);
for (size_t j = 0; j < 2; ++j)
{
@@ -1069,7 +1077,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
arma::mat baselineDistances;
knn.Search(3, baselineNeighbors, baselineDistances);
for (size_t i = 0; i < 14; ++i)
for (size_t i = 0; i < 18; ++i)
{
// We only have a std::move() constructor... so copy the data.
arma::mat referenceCopy(referenceData);
+6 -2
View File
@@ -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[12];
KNNModel models[16];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false);
@@ -638,13 +638,17 @@ BOOST_AUTO_TEST_CASE(RAModelTest)
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);
models[12] = KNNModel(KNNModel::TreeTypes::R_PLUS_TREE, false);
models[13] = KNNModel(KNNModel::TreeTypes::R_PLUS_TREE, true);
models[14] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, false);
models[15] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, true);
arma::Mat<size_t> 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 < 12; ++i)
for (size_t i = 0; i < 16; ++i)
{
// We only have std::move() constructors so make a copy of our data.
arma::mat referenceCopy(referenceData);
+12 -4
View File
@@ -1249,7 +1249,7 @@ BOOST_AUTO_TEST_CASE(RSModelTest)
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
RSModel models[14];
RSModel models[18];
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,10 @@ BOOST_AUTO_TEST_CASE(RSModelTest)
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);
models[14] = RSModel(RSModel::TreeTypes::R_PLUS_TREE, true);
models[15] = RSModel(RSModel::TreeTypes::R_PLUS_TREE, false);
models[16] = RSModel(RSModel::TreeTypes::R_PLUS_PLUS_TREE, true);
models[17] = RSModel(RSModel::TreeTypes::R_PLUS_PLUS_TREE, false);
for (size_t j = 0; j < 2; ++j)
{
@@ -1277,7 +1281,7 @@ BOOST_AUTO_TEST_CASE(RSModelTest)
vector<vector<pair<double, size_t>>> baselineSorted;
SortResults(baselineNeighbors, baselineDistances, baselineSorted);
for (size_t i = 0; i < 14; ++i)
for (size_t i = 0; i < 18; ++i)
{
// We only have std::move() constructors, so make a copy of our data.
arma::mat referenceCopy(referenceData);
@@ -1321,7 +1325,7 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest)
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
RSModel models[14];
RSModel models[18];
models[0] = RSModel(RSModel::TreeTypes::KD_TREE, true);
models[1] = RSModel(RSModel::TreeTypes::KD_TREE, false);
models[2] = RSModel(RSModel::TreeTypes::COVER_TREE, true);
@@ -1336,6 +1340,10 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest)
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);
models[14] = RSModel(RSModel::TreeTypes::R_PLUS_TREE, true);
models[15] = RSModel(RSModel::TreeTypes::R_PLUS_TREE, false);
models[16] = RSModel(RSModel::TreeTypes::R_PLUS_PLUS_TREE, true);
models[17] = RSModel(RSModel::TreeTypes::R_PLUS_PLUS_TREE, false);
for (size_t j = 0; j < 2; ++j)
{
@@ -1348,7 +1356,7 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest)
vector<vector<pair<double, size_t>>> baselineSorted;
SortResults(baselineNeighbors, baselineDistances, baselineSorted);
for (size_t i = 0; i < 14; ++i)
for (size_t i = 0; i < 18; ++i)
{
// We only have std::move() cosntructors, so make a copy of our data.
arma::mat referenceCopy(referenceData);
+228 -1
View File
@@ -137,7 +137,17 @@ void CheckContainment(const TreeType& tree)
for (size_t i = 0; i < tree.NumChildren(); i++)
{
for (size_t j = 0; j < tree.Bound().Dim(); j++)
BOOST_REQUIRE(tree.Bound()[j].Contains(tree.Child(i).Bound()[j]));
{
// All children should be covered by the parent node.
// Some children can be empty (only in case of the R++ tree)
bool success = (tree.Child(i).Bound()[j].Hi() ==
std::numeric_limits<typename TreeType::ElemType>::lowest() &&
tree.Child(i).Bound()[j].Lo() ==
std::numeric_limits<typename TreeType::ElemType>::max()) ||
tree.Bound()[j].Contains(tree.Child(i).Bound()[j]);
BOOST_REQUIRE(success);
}
CheckContainment(tree.Child(i));
}
@@ -854,6 +864,223 @@ BOOST_AUTO_TEST_CASE(DiscreteHilbertValueTest)
point4), -1);
}
template<typename TreeType>
void CheckOverlap(const TreeType& tree)
{
bool success = true;
// Check if two nodes overlap each other.
for (size_t i = 0; i < tree.NumChildren(); i++)
{
success = true;
for (size_t j = 0; j < tree.NumChildren(); j++)
{
if (j == i)
continue;
success = !tree.Child(i).Bound().Contains(tree.Child(j).Bound());
if (!success)
break;
}
if (!success)
break;
}
BOOST_REQUIRE_EQUAL(success, true);
for (size_t i = 0; i < tree.NumChildren(); i++)
CheckOverlap(tree.Child(i));
}
BOOST_AUTO_TEST_CASE(RPlusTreeOverlapTest)
{
arma::mat dataset;
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
typedef RPlusTree<EuclideanDistance,
NeighborSearchStat<NearestNeighborSort>,arma::mat> TreeType;
TreeType rPlusTree(dataset, 20, 6, 5, 2, 0);
CheckOverlap(rPlusTree);
// Ensure that all leaf nodes are at the same level.
BOOST_REQUIRE_EQUAL(GetMinLevel(rPlusTree), GetMaxLevel(rPlusTree));
BOOST_REQUIRE_EQUAL(rPlusTree.TreeDepth(), GetMinLevel(rPlusTree));
}
BOOST_AUTO_TEST_CASE(RPlusTreeTraverserTest)
{
arma::mat dataset;
const int numP = 1000;
dataset.randu(8, numP); // 1000 points in 8 dimensions.
arma::Mat<size_t> neighbors1;
arma::mat distances1;
arma::Mat<size_t> neighbors2;
arma::mat distances2;
typedef RPlusTree<EuclideanDistance, NeighborSearchStat<NearestNeighborSort>,
arma::mat > TreeType;
TreeType rPlusTree(dataset, 20, 6, 5, 2, 0);
// Nearest neighbor search with the R+ tree.
NeighborSearch<NearestNeighborSort, metric::LMetric<2, true>, arma::mat,
RPlusTree > knn1(&rPlusTree, true);
BOOST_REQUIRE_EQUAL(rPlusTree.NumDescendants(), numP);
CheckContainment(rPlusTree);
CheckExactContainment(rPlusTree);
CheckHierarchy(rPlusTree);
CheckOverlap(rPlusTree);
CheckNumDescendants(rPlusTree);
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<typename TreeType>
void CheckRPlusPlusTreeBound(const TreeType& tree)
{
typedef bound::HRectBound<metric::EuclideanDistance,
typename TreeType::ElemType> Bound;
bool success = true;
// Ensure that the maximum bounding rectangle contains all children.
for (size_t k = 0; k < tree.Bound().Dim(); k++)
{
BOOST_REQUIRE_LE(tree.Bound()[k].Hi(),
tree.AuxiliaryInfo().OuterBound()[k].Hi());
BOOST_REQUIRE_LE(tree.AuxiliaryInfo().OuterBound()[k].Lo(),
tree.Bound()[k].Lo());
}
if (tree.IsLeaf())
{
// Ensure that the maximum bounding rectangle contains all points.
for (size_t i = 0; i < tree.Count(); i++)
BOOST_REQUIRE_EQUAL(true,
tree.Bound().Contains(tree.Dataset().col(tree.Point(i))));
return;
}
// Ensure that two children's maximum bounding rectangles do not overlap
// each other.
for (size_t i = 0; i < tree.NumChildren(); i++)
{
const Bound& bound1 = tree.Child(i).AuxiliaryInfo().OuterBound();
success = true;
for (size_t j = 0; j < tree.NumChildren(); j++)
{
if (j == i)
continue;
const Bound& bound2 = tree.Child(j).AuxiliaryInfo().OuterBound();
success = !bound1.Contains(bound2);
if (!success)
break;
}
if (!success)
break;
}
BOOST_REQUIRE_EQUAL(success, true);
for (size_t i = 0; i < tree.NumChildren(); i++)
CheckRPlusPlusTreeBound(tree.Child(i));
}
BOOST_AUTO_TEST_CASE(RPlusPlusTreeBoundTest)
{
arma::mat dataset;
dataset.randu(8, 1000); // 1000 points in 8 dimensions.
// Check the MinimalCoverageSweep.
typedef RPlusPlusTree<EuclideanDistance,
NeighborSearchStat<NearestNeighborSort>,arma::mat> TreeType;
TreeType rPlusPlusTree(dataset, 20, 6, 5, 2, 0);
CheckRPlusPlusTreeBound(rPlusPlusTree);
BOOST_REQUIRE_EQUAL(GetMinLevel(rPlusPlusTree), GetMaxLevel(rPlusPlusTree));
BOOST_REQUIRE_EQUAL(rPlusPlusTree.TreeDepth(), GetMinLevel(rPlusPlusTree));
// Check the MinimalSplitsNumberSweep.
typedef RectangleTree<EuclideanDistance,
NeighborSearchStat<NearestNeighborSort>, arma::mat,
RPlusTreeSplit<RPlusPlusTreeSplitPolicy, MinimalCoverageSweep>,
RPlusPlusTreeDescentHeuristic, RPlusPlusTreeAuxiliaryInformation>
RPlusPlusTreeMinimalSplits;
RPlusPlusTreeMinimalSplits rPlusPlusTree2(dataset, 20, 6, 5, 2, 0);
CheckRPlusPlusTreeBound(rPlusPlusTree2);
BOOST_REQUIRE_EQUAL(GetMinLevel(rPlusPlusTree2), GetMaxLevel(rPlusPlusTree2));
BOOST_REQUIRE_EQUAL(rPlusPlusTree2.TreeDepth(), GetMinLevel(rPlusPlusTree2));
}
BOOST_AUTO_TEST_CASE(RPlusPlusTreeTraverserTest)
{
arma::mat dataset;
const int numP = 1000;
dataset.randu(8, numP); // 1000 points in 8 dimensions.
arma::Mat<size_t> neighbors1;
arma::mat distances1;
arma::Mat<size_t> neighbors2;
arma::mat distances2;
typedef RPlusPlusTree<EuclideanDistance,
NeighborSearchStat<NearestNeighborSort>, arma::mat > TreeType;
TreeType rPlusPlusTree(dataset, 20, 6, 5, 2, 0);
// Nearest neighbor search with the R++ tree.
NeighborSearch<NearestNeighborSort, metric::LMetric<2, true>,
arma::mat, RPlusPlusTree > knn1(&rPlusPlusTree, true);
BOOST_REQUIRE_EQUAL(rPlusPlusTree.NumDescendants(), numP);
CheckContainment(rPlusPlusTree);
CheckExactContainment(rPlusPlusTree);
CheckHierarchy(rPlusPlusTree);
CheckRPlusPlusTreeBound(rPlusPlusTree);
CheckNumDescendants(rPlusPlusTree);
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]);
}
}
// Test the tree splitting. We set MaxLeafSize and MaxNumChildren rather low
// to allow us to test by hand without adding hundreds of points.
BOOST_AUTO_TEST_CASE(RTreeSplitTest)
+2 -26
View File
@@ -1272,10 +1272,6 @@ void GenerateVectorOfTree(TreeType* node,
size_t depth,
std::vector<TreeType*>& v);
template<typename MetricType>
bool DoBoundsIntersect(HRectBound<MetricType>& a,
HRectBound<MetricType>& b);
/**
* Exhaustive kd-tree test based on #125.
*
@@ -1344,7 +1340,7 @@ BOOST_AUTO_TEST_CASE(KdTreeTest)
for (size_t i = depth; i < 2 * depth && i < v.size(); i++)
for (size_t j = i + 1; j < 2 * depth && j < v.size(); j++)
if (v[i] != NULL && v[j] != NULL)
BOOST_REQUIRE(!DoBoundsIntersect(v[i]->Bound(), v[j]->Bound()));
BOOST_REQUIRE(!v[i]->Bound().Contains(v[j]->Bound()));
depth *= 2;
}
@@ -1430,26 +1426,6 @@ BOOST_AUTO_TEST_CASE(BallTreeTest)
}
}
template<typename MetricType>
bool DoBoundsIntersect(HRectBound<MetricType>& a,
HRectBound<MetricType>& b)
{
size_t dimensionality = a.Dim();
Range r_a;
Range r_b;
for (size_t i = 0; i < dimensionality; i++)
{
r_a = a[i];
r_b = b[i];
if (r_a < r_b || r_a > r_b) // If a does not overlap b at all.
return false;
}
return true;
}
template<typename TreeType>
void GenerateVectorOfTree(TreeType* node,
size_t depth,
@@ -1541,7 +1517,7 @@ BOOST_AUTO_TEST_CASE(ExhaustiveSparseKDTreeTest)
for (size_t i = depth; i < 2 * depth && i < v.size(); i++)
for (size_t j = i + 1; j < 2 * depth && j < v.size(); j++)
if (v[i] != NULL && v[j] != NULL)
BOOST_REQUIRE(!DoBoundsIntersect(v[i]->Bound(), v[j]->Bound()));
BOOST_REQUIRE(!v[i]->Bound().Contains(v[j]->Bound()));
depth *= 2;
}