Merge pull request #699 from lozhnikov/r_plus_tree-cherry_pick
R+ and R++ trees implementation
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
/**
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user