Merges multiprobe LSH

This commit is contained in:
Yannis Mentekidis
2016-07-01 15:48:25 +01:00
143 changed files with 4777 additions and 1061 deletions
+15 -1
View File
@@ -1,5 +1,19 @@
### mlpack 2.0.2
### mlpack 2.0.3
###### 2016-??-??
* Added multiprobe LSH (#691). The parameter 'T' to LSHSearch::Search() can
now be used to control the number of extra bins that are probed, as can the
-T (--num_probes) option to mlpack_lsh.
* Added the Hilbert R tree to src/mlpack/core/tree/rectangle_tree/ (#664). It
can be used as the typedef HilbertRTree, and it is now an option in the
mlpack_knn, mlpack_kfn, mlpack_range_search, and mlpack_krann command-line
programs.
* Added the mlpack_preprocess_split and mlpack_preprocess_binarize programs,
which can be used for preprocessing code (#650, #666).
### mlpack 2.0.2
###### 2016-06-20
* Added the function LSHSearch::Projections(), which returns an arma::cube
with each projection table in a slice (#663). Instead of Projection(i), you
should now use Projections().slice(i).
+2 -2
View File
@@ -71,7 +71,7 @@ If you are compiling Armadillo by hand, ensure that LAPACK and BLAS are enabled.
4. Building mlpack from source
------------------------------
(see also [Building mlpack From Source](http://www.mlpack.org/doxygen.php?doc=build.html))
(see also [Building mlpack From Source](http://www.mlpack.org/docs/mlpack-git/doxygen.php?doc=build.html))
mlpack uses CMake as a build system and allows several flexible build
configuration options. One can consult any of numerous CMake tutorials for
@@ -200,7 +200,7 @@ older versions of mlpack:
- [mlpack homepage](http://www.mlpack.org/)
- [Tutorials](http://www.mlpack.org/tutorials.html)
- [Development Site (Github)](https://www.github.com/mlpack/mlpack/)
- [API documentation](http://www.mlpack.org/doxygen.php)
- [API documentation](http://www.mlpack.org/docs/mlpack-git/doxygen.php)
7. Bug reporting
----------------
+10
View File
@@ -43,6 +43,7 @@ set(SOURCES
rectangle_tree/dual_tree_traverser_impl.hpp
rectangle_tree/r_tree_split.hpp
rectangle_tree/r_tree_split_impl.hpp
rectangle_tree/no_auxiliary_information.hpp
rectangle_tree/r_tree_descent_heuristic.hpp
rectangle_tree/r_tree_descent_heuristic_impl.hpp
rectangle_tree/r_star_tree_descent_heuristic.hpp
@@ -51,6 +52,15 @@ set(SOURCES
rectangle_tree/r_star_tree_split_impl.hpp
rectangle_tree/x_tree_split.hpp
rectangle_tree/x_tree_split_impl.hpp
rectangle_tree/x_tree_auxiliary_information.hpp
rectangle_tree/hilbert_r_tree_descent_heuristic.hpp
rectangle_tree/hilbert_r_tree_descent_heuristic_impl.hpp
rectangle_tree/hilbert_r_tree_split.hpp
rectangle_tree/hilbert_r_tree_split_impl.hpp
rectangle_tree/hilbert_r_tree_auxiliary_information.hpp
rectangle_tree/hilbert_r_tree_auxiliary_information_impl.hpp
rectangle_tree/discrete_hilbert_value.hpp
rectangle_tree/discrete_hilbert_value_impl.hpp
statistic.hpp
traversal_info.hpp
tree_traits.hpp
+6
View File
@@ -19,10 +19,16 @@
#include "rectangle_tree/dual_tree_traverser_impl.hpp"
#include "rectangle_tree/r_tree_split.hpp"
#include "rectangle_tree/r_star_tree_split.hpp"
#include "rectangle_tree/no_auxiliary_information.hpp"
#include "rectangle_tree/r_tree_descent_heuristic.hpp"
#include "rectangle_tree/r_star_tree_descent_heuristic.hpp"
#include "rectangle_tree/traits.hpp"
#include "rectangle_tree/x_tree_split.hpp"
#include "rectangle_tree/x_tree_auxiliary_information.hpp"
#include "rectangle_tree/hilbert_r_tree_descent_heuristic.hpp"
#include "rectangle_tree/hilbert_r_tree_split.hpp"
#include "rectangle_tree/hilbert_r_tree_auxiliary_information.hpp"
#include "rectangle_tree/discrete_hilbert_value.hpp"
#include "rectangle_tree/typedef.hpp"
#endif
@@ -0,0 +1,263 @@
/**
* @file discrete_hilbert_value.hpp
* @author Mikhail Lozhnikov
*
* Defintion of the DiscreteHilbertValue class, a class that calculates
* the ordering of points using the Hilbert curve.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_HPP
#include <mlpack/core.hpp>
namespace mlpack {
namespace tree /** Trees and tree-building procedures. */ {
/**
* The DiscreteHilbertValue class stores Hilbert values for all of the points in
* a RectangleTree node, and calculates Hilbert values for new points. This
* implementation calculates the full discrete Hilbert value; for a
* d-dimensional vector filled with elements of size E, each Hilbert value will
* take dE space.
*/
template<typename TreeElemType>
class DiscreteHilbertValue
{
public:
//! Depending on the precision of the tree element type, we may need to use
//! uint32_t or uint64_t.
typedef typename std::conditional<sizeof(TreeElemType) * CHAR_BIT <= 32,
uint32_t,
uint64_t>::type HilbertElemType;
//! Default constructor.
DiscreteHilbertValue();
/**
* Construct this for the node tree. If the node is the root this method
* computes the Hilbert value for each point in the tree's dataset.
*
* @param node The node that stores this Hilbert value.
*/
template<typename TreeType>
DiscreteHilbertValue(const TreeType* tree);
/**
* Create a Hilbert value object by copying from another one.
*
* @param other The Hilbert value object from which the value will be copied.
*/
DiscreteHilbertValue(const DiscreteHilbertValue& other);
//! Free memory
~DiscreteHilbertValue();
/**
* Compare two points. It returns 1 if the first point is greater than the
* second one, -1 if the first point is less than the second one and 0 if the
* Hilbert values of the points are equal. In order to do it this method
* computes the Hilbert values of the points.
*
* @param pt1 The first point.
* @param pt2 The second point.
*/
template<typename VecType1, typename VecType2>
static int ComparePoints(const VecType1& pt1, const VecType2& pt2,
typename boost::enable_if<IsVector<VecType1>>* = 0,
typename boost::enable_if<IsVector<VecType2>>* = 0);
/**
* Compare two Hilbert values. It returns 1 if the first value is greater than
* the second one, -1 if the first value is less than the second one and 0 if
* the values are equal. This method does not compute the Hilbert values.
*
* @param val1 The first point.
* @param val2 The second point.
*/
static int CompareValues(const DiscreteHilbertValue& val1,
const DiscreteHilbertValue& val2);
/**
* Compare the largest Hilbert value of the node with the val value. It
* returns 1 if the value of the node is greater than val, -1 if the value of
* the node is less than val and 0 if the values are equal. This method does
* not compute the Hilbert values.
*
* @param val The Hilbert value to compare with.
*/
int CompareWith(const DiscreteHilbertValue& val) const;
/**
* Compare the largest Hilbert value of the node with the Hilbert value of the
* point. It returns 1 if the value of the node is greater than the value of
* the point, -1 if the value of the node is less than the value of the point
* and 0 if the values are equal. This method computes the Hilbert value of
* the point.
*
* @param pt The point to compare with.
*/
template<typename VecType>
int CompareWith(const VecType& pt,
typename boost::enable_if<IsVector<VecType>>* = 0) const;
/**
* Compare the Hilbert value of the cached point with the Hilbert value of the
* given point. It returns 1 if the value of the node is greater than the
* value of the point, -1 if the value of the node is less than the value of
* the point and 0 if the values are equal. This method computes the Hilbert
* value of the point.
*
* @param pt The point to compare with.
*/
template<typename VecType>
int CompareWithCachedPoint(
const VecType& pt,
typename boost::enable_if<IsVector<VecType>>* = 0) const;
/**
* Update the largest Hilbert value of the node and insert the point in the
* local dataset if the node is a leaf.
*
* @param node The node in which the point is being inserted.
* @param point The number of the point being inserted.
*/
template<typename TreeType, typename VecType>
size_t InsertPoint(TreeType *node,
const VecType& pt,
typename boost::enable_if<IsVector<VecType>>* = 0);
/**
* Update the largest Hilbert value of the node.
*
* @param node The node being inserted.
*/
template<typename TreeType>
void InsertNode(TreeType* node);
/**
* Update the largest Hilbert value of the node and delete the point from the
* local dataset.
*
* @param node The node from which the point is being deleted.
* @param localIndex The index of the point in the local dataset.
*/
template<typename TreeType>
void DeletePoint(TreeType* node, const size_t localIndex);
/**
* Update the largest Hilbert value of the node.
*
* @param node The node from which another node is being deleted.
* @param nodeIndex The index of the node being deleted.
*/
template<typename TreeType>
void RemoveNode(TreeType* node, const size_t nodeIndex);
/**
* Copy the local Hilbert value's pointer.
*
* @param val The DiscreteHilbertValue object from which the dataset
* will be copied.
*/
DiscreteHilbertValue& operator=(const DiscreteHilbertValue& val);
/**
* Nullify the localHilbertValues pointer in order to prevent an invalid free.
*/
void NullifyData();
/**
* Update the largest Hilbert value and the local Hilbert values of an
* intermediate node. The children of the node (or the points that the node
* contains) should be arranged according to their Hilbert values.
*
* @param node The node in which the information should be updated.
*/
template<typename TreeType>
void UpdateLargestValue(TreeType* node);
/**
* This method updates the largest Hilbert value of a leaf node and
* redistributes the Hilbert values of points according to their new position
* after the split algorithm.
*
* @param parent The parent of the node that was split.
* @param firstSibling The first cooperating sibling.
* @param lastSibling The last cooperating sibling.
*/
template<typename TreeType>
void RedistributeHilbertValues(TreeType* parent,
const size_t firstSibling,
const size_t lastSibling);
/**
* Calculate the Hilbert value of the point pt.
*
* @param pt The point for which the Hilbert value should be calculated.
*/
template<typename VecType>
static arma::Col<HilbertElemType> CalculateValue(
const VecType& pt,
typename boost::enable_if<IsVector<VecType>>* = 0);
/**
* Compare two Hilbert values. It returns 1 if the first value is greater than
* the second one, -1 if the first value is less than the second one and 0 if
* the values are equal. This method does not compute the Hilbert values.
*
* @param value1 The first value.
* @param value2 The second value.
*/
static int CompareValues(const arma::Col<HilbertElemType>& value1,
const arma::Col<HilbertElemType>& value2);
//! Return the number of values.
size_t NumValues() const { return numValues; }
//! Modify the number of values.
size_t& NumValues() { return numValues; }
//! Return the Hilbert values.
const arma::Mat<HilbertElemType>* LocalHilbertValues() const
{ return localHilbertValues; }
//! Modify the Hilbert values.
arma::Mat<HilbertElemType>*& LocalHilbertValues()
{ return localHilbertValues; }
//! Return the cached point (valueToInsert).
const arma::Col<HilbertElemType>* ValueToInsert() const
{ return valueToInsert; }
//! Modify the cached point (valueToInsert).
arma::Col<HilbertElemType>* ValueToInsert() { return valueToInsert; }
private:
//! The number of bits that we can store.
static constexpr size_t order = sizeof(HilbertElemType) * CHAR_BIT;
//! The local Hilbert values.
arma::Mat<HilbertElemType>* localHilbertValues;
//! Indicates that the node owns the localHilbertValues variable.
bool ownsLocalHilbertValues;
//! The number of values in the localHilbertValues dataset.
size_t numValues;
/** The Hilbert value of the point that is being inserted.
* The pointer is the same in all nodes. The value is updated in InsertPoint()
* if it is invoked at the root level. This variable helps to avoid
* multiple computation of the Hilbert value of a point in the insertion
* process.
*/
arma::Col<HilbertElemType>* valueToInsert;
//! Indicates that the node owns the valueToInsert.
bool ownsValueToInsert;
public:
template<typename Archive>
void Serialize(Archive& ar, const unsigned int /* version */);
};
} // namespace tree
} // namespace mlpack
// Include implementation.
#include "discrete_hilbert_value_impl.hpp"
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_HPP
@@ -0,0 +1,457 @@
/**
* @file discrete_hilbert_value.hpp
* @author Mikhail Lozhnikov
*
* Defintion of the DiscreteHilbertValue class, a class that calculates
* the ordering of points using the Hilbert curve.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_IMPL_HPP
#include "discrete_hilbert_value.hpp"
namespace mlpack {
namespace tree /** Trees and tree-building procedures. */ {
template<typename TreeElemType>
DiscreteHilbertValue<TreeElemType>::DiscreteHilbertValue() :
localHilbertValues(NULL),
ownsLocalHilbertValues(false),
numValues(0),
valueToInsert(NULL),
ownsValueToInsert(false)
{ }
template<typename TreeElemType>
DiscreteHilbertValue<TreeElemType>::~DiscreteHilbertValue()
{
if (ownsLocalHilbertValues)
delete localHilbertValues;
if (ownsValueToInsert)
delete valueToInsert;
}
template<typename TreeElemType>
template<typename TreeType>
DiscreteHilbertValue<TreeElemType>::DiscreteHilbertValue(const TreeType* tree) :
localHilbertValues(NULL),
ownsLocalHilbertValues(false),
numValues(0),
valueToInsert(tree->Parent() ?
tree->Parent()->AuxiliaryInfo().HilbertValue().ValueToInsert() :
new arma::Col<HilbertElemType>(tree->Dataset().n_rows)),
ownsValueToInsert(tree->Parent() ? false : true)
{
// Calculate the Hilbert value for all points.
if (!tree->Parent()) // This is the root node.
ownsLocalHilbertValues = true;
else if (tree->Parent()->Child(0).IsLeaf())
{
// This is a leaf node.
assert(tree->Parent()->NumChildren() > 0);
ownsLocalHilbertValues = true;
}
if (ownsLocalHilbertValues)
{
localHilbertValues = new arma::Mat<HilbertElemType>(tree->Dataset().n_rows,
tree->MaxLeafSize() + 1);
}
}
template<typename TreeElemType>
DiscreteHilbertValue<TreeElemType>::
DiscreteHilbertValue(const DiscreteHilbertValue& other) :
localHilbertValues(
const_cast<arma::Mat<HilbertElemType>*>(other.LocalHilbertValues())),
ownsLocalHilbertValues(other.ownsLocalHilbertValues),
numValues(other.NumValues()),
valueToInsert(
const_cast<arma::Col<HilbertElemType>*>(other.ValueToInsert())),
ownsValueToInsert(false)
{ }
template<typename TreeElemType>
template<typename VecType>
arma::Col<typename DiscreteHilbertValue<TreeElemType>::HilbertElemType>
DiscreteHilbertValue<TreeElemType>::
CalculateValue(const VecType& pt,typename boost::enable_if<IsVector<VecType>>*)
{
typedef typename VecType::elem_type VecElemType;
arma::Col<HilbertElemType> res(pt.n_rows);
// Calculate the number of bits for the exponent.
const int numExpBits = std::ceil(std::log2(
std::numeric_limits<VecElemType>::max_exponent -
std::numeric_limits<VecElemType>::min_exponent + 1.0));
// Calculate the number of bits for the mantissa.
const int numMantBits = order - numExpBits - 1;
for (size_t i = 0; i < pt.n_rows; i++)
{
int e;
VecElemType normalizedVal = std::frexp(pt(i),&e);
bool sgn = std::signbit(normalizedVal);
if (pt(i) == 0)
e = std::numeric_limits<VecElemType>::min_exponent;
if (sgn)
normalizedVal = -normalizedVal;
if (e < std::numeric_limits<VecElemType>::min_exponent)
{
HilbertElemType tmp = (HilbertElemType) 1 <<
(std::numeric_limits<VecElemType>::min_exponent - e);
e = std::numeric_limits<VecElemType>::min_exponent;
normalizedVal /= tmp;
}
// Extract the mantissa.
HilbertElemType tmp = (HilbertElemType) 1 << numMantBits;
res(i) = std::floor(normalizedVal * tmp);
// Add the exponent.
assert(res(i) < ((HilbertElemType) 1 << numMantBits));
res(i) |= ((HilbertElemType)
(e - std::numeric_limits<VecElemType>::min_exponent)) << numMantBits;
assert(res(i) < ((HilbertElemType) 1 << (order - 1)) - 1);
// Negative values should be inverted.
if (sgn)
{
res(i) = ((HilbertElemType) 1 << (order - 1)) - 1 - res(i);
assert((res(i) >> (order - 1)) == 0);
}
else
{
res(i) |= (HilbertElemType) 1 << (order - 1);
assert((res(i) >> (order - 1)) == 1);
}
}
HilbertElemType M = (HilbertElemType) 1 << (order - 1);
// Since the Hilbert curve is continuous we should permutate and intend
// coordinate axes depending on the position of the point.
for (HilbertElemType Q = M; Q > 1; Q >>= 1)
{
HilbertElemType P = Q - 1;
for (size_t i = 0; i < pt.n_rows; i++)
{
if (res(i) & Q) // Invert.
res(0) ^= P;
else // Permutate.
{
HilbertElemType t = (res(0) ^ res(i)) & P;
res(0) ^= t;
res(i) ^= t;
}
}
}
// Gray encode.
for (size_t i = 1; i < pt.n_rows; i++)
res(i) ^= res(i - 1);
HilbertElemType t = 0;
// Some coordinate axes should be inverted.
for (HilbertElemType Q = M; Q > 1; Q >>= 1)
if (res(pt.n_rows - 1) & Q)
t ^= Q - 1;
for (size_t i = 0; i < pt.n_rows; i++)
res(i) ^= t;
// We should rearrange bits in order to compare two Hilbert values faster.
arma::Col<HilbertElemType> rearrangedResult(pt.n_rows, arma::fill::zeros);
for (size_t i = 0; i < order; i++)
for (size_t j = 0; j < pt.n_rows; j++)
{
size_t bit = (i * pt.n_rows + j) % order;
size_t row = (i * pt.n_rows + j) / order;
rearrangedResult(row) |= (((res(j) >> (order - 1 - i)) & 1) <<
(order - 1 - bit));
}
return rearrangedResult;
}
template<typename TreeElemType>
int DiscreteHilbertValue<TreeElemType>::
CompareValues(const arma::Col<HilbertElemType>& value1,
const arma::Col<HilbertElemType>& value2)
{
for (size_t i = 0; i < value1.n_rows; i++)
{
if (value1(i) > value2(i))
return 1;
else if (value1(i) < value2(i))
return -1;
}
return 0;
}
template<typename TreeElemType>
template<typename VecType1, typename VecType2>
int DiscreteHilbertValue<TreeElemType>::
ComparePoints(const VecType1& pt1,
const VecType2& pt2,
typename boost::enable_if<IsVector<VecType1>>*,
typename boost::enable_if<IsVector<VecType2>>*)
{
arma::Col<HilbertElemType> val1 = CalculateValue(pt1);
arma::Col<HilbertElemType> val2 = CalculateValue(pt2);
return CompareValues(val1, val2);
}
template<typename TreeElemType>
int DiscreteHilbertValue<TreeElemType>::
CompareValues(const DiscreteHilbertValue& val1,
const DiscreteHilbertValue& val2)
{
if (val1.NumValues() > 0 && val2.NumValues() == 0)
return 1;
else if (val1.NumValues() == 0 && val2.NumValues() > 0)
return -1;
else if (val1.NumValues() == 0 && val2.NumValues() == 0)
return 0;
return CompareValues(val1.LocalHilbertValues()->col(val1.NumValues() - 1),
val2.LocalHilbertValues()->col(val2.NumValues() - 1));
}
template<typename TreeElemType>
int DiscreteHilbertValue<TreeElemType>::
CompareWith(const DiscreteHilbertValue& val) const
{
return CompareValues(*this, val);
}
template<typename TreeElemType>
template<typename VecType>
int DiscreteHilbertValue<TreeElemType>::
CompareWith(const VecType& pt,
typename boost::enable_if<IsVector<VecType>>*) const
{
arma::Col<HilbertElemType> val = CalculateValue(pt);
if (numValues == 0)
return -1;
return CompareValues(localHilbertValues->col(numValues - 1),val);
}
template<typename TreeElemType>
template<typename VecType>
int DiscreteHilbertValue<TreeElemType>::
CompareWithCachedPoint(const VecType& ,
typename boost::enable_if<IsVector<VecType>>*) const
{
if (numValues == 0)
return -1;
return CompareValues(localHilbertValues->col(numValues - 1), *valueToInsert);
}
template<typename TreeElemType>
template<typename TreeType, typename VecType>
size_t DiscreteHilbertValue<TreeElemType>::
InsertPoint(TreeType *node,
const VecType& pt,
typename boost::enable_if<IsVector<VecType>>*)
{
size_t i = 0;
// All points are inserted to the root node.
if (!node->Parent())
*valueToInsert = CalculateValue(pt);
if (node->IsLeaf())
{
// Find an appropriate place.
for (i = 0; i < numValues; i++)
if (CompareValues(localHilbertValues->col(i), *valueToInsert) > 0)
break;
for (size_t j = numValues; j > i; j--)
localHilbertValues->col(j) = localHilbertValues->col(j-1);
localHilbertValues->col(i) = *valueToInsert;
numValues++;
// Propagate changes of the largest Hilbert value downward.
TreeType* root = node->Parent();
while (root != NULL)
{
root->AuxiliaryInfo().HilbertValue().UpdateLargestValue(root);
root = root->Parent();
}
}
return i;
}
template<typename TreeElemType>
template<typename TreeType>
void DiscreteHilbertValue<TreeElemType>::InsertNode(TreeType* node)
{
DiscreteHilbertValue &val = node->AuxiliaryInfo().HilbertValue();
if (CompareWith(node,val) < 0)
{
localHilbertValues = val.LocalHilbertValues();
numValues = val.NumValues();
}
}
template<typename TreeElemType>
template<typename TreeType>
void DiscreteHilbertValue<TreeElemType>::
DeletePoint(TreeType* node, const size_t localIndex)
{
// Delete the Hilbert value from the local dataset
for (size_t i = numValues - 1; i > localIndex; i--)
localHilbertValues->col(i - 1) = localHilbertValues->col(i);
numValues--;
}
template<typename TreeElemType>
template<typename TreeType>
void DiscreteHilbertValue<TreeElemType>::
RemoveNode(TreeType* node, const size_t nodeIndex)
{
if (node->NumChildren() <= 1)
{
localHilbertValues = NULL;
numValues = 0;
return;
}
if (nodeIndex + 1 == node->NumChildren())
{
// Update the largest Hilbert value if the value exists
TreeType& child = node->Child(nodeIndex - 1);
if (child.AuxiliaryInfo.HilbertValue().NumValues() != 0)
{
numValues = child.AuxiliaryInfo.HilbertValue().NumValues();
localHilbertValues =
child.AuxiliaryInfo.HilbertValue().LocalHilbertValues();
}
else
{
localHilbertValues = NULL;
numValues = 0;
}
}
}
template<typename TreeElemType>
DiscreteHilbertValue<TreeElemType>& DiscreteHilbertValue<TreeElemType>::
operator=(const DiscreteHilbertValue& val)
{
localHilbertValues = const_cast<arma::Mat<HilbertElemType>* >
(val.LocalHilbertValues());
ownsLocalHilbertValues = false;
numValues = val.NumValues();
return *this;
}
template<typename TreeElemType>
void DiscreteHilbertValue<TreeElemType>::NullifyData()
{
ownsLocalHilbertValues = false;
}
template<typename TreeElemType>
template<typename TreeType>
void DiscreteHilbertValue<TreeElemType>::UpdateLargestValue(TreeType* node)
{
if (!node->IsLeaf())
{
// Update the largest Hilbert value
localHilbertValues = node->Child(node->NumChildren() -
1).AuxiliaryInfo().HilbertValue().LocalHilbertValues();
numValues = node->Child(node->NumChildren() -
1).AuxiliaryInfo().HilbertValue().NumValues();
}
}
template<typename TreeElemType>
template<typename TreeType>
void DiscreteHilbertValue<TreeElemType>::RedistributeHilbertValues(
TreeType* parent,
const size_t firstSibling,
const size_t lastSibling)
{
// We need to update the local dataset if points were redistributed.
size_t numPoints = 0;
for (size_t i = firstSibling; i <= lastSibling; i++)
numPoints += parent->Child(i).NumPoints();
// Copy the local Hilbert values.
arma::Mat<HilbertElemType> tmp(localHilbertValues->n_rows, numPoints);
size_t iPoint = 0;
for (size_t i = firstSibling; i<= lastSibling; i++)
{
DiscreteHilbertValue<TreeElemType> &value =
parent->Child(i).AuxiliaryInfo().HilbertValue();
for (size_t j = 0; j < value.NumValues(); j++)
{
tmp.col(iPoint) = value.LocalHilbertValues()->col(j);
iPoint++;
}
}
assert(iPoint == numPoints);
iPoint = 0;
// Redistribute the Hilbert values.
for (size_t i = firstSibling; i <= lastSibling; i++)
{
DiscreteHilbertValue<TreeElemType> &value =
parent->Child(i).AuxiliaryInfo().HilbertValue();
for (size_t j = 0; j < parent->Child(i).NumPoints(); j++)
{
value.LocalHilbertValues()->col(j) = tmp.col(iPoint);
iPoint++;
}
value.NumValues() = parent->Child(i).NumPoints();
}
assert(iPoint == numPoints);
}
template<typename TreeElemType>
template<typename Archive>
void DiscreteHilbertValue<TreeElemType>::
Serialize(Archive& ar, const unsigned int /* version */)
{
using data::CreateNVP;
ar & CreateNVP(localHilbertValues, "localHilbertValues");
ar & CreateNVP(ownsLocalHilbertValues, "ownsLocalHilbertValues");
ar & CreateNVP(numValues, "numValues");
ar & CreateNVP(valueToInsert, "valueToInsert");
ar & CreateNVP(ownsValueToInsert, "ownsValueToInsert");
}
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_DISCRETE_HILBERT_VALUE_IMPL_HPP
@@ -19,11 +19,12 @@ namespace tree {
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
template<typename RuleType>
class RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::DualTreeTraverser
DescentType, AuxiliaryInformationType>::DualTreeTraverser
{
public:
/**
@@ -20,10 +20,12 @@ namespace tree {
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
template<typename RuleType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
DualTreeTraverser<RuleType>::DualTreeTraverser(RuleType& rule) :
rule(rule),
numPrunes(0),
@@ -35,10 +37,12 @@ DualTreeTraverser<RuleType>::DualTreeTraverser(RuleType& rule) :
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
template<typename RuleType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
DualTreeTraverser<RuleType>::Traverse(RectangleTree& queryNode,
RectangleTree& referenceNode)
{
@@ -63,14 +67,14 @@ DualTreeTraverser<RuleType>::Traverse(RectangleTree& queryNode,
{
// Restore the traversal information.
rule.TraversalInfo() = traversalInfo;
const double childScore = rule.Score(queryNode.Points()[query],
const double childScore = rule.Score(queryNode.Point(query),
referenceNode);
if (childScore == DBL_MAX)
continue; // We don't require a search in this reference node.
for(size_t ref = 0; ref < referenceNode.Count(); ++ref)
rule.BaseCase(queryNode.Points()[query], referenceNode.Points()[ref]);
rule.BaseCase(queryNode.Point(query), referenceNode.Point(ref));
numBaseCases += referenceNode.Count();
}
@@ -99,7 +103,7 @@ DualTreeTraverser<RuleType>::Traverse(RectangleTree& queryNode,
for (size_t i = 0; i < referenceNode.NumChildren(); i++)
{
rule.TraversalInfo() = traversalInfo;
nodesAndScores[i].node = referenceNode.Children()[i];
nodesAndScores[i].node = &(referenceNode.Child(i));
nodesAndScores[i].score = rule.Score(queryNode,
*(nodesAndScores[i].node));
nodesAndScores[i].travInfo = rule.TraversalInfo();
@@ -134,7 +138,7 @@ DualTreeTraverser<RuleType>::Traverse(RectangleTree& queryNode,
for (size_t i = 0; i < referenceNode.NumChildren(); i++)
{
rule.TraversalInfo() = traversalInfo;
nodesAndScores[i].node = referenceNode.Children()[i];
nodesAndScores[i].node = &(referenceNode.Child(i));
nodesAndScores[i].score = rule.Score(queryNode.Child(j),
*nodesAndScores[i].node);
nodesAndScores[i].travInfo = rule.TraversalInfo();
@@ -0,0 +1,120 @@
/**
* @file hilbert_r_tree_auxiliary_information.hpp
* @author Mikhail Lozhnikov
*
* Definition of the HilbertRTreeAuxiliaryInformation class,
* a class that provides some Hilbert r-tree specific information
* about the nodes.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_HPP
namespace mlpack {
namespace tree {
template<typename TreeType,
template<typename> class HilbertValueType>
class HilbertRTreeAuxiliaryInformation
{
public:
//! The element type held by the tree.
typedef typename TreeType::ElemType ElemType;
//! Default constructor
HilbertRTreeAuxiliaryInformation();
/**
* Construct this as an auxiliary information for the given node.
*
* @param node The node that stores this auxiliary information.
*/
HilbertRTreeAuxiliaryInformation(const TreeType* node);
/**
* Create an auxiliary information object by copying from the other node.
*
* @param other The node from which the information will be copied.
*/
HilbertRTreeAuxiliaryInformation(
const HilbertRTreeAuxiliaryInformation& other);
/**
* The Hilbert R tree requires to insert points according to their Hilbert
* value. This method should take care of it. It returns false if it does
* nothing and true if it handles the insertion process.
*
* @param node The node in which the point is being inserted.
* @param point The number of the point being inserted.
*/
bool HandlePointInsertion(TreeType* node, const size_t point);
/**
* The Hilbert R tree requires to insert nodes according to their Hilbert
* value. This method should take care of it. It returns false if it does
* nothing and true if it handles the insertion process.
*
* @param node The node in which the nodeToInsert is being inserted.
* @param nodeToInsert The node being inserted.
* @param insertionLevel The level of the tree at which the nodeToInsert
* should be inserted.
*/
bool HandleNodeInsertion(TreeType* node,
TreeType* nodeToInsert,
bool insertionLevel);
/**
* The Hilbert R tree requires all points to be arranged according to their
* Hilbert value. This method should take care of saving this property after
* the deletion process. It returns false if it does nothing and true if it
* handles the deletion process.
*
* @param node The node from which the point is being deleted.
* @param localIndex The index of the point being deleted.
*/
bool HandlePointDeletion(TreeType* node, const size_t localIndex);
/**
* The Hilbert R tree requires all nodes to be arranged according to their
* Hilbert value. This method should take care of saving this property after
* the deletion process. It returns false if it does nothing and true if it
* handles the deletion process.
*
* @param node The node from which the node is being deleted.
* @param nodeIndex The index of the node being deleted.
*/
bool HandleNodeRemoval(TreeType* node, const size_t nodeIndex);
/**
* Update the auxiliary information in the node. The method returns true if
* the update should be propogated downward.
*
* @param node The node in which the auxiliary information being update.
*/
bool UpdateAuxiliaryInfo(TreeType* node);
//! Clear memory.
void NullifyData();
private:
//! The largest Hilbert value of a point enclosed by the node.
HilbertValueType<ElemType> hilbertValue;
public:
//! Return the largest Hilbert value of a point covered by the node.
const HilbertValueType<ElemType>& HilbertValue() const
{ return hilbertValue; }
//! Modify the largest Hilbert value of a point covered by the node.
HilbertValueType<ElemType>& HilbertValue() { return hilbertValue; }
/**
* Serialize the information.
*/
template<typename Archive>
void Serialize(Archive& ar, const unsigned int /* version */);
};
} // namespace tree
} // namespace mlpack
#include "hilbert_r_tree_auxiliary_information_impl.hpp"
#endif//MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_HPP
@@ -0,0 +1,169 @@
/**
* @file hilbert_r_tree_auxiliary_information.hpp
* @author Mikhail Lozhnikov
*
* Implementation of the HilbertRTreeAuxiliaryInformation class, a class that
* provides some Hilbert r-tree specific information about the nodes.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_IMPL_HPP
#include "hilbert_r_tree_auxiliary_information.hpp"
namespace mlpack {
namespace tree {
template<typename TreeType,
template<typename> class HilbertValueType>
HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
HilbertRTreeAuxiliaryInformation()
{ }
template<typename TreeType,
template<typename> class HilbertValueType>
HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
HilbertRTreeAuxiliaryInformation(const TreeType* node) :
hilbertValue(node)
{ }
template<typename TreeType,
template<typename> class HilbertValueType>
HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
HilbertRTreeAuxiliaryInformation(
const HilbertRTreeAuxiliaryInformation& other) :
hilbertValue(other.HilbertValue())
{ }
template<typename TreeType,
template<typename> class HilbertValueType>
bool HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
HandlePointInsertion(TreeType* node, const size_t point)
{
if (node->IsLeaf())
{
// Get the position at which the point should be inserted, and then update
// the largest Hilbert value of the node.
size_t pos = hilbertValue.InsertPoint(node, node->Dataset().col(point));
// Move points.
for (size_t i = node->NumPoints(); i > pos; i--)
node->Point(i) = node->Point(i - 1);
// Insert the point.
node->Point(pos) = point;
node->Count()++;
}
else
{
// Calculate the Hilbert value.
hilbertValue.InsertPoint(node, node->Dataset().col(point));
}
return true;
}
template<typename TreeType,
template<typename> class HilbertValueType>
bool HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
HandleNodeInsertion(TreeType* node, TreeType* nodeToInsert, bool insertionLevel)
{
if (insertionLevel)
{
size_t pos;
// Find the best position for the node being inserted.
// The node should be inserted according to its Hilbert value.
for (pos = 0; pos < node->NumChildren(); pos++)
if (HilbertValueType<ElemType>::CompareValues(
node->Child(pos).AuxiliaryInfo().HilbertValue(),
nodeToInsert->AuxiliaryInfo().HilbertValue()) < 0)
break;
// Move nodes.
for (size_t i = node->NumChildren(); i > pos; i--)
node->children[i] = node->children[i - 1];
// Insert the node.
node->children[pos] = nodeToInsert;
nodeToInsert->Parent() = node;
// Update the largest Hilbert value.
hilbertValue.InsertNode(nodeToInsert);
}
else
hilbertValue.InsertNode(nodeToInsert); // Update the largest Hilbert value.
return true;
}
template<typename TreeType,
template<typename> class HilbertValueType>
bool HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
HandlePointDeletion(TreeType* node, const size_t localIndex)
{
// Update the largest Hilbert value.
hilbertValue.DeletePoint(node,localIndex);
for (size_t i = localIndex + 1; localIndex < node->NumPoints(); i++)
node->Point(i - 1) = node->Point(i);
node->NumPoints()--;
return true;
}
template<typename TreeType,
template<typename> class HilbertValueType>
bool HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
HandleNodeRemoval(TreeType* node, const size_t nodeIndex)
{
// Update the largest Hilbert value.
hilbertValue.RemoveNode(node,nodeIndex);
for (size_t i = nodeIndex + 1; nodeIndex < node->NumChildren(); i++)
node->children[i - 1] = node->children[i];
node->NumChildren()--;
return true;
}
template<typename TreeType,
template<typename> class HilbertValueType>
bool HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
UpdateAuxiliaryInfo(TreeType* node)
{
if (node->IsLeaf()) // Should already be updated
return true;
TreeType& child = node->Child(node->NumChildren() - 1);
if (hilbertValue.CompareWith(child.AuxiliaryInfo().HilbertValue()) < 0)
{
hilbertValue = child.AuxiliaryInfo().HilbertValue();
return true;
}
return false;
}
template<typename TreeType,
template<typename> class HilbertValueType>
void HilbertRTreeAuxiliaryInformation<TreeType, HilbertValueType>::
NullifyData()
{
hilbertValue.NullifyData();
}
template<typename TreeType,
template<typename> class HilbertValueType>
template<typename Archive>
void HilbertRTreeAuxiliaryInformation<TreeType ,HilbertValueType>::
Serialize(Archive& ar, const unsigned int /* version */)
{
using data::CreateNVP;
ar & CreateNVP(hilbertValue, "hilbertValue");
}
} // namespace tree
} // namespace mlpack
#endif//MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_AUXILIARY_INFORMATION_IMPL_HPP
@@ -0,0 +1,53 @@
/**
* @file hilbert_r_tree_descent_heuristic.hpp
* @author Mikhail Lozhnikov
*
* Definition of HilbertRTreeDescentHeuristic, a class that chooses the best
* child of a node in an R tree when inserting a new point.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP
#include <mlpack/core.hpp>
namespace mlpack {
namespace tree {
/**
* This class chooses the best child of a node in a Hilbert R tree when
* inserting a new point. This is done, in this class, by using the Hilbert
* value of the point to be inserted.
*/
class HilbertRTreeDescentHeuristic
{
public:
/**
* Evaluate the node using a heuristic. Returns the number of the node with
* minimum largest Hilbert value that is greater than the Hilbert value of the
* point being inserted.
*
* @param node The node that is being evaluated.
* @param point The number of the point that is being inserted.
*/
template<typename TreeType>
static size_t ChooseDescentNode(const TreeType* node, const size_t point);
/**
* Evaluate the node using a heuristic. Returns the number of the node with
* minimum largest Hilbert value that is greater than the largest Hilbert
* value of the point being inserted.
*
* @param node The node that is being evaluated.
* @param insertedNode The node that is being inserted.
*/
template<typename TreeType>
static size_t ChooseDescentNode(const TreeType* node,
const TreeType* insertedNode);
};
} // namespace tree
} // namespace mlpack
#include "hilbert_r_tree_descent_heuristic_impl.hpp"
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_HPP
@@ -0,0 +1,49 @@
/**
* @file hilbert_r_tree_descent_heuristic_impl.hpp
* @author Mikhail Lozhnikov
*
* Implementation of HilbertRTreeDescentHeuristic, a class that chooses the best
* child of a node in an R tree when inserting a new point.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP
#include "hilbert_r_tree_descent_heuristic.hpp"
namespace mlpack {
namespace tree {
template<typename TreeType>
size_t HilbertRTreeDescentHeuristic::ChooseDescentNode(
const TreeType* node,
const size_t point)
{
size_t bestIndex = 0;
for (bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++)
if (node->Child(bestIndex).AuxiliaryInfo().HilbertValue().
CompareWithCachedPoint(node->Dataset().col(point)) > 0)
break;
return bestIndex;
}
template<typename TreeType>
size_t HilbertRTreeDescentHeuristic::ChooseDescentNode(
const TreeType* node,
const TreeType* insertedNode)
{
size_t bestIndex = 0;
for (bestIndex = 0; bestIndex < node->NumChildren() - 1; bestIndex++)
if (node->Child(bestIndex).AuxiliaryInfo().HilbertValue().
CompareWith(node, node->AuxiliaryInfo().HilbertValue()) > 0)
break;
return bestIndex;
}
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_DESCENT_HEURISTIC_IMPL_HPP
@@ -1 +1,95 @@
/**
* @file hilbert_r_tree_split.hpp
* @author Mikhail Lozhnikov
*
* Defintion of the HilbertRTreeSplit class, a class that splits the nodes of an R
* tree, starting at a leaf node and moving upwards if necessary.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_HPP
#include <mlpack/core.hpp>
namespace mlpack {
namespace tree /** Trees and tree-building procedures. */ {
/**
* The splitting procedure for the Hilbert R tree. The template parameter
* splitOrder is the order of the splitting policy. The Hilbert R tree splits a
* node on overflow, turning splitOrder nodes into (splitOrder + 1) nodes.
*
* @tparam splitOrder Number of nodes to split.
*/
template<size_t splitOrder = 2>
class HilbertRTreeSplit
{
public:
/**
* 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:
/**
* Try to find splitOrder cooperating siblings in order to redistribute their
* children evenly. Returns true on success.
*
* @param parent The parent of of the overflowing node.
* @param iTree The number of the overflowing node.
* @param firstSibling The first cooperating sibling.
* @param lastSibling The last cooperating sibling.
*/
template<typename TreeType>
static bool FindCooperatingSiblings(TreeType* parent,
const size_t iTree,
size_t& firstSibling,
size_t& lastSibling);
/**
* Redistribute the children of the cooperating siblings evenly among them.
*
* @param parent The parent of of the overflowing node.
* @param firstSibling The first cooperating sibling.
* @param lastSibling The last cooperating sibling.
*/
template<typename TreeType>
static void RedistributeNodesEvenly(const TreeType* parent,
const size_t firstSibling,
const size_t lastSibling);
/**
* Redistribute the points of the cooperating siblings evenly among them.
*
* @param parent The parent of of the overflowing node.
* @param firstSibling The first cooperating sibling.
* @param lastSibling The last cooperating sibling.
*/
template<typename TreeType>
static void RedistributePointsEvenly(TreeType* parent,
const size_t firstSibling,
const size_t lastSibling);
};
} // namespace tree
} // namespace mlpack
// Include implementation.
#include "hilbert_r_tree_split_impl.hpp"
#endif
@@ -1 +1,341 @@
/**
* @file hilbert_r_tree_split_impl.hpp
* @author Mikhail Lozhnikov
*
* Implementation of class (HilbertRTreeSplit) to split a RectangleTree.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_IMPL_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_IMPL_HPP
#include "hilbert_r_tree_split.hpp"
#include "rectangle_tree.hpp"
#include <mlpack/core/math/range.hpp>
namespace mlpack {
namespace tree {
template<size_t splitOrder>
template<typename TreeType>
void HilbertRTreeSplit<splitOrder>::SplitLeafNode(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->Count() = 0;
tree->NullifyData();
// Because this was a leaf node, numChildren must be 0.
tree->children[(tree->NumChildren())++] = copy;
SplitLeafNode(copy, relevels);
return;
}
TreeType* parent = tree->Parent();
size_t iTree = 0;
for (iTree = 0; parent->children[iTree] != tree; iTree++);
// Try to find splitOrder cooperating siblings in order to redistribute points
// among them and avoid split.
size_t firstSibling, lastSibling;
if (FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling))
{
RedistributePointsEvenly(parent, firstSibling, lastSibling);
return;
}
// We can not find splitOrder cooperating siblings since they are all full.
// We introduce new one instead.
size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ?
iTree + splitOrder : parent->NumChildren());
for (size_t i = parent->NumChildren(); i > iNewSibling ; i--)
parent->children[i] = parent->children[i - 1];
parent->NumChildren()++;
parent->children[iNewSibling] = new TreeType(parent);
lastSibling = (iTree + splitOrder < parent->NumChildren() ?
iTree + splitOrder : parent->NumChildren() - 1);
firstSibling = (lastSibling > splitOrder ? lastSibling - splitOrder : 0);
assert(lastSibling - firstSibling <= splitOrder);
assert(firstSibling >= 0);
assert(lastSibling < parent->NumChildren());
// Redistribute the points among (splitOrder + 1) cooperating siblings evenly.
RedistributePointsEvenly(parent, firstSibling, lastSibling);
if (parent->NumChildren() == parent->MaxNumChildren() + 1)
SplitNonLeafNode(parent, relevels);
}
template<size_t splitOrder>
template<typename TreeType>
bool HilbertRTreeSplit<splitOrder>::
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;
SplitNonLeafNode(copy, relevels);
return true;
}
TreeType* parent = tree->Parent();
size_t iTree = 0;
for (iTree = 0; parent->children[iTree] != tree; iTree++);
// Try to find splitOrder cooperating siblings in order to redistribute
// children among them and avoid split.
size_t firstSibling, lastSibling;
if (FindCooperatingSiblings(parent, iTree, firstSibling, lastSibling))
{
RedistributeNodesEvenly(parent, firstSibling, lastSibling);
return false;
}
// We can not find splitOrder cooperating siblings since they are all full.
// We introduce new one instead.
size_t iNewSibling = (iTree + splitOrder < parent->NumChildren() ?
iTree + splitOrder : parent->NumChildren());
for (size_t i = parent->NumChildren(); i > iNewSibling ; i--)
parent->children[i] = parent->children[i - 1];
parent->NumChildren()++;
parent->children[iNewSibling] = new TreeType(parent);
lastSibling = (iTree + splitOrder < parent->NumChildren() ?
iTree + splitOrder : parent->NumChildren() - 1);
firstSibling = (lastSibling > splitOrder ?
lastSibling - splitOrder : 0);
assert(lastSibling - firstSibling <= splitOrder);
assert(firstSibling >= 0);
assert(lastSibling < parent->NumChildren());
// Redistribute children among (splitOrder + 1) cooperating siblings evenly.
RedistributeNodesEvenly(parent, firstSibling, lastSibling);
if (parent->NumChildren() == parent->MaxNumChildren() + 1)
SplitNonLeafNode(parent, relevels);
return false;
}
template<size_t splitOrder>
template<typename TreeType>
bool HilbertRTreeSplit<splitOrder>::FindCooperatingSiblings(
TreeType* parent,
const size_t iTree,
size_t& firstSibling,
size_t& lastSibling)
{
size_t start = (iTree > splitOrder - 1 ? iTree - splitOrder + 1 : 0);
size_t end = (iTree + splitOrder <= parent->NumChildren() ?
iTree + splitOrder : parent->NumChildren());
size_t iUnderfullSibling;
// Try to find empty space among cooperating siblings.
if (parent->Child(iTree).NumChildren() != 0)
{
for (iUnderfullSibling = start; iUnderfullSibling < end;
iUnderfullSibling++)
if (parent->Child(iUnderfullSibling).NumChildren() <
parent->Child(iUnderfullSibling).MaxNumChildren() - 1)
break;
}
else
{
for (iUnderfullSibling = start; iUnderfullSibling < end;
iUnderfullSibling++)
if (parent->Child(iUnderfullSibling).NumPoints() <
parent->Child(iUnderfullSibling).MaxLeafSize() - 1)
break;
}
if (iUnderfullSibling == end) // All nodes are full.
return false;
if (iUnderfullSibling > iTree)
{
lastSibling = (iTree + splitOrder - 1 < parent->NumChildren() ?
iTree + splitOrder - 1 : parent->NumChildren() - 1);
firstSibling = (lastSibling > splitOrder - 1 ?
lastSibling - splitOrder + 1 : 0);
}
else
{
lastSibling = (iUnderfullSibling + splitOrder - 1 < parent->NumChildren() ?
iUnderfullSibling + splitOrder - 1 : parent->NumChildren() - 1);
firstSibling = (lastSibling > splitOrder - 1 ?
lastSibling - splitOrder + 1 : 0);
}
assert(lastSibling - firstSibling <= splitOrder - 1);
assert(firstSibling >= 0);
assert(lastSibling < parent->NumChildren());
return true;
}
template<size_t splitOrder>
template<typename TreeType>
void HilbertRTreeSplit<splitOrder>::
RedistributeNodesEvenly(const TreeType *parent,
size_t firstSibling, size_t lastSibling)
{
size_t numChildren = 0;
size_t numChildrenPerNode, numRestChildren;
for (size_t i = firstSibling; i <= lastSibling; i++)
numChildren += parent->Child(i).NumChildren();
numChildrenPerNode = numChildren / (lastSibling - firstSibling + 1);
numRestChildren = numChildren % (lastSibling - firstSibling + 1);
std::vector<TreeType*> children(numChildren);
// Copy children's children in order to redistribute them.
size_t iChild = 0;
for (size_t i = firstSibling; i <= lastSibling; i++)
{
for (size_t j = 0; j < parent->Child(i).NumChildren(); j++)
{
children[iChild] = parent->Child(i).children[j];
iChild++;
}
}
iChild = 0;
for (size_t i = firstSibling; i <= lastSibling; i++)
{
// Since we redistribute children of a sibling we should recalculate the
// bound.
parent->Child(i).Bound().Clear();
parent->Child(i).numDescendants = 0;
for (size_t j = 0; j < numChildrenPerNode; j++)
{
parent->Child(i).Bound() |= children[iChild]->Bound();
parent->Child(i).numDescendants += children[iChild]->numDescendants;
parent->Child(i).children[j] = children[iChild];
children[iChild]->Parent() = parent->children[i];
iChild++;
}
if (numRestChildren > 0)
{
parent->Child(i).Bound() |= children[iChild]->Bound();
parent->Child(i).numDescendants += children[iChild]->numDescendants;
parent->Child(i).children[numChildrenPerNode] = children[iChild];
children[iChild]->Parent() = parent->children[i];
parent->Child(i).NumChildren() = numChildrenPerNode + 1;
numRestChildren--;
iChild++;
}
else
{
parent->Child(i).NumChildren() = numChildrenPerNode;
}
assert(parent->Child(i).NumChildren() <=
parent->Child(i).MaxNumChildren());
// Fix the largest Hilbert value of the sibling.
parent->Child(i).AuxiliaryInfo().HilbertValue().UpdateLargestValue(
parent->children[i]);
}
}
template<size_t splitOrder>
template<typename TreeType>
void HilbertRTreeSplit<splitOrder>::
RedistributePointsEvenly(TreeType* parent,
const size_t firstSibling,
const size_t lastSibling)
{
size_t numPoints = 0;
size_t numPointsPerNode, numRestPoints;
for (size_t i = firstSibling; i <= lastSibling; i++)
numPoints += parent->Child(i).NumPoints();
numPointsPerNode = numPoints / (lastSibling - firstSibling + 1);
numRestPoints = numPoints % (lastSibling - firstSibling + 1);
std::vector<size_t> points(numPoints);
// Copy children's points in order to redistribute them.
size_t iPoint = 0;
for (size_t i = firstSibling; i <= lastSibling; i++)
{
for (size_t j = 0; j < parent->Child(i).NumPoints(); j++)
points[iPoint++] = parent->Child(i).Point(j);
}
iPoint = 0;
for (size_t i = firstSibling; i <= lastSibling; i++)
{
// Since we redistribute points of a sibling we should recalculate the
// bound.
parent->Child(i).Bound().Clear();
size_t j;
for (j = 0; j < numPointsPerNode; j++)
{
parent->Child(i).Bound() |= parent->Dataset().col(points[iPoint]);
parent->Child(i).Point(j) = points[iPoint];
iPoint++;
}
if (numRestPoints > 0)
{
parent->Child(i).Bound() |= parent->Dataset().col(points[iPoint]);
parent->Child(i).Point(j) = points[iPoint];
parent->Child(i).Count() = numPointsPerNode + 1;
numRestPoints--;
iPoint++;
}
else
{
parent->Child(i).Count() = numPointsPerNode;
}
parent->Child(i).numDescendants = parent->Child(i).Count();
assert(parent->Child(i).NumPoints() <=
parent->Child(i).MaxLeafSize());
}
// Fix the largest Hilbert values of the siblings.
parent->AuxiliaryInfo().HilbertValue().RedistributeHilbertValues(parent,
firstSibling, lastSibling);
TreeType* root = parent;
while (root != NULL)
{
root->AuxiliaryInfo().HilbertValue().UpdateLargestValue(root);
root = root->Parent();
}
}
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_HILBERT_R_TREE_SPLIT_IMPL_HPP
@@ -0,0 +1,117 @@
/**
* @file no_auxiliary_information.hpp
* @author Mikhail Lozhnikov
*
* Definition of the NoAuxiliaryInformation class, a class that provides
* no additional information about the nodes.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_NO_AUXILIARY_INFORMATION_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_NO_AUXILIARY_INFORMATION_HPP
namespace mlpack {
namespace tree {
template<typename TreeType>
class NoAuxiliaryInformation
{
public:
//! Construct the auxiliary information object.
NoAuxiliaryInformation() { };
//! Construct the auxiliary information object.
NoAuxiliaryInformation(const TreeType* /* node */) { };
//! Construct the auxiliary information object.
NoAuxiliaryInformation(const TreeType& /* node */) { };
/**
* Some tree types require to save some properties at the insertion process.
* This method allows the auxiliary information the option of manipulating
* the tree in order to perform the insertion process. If the auxiliary
* information does that, then the method should return true; if the method
* returns false the RectangleTree performs its default behavior.
*
* @param node The node in which the point is being inserted.
* @param point The global number of the point being inserted.
*/
bool HandlePointInsertion(TreeType* , const size_t)
{
return false;
}
/**
* Some tree types require to save some properties at the insertion process.
* This method allows the auxiliary information the option of manipulating
* the tree in order to perform the insertion process. If the auxiliary
* 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 */)
{
return false;
}
/**
* 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 */)
{
return false;
}
/**
* 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 */)
{
return false;
}
/**
* 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 */)
{
return false;
}
/**
* Nullify the auxiliary information in order to prevent an invalid free.
*/
void NullifyData()
{ }
/**
* Serialize the information.
*/
template<typename Archive>
void Serialize(Archive &, const unsigned int /* version */) { };
};
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_NO_AUXILIARY_INFORMATION_HPP
@@ -14,25 +14,25 @@ namespace mlpack {
namespace tree {
/**
* When descending a Rectangle tree to insert a point, we need to have a way to
* When descending a RectangleTree to insert a point, we need to have a way to
* choose a child node when the point isn't enclosed by any of them. This
* heuristic is used to do so.
* heuristic is used to do so using the rules for the R* tree.
*/
class RStarTreeDescentHeuristic
{
public:
/**
* Evaluate the node using a hueristic. The heuristic guarantees two things:
* Evaluate the node using a heuristic. The heuristic guarantees two things:
*
* 1. If point is contained in (or on) bound, the value returned is zero.
* 2. If the point is not contained in (or on) bound, the value returned is
* greater than zero.
* 1. If point is contained in (or on) bound, the value returned is zero.
* 2. If the point is not contained in (or on) bound, the value returned is
* greater than zero.
*
* @param bound The bound used for the node that is being evaluated.
* @param point The point that is being inserted.
* @param point The index of the point that is being inserted.
*/
template<typename TreeType>
static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point);
static size_t ChooseDescentNode(const TreeType* node, const size_t point);
template<typename TreeType>
static size_t ChooseDescentNode(const TreeType* node,
@@ -16,7 +16,7 @@ namespace tree {
template<typename TreeType>
inline size_t RStarTreeDescentHeuristic::ChooseDescentNode(
const TreeType* node,
const arma::vec& point)
const size_t point)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -25,7 +25,7 @@ inline size_t RStarTreeDescentHeuristic::ChooseDescentNode(
std::vector<ElemType> originalScores(node->NumChildren());
ElemType origMinScore = std::numeric_limits<ElemType>::max();
if (node->Children()[0]->IsLeaf())
if (node->Child(0).IsLeaf())
{
// If its children are leaf nodes, use minimum overlap to choose.
size_t bestIndex = 0;
@@ -41,12 +41,23 @@ inline size_t RStarTreeDescentHeuristic::ChooseDescentNode(
ElemType newOverlap = 1.0;
for (size_t k = 0; k < node->Bound().Dim(); k++)
{
ElemType newHigh = std::max(point[k],
node->Children()[i]->Bound()[k].Hi());
ElemType newLow = std::min(point[k],
node->Children()[i]->Bound()[k].Lo());
overlap *= node->Children()[i]->Bound()[k].Hi() < node->Children()[j]->Bound()[k].Lo() || node->Children()[i]->Bound()[k].Lo() > node->Children()[j]->Bound()[k].Hi() ? 0 : std::min(node->Children()[i]->Bound()[k].Hi(), node->Children()[j]->Bound()[k].Hi()) - std::max(node->Children()[i]->Bound()[k].Lo(), node->Children()[j]->Bound()[k].Lo());
newOverlap *= newHigh < node->Children()[j]->Bound()[k].Lo() || newLow > node->Children()[j]->Bound()[k].Hi() ? 0 : std::min(newHigh, node->Children()[j]->Bound()[k].Hi()) - std::max(newLow, node->Children()[j]->Bound()[k].Lo());
ElemType newHigh = std::max(node->Dataset().col(point)[k],
node->Child(i).Bound()[k].Hi());
ElemType newLow = std::min(node->Dataset().col(point)[k],
node->Child(i).Bound()[k].Lo());
overlap *= node->Child(i).Bound()[k].Hi() <
node->Child(j).Bound()[k].Lo() ||
node->Child(i).Bound()[k].Lo() >
node->Child(j).Bound()[k].Hi() ? 0 :
std::min(node->Child(i).Bound()[k].Hi(),
node->Child(j).Bound()[k].Hi()) -
std::max(node->Child(i).Bound()[k].Lo(),
node->Child(j).Bound()[k].Lo());
newOverlap *= newHigh < node->Child(j).Bound()[k].Lo() ||
newLow > node->Child(j).Bound()[k].Hi() ? 0 :
std::min(newHigh, node->Child(j).Bound()[k].Hi()) -
std::max(newLow, node->Child(j).Bound()[k].Lo());
}
sc += newOverlap - overlap;
}
@@ -90,9 +101,13 @@ inline size_t RStarTreeDescentHeuristic::ChooseDescentNode(
ElemType v2 = 1.0;
for (size_t j = 0; j < node->Bound().Dim(); j++)
{
v1 *= node->Children()[i]->Bound()[j].Width();
v2 *= node->Children()[i]->Bound()[j].Contains(point[j]) ? node->Children()[i]->Bound()[j].Width() : (node->Children()[i]->Bound()[j].Hi() < point[j] ? (point[j] - node->Children()[i]->Bound()[j].Lo()) :
(node->Children()[i]->Bound()[j].Hi() - point[j]));
v1 *= node->Child(i).Bound()[j].Width();
v2 *= node->Child(i).Bound()[j].Contains(
node->Dataset().col(point)[j]) ?
node->Child(i).Bound()[j].Width() :
(node->Child(i).Bound()[j].Hi() < node->Dataset().col(point)[j] ?
(node->Dataset().col(point)[j] - node->Child(i).Bound()[j].Lo()) :
(node->Child(i).Bound()[j].Hi() - node->Dataset().col(point)[j]));
}
assert(v2 - v1 >= 0);
@@ -157,11 +172,16 @@ inline size_t RStarTreeDescentHeuristic::ChooseDescentNode(
{
ElemType v1 = 1.0;
ElemType v2 = 1.0;
for (size_t j = 0; j < node->Children()[i]->Bound().Dim(); j++)
for (size_t j = 0; j < node->Child(i).Bound().Dim(); j++)
{
v1 *= node->Children()[i]->Bound()[j].Width();
v2 *= node->Children()[i]->Bound()[j].Contains(insertedNode->Bound()[j]) ? node->Children()[i]->Bound()[j].Width() :
(insertedNode->Bound()[j].Contains(node->Children()[i]->Bound()[j]) ? insertedNode->Bound()[j].Width() : (insertedNode->Bound()[j].Lo() < node->Children()[i]->Bound()[j].Lo() ? (node->Children()[i]->Bound()[j].Hi() - insertedNode->Bound()[j].Lo()) : (insertedNode->Bound()[j].Hi() - node->Children()[i]->Bound()[j].Lo())));
v1 *= node->Child(i).Bound()[j].Width();
v2 *= node->Child(i).Bound()[j].Contains(insertedNode->Bound()[j]) ?
node->Child(i).Bound()[j].Width() :
(insertedNode->Bound()[j].Contains(node->Child(i).Bound()[j]) ?
insertedNode->Bound()[j].Width() :
(insertedNode->Bound()[j].Lo() < node->Child(i).Bound()[j].Lo() ?
(node->Child(i).Bound()[j].Hi() - insertedNode->Bound()[j].Lo()) :
(insertedNode->Bound()[j].Hi() - node->Child(i).Bound()[j].Lo())));
}
assert(v2 - v1 >= 0);
@@ -18,31 +18,23 @@ namespace tree /** Trees and tree-building procedures. */ {
* nodes overflow, we split them, moving up the tree and splitting nodes
* as necessary.
*/
template <typename TreeType>
class RStarTreeSplit
{
public:
//! Default constructor.
RStarTreeSplit() { }
//! Construct this with the specified node.
RStarTreeSplit(const TreeType* /* node */) { }
//! Create a copy of the other.split.
RStarTreeSplit(const TreeType& /* other */) { }
/**
* Split a leaf node using the algorithm described in "The R*-tree: An
* Efficient and Robust Access method for Points and Rectangles." If
* necessary, this split will propagate upwards through the tree.
*/
void SplitLeafNode(TreeType* tree, std::vector<bool>& relevels);
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.
*/
bool SplitNonLeafNode(TreeType* tree, std::vector<bool>& relevels);
template <typename TreeType>
static bool SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels);
private:
/**
@@ -68,14 +60,8 @@ class RStarTreeSplit
/**
* Insert a node into another node.
*/
template <typename TreeType>
static void InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode);
public:
/**
* Serialize the split.
*/
template<typename Archive>
void Serialize(Archive &, const unsigned int /* version */) { };
};
} // namespace tree
@@ -22,7 +22,7 @@ namespace tree {
* new nodes into the tree, spliting the parent if necessary.
*/
template<typename TreeType>
void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& relevels)
void RStarTreeSplit::SplitLeafNode(TreeType *tree,std::vector<bool>& relevels)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -38,10 +38,10 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
tree->Count() = 0;
tree->NullifyData();
// Because this was a leaf node, numChildren must be 0.
tree->Children()[(tree->NumChildren())++] = copy;
tree->children[(tree->NumChildren())++] = copy;
assert(tree->NumChildren() == 1);
copy->Split().SplitLeafNode(copy, relevels);
RStarTreeSplit::SplitLeafNode(copy,relevels);
return;
}
@@ -53,12 +53,12 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
// We sort the points by decreasing distance to the centroid of the bound.
// We then remove the first p entries and reinsert them at the root.
TreeType* root = tree;
while(root->Parent() != NULL)
while (root->Parent() != NULL)
root = root->Parent();
size_t p = tree->MaxLeafSize() * 0.3; // The paper says this works the best.
if (p == 0)
{
tree->Split().SplitLeafNode(tree, relevels);
RStarTreeSplit::SplitLeafNode(tree,relevels);
return;
}
@@ -68,17 +68,19 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Metric().Evaluate(center,
tree->LocalDataset().col(i));
tree->Dataset().col(tree->Point(i)));
sorted[i].n = i;
}
std::sort(sorted.begin(), sorted.end(), StructComp<ElemType>);
std::vector<int> pointIndices(p);
std::vector<size_t> pointIndices(p);
for (size_t i = 0; i < p; i++)
{
// We start from the end of sorted.
pointIndices[i] = tree->Points()[sorted[sorted.size() - 1 - i].n];
root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n],
pointIndices[i] = tree->Point(sorted[sorted.size() - 1 - i].n);
root->DeletePoint(tree->Point(sorted[sorted.size() - 1 - i].n),
relevels);
}
@@ -104,7 +106,7 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
std::vector<SortStruct<ElemType>> sorted(tree->Count());
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->LocalDataset().col(i)[j];
sorted[i].d = tree->Dataset().col(tree->Point(i))[j];
sorted[i].n = i;
}
@@ -140,25 +142,25 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
std::vector<ElemType> minG2(maxG1.size());
for (size_t k = 0; k < tree->Bound().Dim(); k++)
{
minG1[k] = maxG1[k] = tree->LocalDataset().col(sorted[0].n)[k];
minG1[k] = maxG1[k] = tree->Dataset().col(tree->Point(sorted[0].n))[k];
minG2[k] = maxG2[k] =
tree->LocalDataset().col(sorted[sorted.size() - 1].n)[k];
tree->Dataset().col(tree->Point(sorted[sorted.size() - 1].n))[k];
for (size_t l = 1; l < tree->Count() - 1; l++)
{
if (l < cutOff)
{
if (tree->LocalDataset().col(sorted[l].n)[k] < minG1[k])
minG1[k] = tree->LocalDataset().col(sorted[l].n)[k];
else if (tree->LocalDataset().col(sorted[l].n)[k] > maxG1[k])
maxG1[k] = tree->LocalDataset().col(sorted[l].n)[k];
if (tree->Dataset().col(tree->Point(sorted[l].n))[k] < minG1[k])
minG1[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k];
else if (tree->Dataset().col(tree->Point(sorted[l].n))[k] > maxG1[k])
maxG1[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k];
}
else
{
if (tree->LocalDataset().col(sorted[l].n)[k] < minG2[k])
minG2[k] = tree->LocalDataset().col(sorted[l].n)[k];
else if (tree->LocalDataset().col(sorted[l].n)[k] > maxG2[k])
maxG2[k] = tree->LocalDataset().col(sorted[l].n)[k];
if (tree->Dataset().col(tree->Point(sorted[l].n))[k] < minG2[k])
minG2[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k];
else if (tree->Dataset().col(tree->Point(sorted[l].n))[k] > maxG2[k])
maxG2[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k];
}
}
}
@@ -208,7 +210,7 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
std::vector<SortStruct<ElemType>> sorted(tree->Count());
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->LocalDataset().col(i)[bestAxis];
sorted[i].d = tree->Dataset().col(tree->Point(i))[bestAxis];
sorted[i].n = i;
}
@@ -222,9 +224,9 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
for (size_t i = 0; i < tree->Count(); i++)
{
if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize())
treeOne->InsertPoint(tree->Points()[sorted[i].n]);
treeOne->InsertPoint(tree->Point(sorted[i].n));
else
treeTwo->InsertPoint(tree->Points()[sorted[i].n]);
treeTwo->InsertPoint(tree->Point(sorted[i].n));
}
}
else
@@ -232,26 +234,26 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
for (size_t i = 0; i < tree->Count(); i++)
{
if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize())
treeOne->InsertPoint(tree->Points()[sorted[i].n]);
treeOne->InsertPoint(tree->Point(sorted[i].n));
else
treeTwo->InsertPoint(tree->Points()[sorted[i].n]);
treeTwo->InsertPoint(tree->Point(sorted[i].n));
}
}
// Remove this node and insert treeOne and treeTwo.
TreeType* par = tree->Parent();
size_t index = 0;
while (par->Children()[index] != tree) { index++; }
while (par->children[index] != tree) { index++; }
assert(index != par->NumChildren());
par->Children()[index] = treeOne;
par->Children()[par->NumChildren()++] = treeTwo;
par->children[index] = treeOne;
par->children[par->NumChildren()++] = treeTwo;
// We only add one at a time, so we should only need to test for equality
// just in case, we use an assert.
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
if (par->NumChildren() == par->MaxNumChildren() + 1)
par->Split().SplitNonLeafNode(par, relevels);
RStarTreeSplit::SplitNonLeafNode(par,relevels);
assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren());
assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren());
@@ -269,8 +271,7 @@ void RStarTreeSplit<TreeType>::SplitLeafNode(TreeType *tree,std::vector<bool>& r
* higher up the tree because they were already updated if necessary.
*/
template<typename TreeType>
bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<bool>& relevels)
bool RStarTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -286,9 +287,9 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
copy->Parent() = tree;
tree->NumChildren() = 0;
tree->NullifyData();
tree->Children()[(tree->NumChildren())++] = copy;
tree->children[(tree->NumChildren())++] = copy;
copy->Split().SplitNonLeafNode(copy, relevels);
RStarTreeSplit::SplitNonLeafNode(copy,relevels);
return true;
}
@@ -368,7 +369,7 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<SortStruct<ElemType>> sorted(tree->NumChildren());
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Children()[i]->Bound()[j].Lo();
sorted[i].d = tree->Child(i).Bound()[j].Lo();
sorted[i].n = i;
}
@@ -404,28 +405,28 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<ElemType> minG2(maxG1.size());
for (size_t k = 0; k < tree->Bound().Dim(); k++)
{
minG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Lo();
maxG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Hi();
minG1[k] = tree->Child(sorted[0].n).Bound()[k].Lo();
maxG1[k] = tree->Child(sorted[0].n).Bound()[k].Hi();
minG2[k] =
tree->Children()[sorted[sorted.size() - 1].n]->Bound()[k].Lo();
tree->Child(sorted[sorted.size() - 1].n).Bound()[k].Lo();
maxG2[k] =
tree->Children()[sorted[sorted.size() - 1].n]->Bound()[k].Hi();
tree->Child(sorted[sorted.size() - 1].n).Bound()[k].Hi();
for (size_t l = 1; l < tree->NumChildren() - 1; l++)
{
if (l < cutOff)
{
if (tree->Children()[sorted[l].n]->Bound()[k].Lo() < minG1[k])
minG1[k] = tree->Children()[sorted[l].n]->Bound()[k].Lo();
else if (tree->Children()[sorted[l].n]->Bound()[k].Hi() > maxG1[k])
maxG1[k] = tree->Children()[sorted[l].n]->Bound()[k].Hi();
if (tree->Child(sorted[l].n).Bound()[k].Lo() < minG1[k])
minG1[k] = tree->Child(sorted[l].n).Bound()[k].Lo();
else if (tree->Child(sorted[l].n).Bound()[k].Hi() > maxG1[k])
maxG1[k] = tree->Child(sorted[l].n).Bound()[k].Hi();
}
else
{
if (tree->Children()[sorted[l].n]->Bound()[k].Lo() < minG2[k])
minG2[k] = tree->Children()[sorted[l].n]->Bound()[k].Lo();
else if (tree->Children()[sorted[l].n]->Bound()[k].Hi() > maxG2[k])
maxG2[k] = tree->Children()[sorted[l].n]->Bound()[k].Hi();
if (tree->Child(sorted[l].n).Bound()[k].Lo() < minG2[k])
minG2[k] = tree->Child(sorted[l].n).Bound()[k].Lo();
else if (tree->Child(sorted[l].n).Bound()[k].Hi() > maxG2[k])
maxG2[k] = tree->Child(sorted[l].n).Bound()[k].Hi();
}
}
}
@@ -480,7 +481,7 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<SortStruct<ElemType>> sorted(tree->NumChildren());
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Children()[i]->Bound()[j].Hi();
sorted[i].d = tree->Child(i).Bound()[j].Hi();
sorted[i].n = i;
}
@@ -517,28 +518,28 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
for (size_t k = 0; k < tree->Bound().Dim(); k++)
{
minG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Lo();
maxG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Hi();
minG1[k] = tree->Child(sorted[0].n).Bound()[k].Lo();
maxG1[k] = tree->Child(sorted[0].n).Bound()[k].Hi();
minG2[k] =
tree->Children()[sorted[sorted.size() - 1].n]->Bound()[k].Lo();
tree->Child(sorted[sorted.size() - 1].n).Bound()[k].Lo();
maxG2[k] =
tree->Children()[sorted[sorted.size() - 1].n]->Bound()[k].Hi();
tree->Child(sorted[sorted.size() - 1].n).Bound()[k].Hi();
for (size_t l = 1; l < tree->NumChildren() - 1; l++)
{
if (l < cutOff)
{
if (tree->Children()[sorted[l].n]->Bound()[k].Lo() < minG1[k])
minG1[k] = tree->Children()[sorted[l].n]->Bound()[k].Lo();
else if (tree->Children()[sorted[l].n]->Bound()[k].Hi() > maxG1[k])
maxG1[k] = tree->Children()[sorted[l].n]->Bound()[k].Hi();
if (tree->Child(sorted[l].n).Bound()[k].Lo() < minG1[k])
minG1[k] = tree->Child(sorted[l].n).Bound()[k].Lo();
else if (tree->Child(sorted[l].n).Bound()[k].Hi() > maxG1[k])
maxG1[k] = tree->Child(sorted[l].n).Bound()[k].Hi();
}
else
{
if (tree->Children()[sorted[l].n]->Bound()[k].Lo() < minG2[k])
minG2[k] = tree->Children()[sorted[l].n]->Bound()[k].Lo();
else if (tree->Children()[sorted[l].n]->Bound()[k].Hi() > maxG2[k])
maxG2[k] = tree->Children()[sorted[l].n]->Bound()[k].Hi();
if (tree->Child(sorted[l].n).Bound()[k].Lo() < minG2[k])
minG2[k] = tree->Child(sorted[l].n).Bound()[k].Lo();
else if (tree->Child(sorted[l].n).Bound()[k].Hi() > maxG2[k])
maxG2[k] = tree->Child(sorted[l].n).Bound()[k].Hi();
}
}
}
@@ -592,7 +593,7 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
{
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Children()[i]->Bound()[bestAxis].Lo();
sorted[i].d = tree->Child(i).Bound()[bestAxis].Lo();
sorted[i].n = i;
}
}
@@ -600,7 +601,7 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
{
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Children()[i]->Bound()[bestAxis].Hi();
sorted[i].d = tree->Child(i).Bound()[bestAxis].Hi();
sorted[i].n = i;
}
}
@@ -615,9 +616,9 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
for (size_t i = 0; i < tree->NumChildren(); i++)
{
if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren())
InsertNodeIntoTree(treeOne, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeOne, &(tree->Child(sorted[i].n)));
else
InsertNodeIntoTree(treeTwo, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeTwo, &(tree->Child(sorted[i].n)));
}
}
else
@@ -625,35 +626,33 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
for (size_t i = 0; i < tree->NumChildren(); i++)
{
if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren())
InsertNodeIntoTree(treeOne, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeOne, &(tree->Child(sorted[i].n)));
else
InsertNodeIntoTree(treeTwo, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeTwo, &(tree->Child(sorted[i].n)));
}
}
// Remove this node and insert treeOne and treeTwo
TreeType* par = tree->Parent();
size_t index = 0;
while (par->Children()[index] != tree) { index++; }
while (par->children[index] != tree) { index++; }
par->Children()[index] = treeOne;
par->Children()[par->NumChildren()++] = treeTwo;
par->children[index] = treeOne;
par->children[par->NumChildren()++] = treeTwo;
// We only add one at a time, so we should only need to test for equality
// just in case, we use an assert.
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
if (par->NumChildren() == par->MaxNumChildren() + 1)
{
par->Split().SplitNonLeafNode(par, relevels);
}
RStarTreeSplit::SplitNonLeafNode(par,relevels);
// We have to update the children of each of these new nodes so that they
// record the correct parent.
for (size_t i = 0; i < treeOne->NumChildren(); i++)
treeOne->Children()[i]->Parent() = treeOne;
treeOne->children[i]->Parent() = treeOne;
for (size_t i = 0; i < treeTwo->NumChildren(); i++)
treeTwo->Children()[i]->Parent() = treeTwo;
treeTwo->children[i]->Parent() = treeTwo;
assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren());
assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren());
@@ -673,11 +672,11 @@ bool RStarTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
* numberOfChildren.
*/
template<typename TreeType>
void RStarTreeSplit<TreeType>::InsertNodeIntoTree(TreeType* destTree,
TreeType* srcNode)
void RStarTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode)
{
destTree->Bound() |= srcNode->Bound();
destTree->Children()[destTree->NumChildren()++] = srcNode;
destTree->numDescendants += srcNode->numDescendants;
destTree->children[destTree->NumChildren()++] = srcNode;
}
} // namespace tree
@@ -29,10 +29,10 @@ class RTreeDescentHeuristic
* is greater than zero.
*
* @param node The node that is being evaluated.
* @param point The point that is being inserted.
* @param point The index of the point that is being inserted.
*/
template<typename TreeType>
static size_t ChooseDescentNode(const TreeType* node, const arma::vec& point);
static size_t ChooseDescentNode(const TreeType* node, const size_t point);
/**
* Evaluate the node using a heuristic. The heuristic guarantees two things:
@@ -15,7 +15,7 @@ namespace tree {
template<typename TreeType>
inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node,
const arma::vec& point)
const size_t point)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -28,14 +28,14 @@ inline size_t RTreeDescentHeuristic::ChooseDescentNode(const TreeType* node,
{
ElemType v1 = 1.0;
ElemType v2 = 1.0;
for (size_t j = 0; j < node->Children()[i]->Bound().Dim(); j++)
for (size_t j = 0; j < node->Child(i).Bound().Dim(); j++)
{
v1 *= node->Children()[i]->Bound()[j].Width();
v2 *= node->Children()[i]->Bound()[j].Contains(point[j]) ?
node->Children()[i]->Bound()[j].Width() :
(node->Children()[i]->Bound()[j].Hi() < point[j] ?
(point[j] - node->Children()[i]->Bound()[j].Lo()) :
(node->Children()[i]->Bound()[j].Hi() - point[j]));
v1 *= node->Child(i).Bound()[j].Width();
v2 *= node->Child(i).Bound()[j].Contains(node->Dataset().col(point)[j]) ?
node->Child(i).Bound()[j].Width() :
(node->Child(i).Bound()[j].Hi() < node->Dataset().col(point)[j] ?
(node->Dataset().col(point)[j] - node->Child(i).Bound()[j].Lo()) :
(node->Child(i).Bound()[j].Hi() - node->Dataset().col(point)[j]));
}
assert(v2 - v1 >= 0);
@@ -72,17 +72,17 @@ inline size_t RTreeDescentHeuristic::ChooseDescentNode(
{
ElemType v1 = 1.0;
ElemType v2 = 1.0;
for (size_t j = 0; j < node->Children()[i]->Bound().Dim(); j++)
for (size_t j = 0; j < node->Child(i).Bound().Dim(); j++)
{
v1 *= node->Children()[i]->Bound()[j].Width();
v2 *= node->Children()[i]->Bound()[j].Contains(insertedNode->Bound()[j]) ?
node->Children()[i]->Bound()[j].Width() :
(insertedNode->Bound()[j].Contains(node->Children()[i]->Bound()[j]) ?
v1 *= node->Child(i).Bound()[j].Width();
v2 *= node->Child(i).Bound()[j].Contains(insertedNode->Bound()[j]) ?
node->Child(i).Bound()[j].Width() :
(insertedNode->Bound()[j].Contains(node->Child(i).Bound()[j]) ?
insertedNode->Bound()[j].Width() :
(insertedNode->Bound()[j].Lo() < node->Children()[i]->Bound()[j].Lo()
? (node->Children()[i]->Bound()[j].Hi() -
(insertedNode->Bound()[j].Lo() < node->Child(i).Bound()[j].Lo()
? (node->Child(i).Bound()[j].Hi() -
insertedNode->Bound()[j].Lo()) : (insertedNode->Bound()[j].Hi() -
node->Children()[i]->Bound()[j].Lo())));
node->Child(i).Bound()[j].Lo())));
}
assert(v2 - v1 >= 0);
@@ -18,45 +18,40 @@ namespace tree /** Trees and tree-building procedures. */ {
* nodes overflow, we split them, moving up the tree and splitting nodes
* as necessary.
*/
template<typename TreeType>
class RTreeSplit
{
public:
//! Default constructor.
RTreeSplit() { }
//! Construct this with the specified node.
RTreeSplit(const TreeType* /* node */) { }
//! Create a copy of the other split.
RTreeSplit(const TreeType& /* other */) { }
/**
* Split a leaf node using the "default" algorithm. If necessary, this split
* will propagate upwards through the tree.
*/
void SplitLeafNode(TreeType* tree, std::vector<bool>& relevels);
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.
*/
bool SplitNonLeafNode(TreeType* tree, std::vector<bool>& relevels);
template<typename TreeType>
static bool SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels);
private:
/**
* Get the seeds for splitting a leaf node.
*/
static void GetPointSeeds(const TreeType* tree, int& i, int& j);
template<typename TreeType>
static void GetPointSeeds(const TreeType *tree,int& i, int& j);
/**
* Get the seeds for splitting a non-leaf node.
*/
static void GetBoundSeeds(const TreeType* tree, int& i, int& j);
template<typename TreeType>
static void GetBoundSeeds(const TreeType *tree,int& i, int& j);
/**
* Assign points to the two new nodes.
*/
template<typename TreeType>
static void AssignPointDestNode(TreeType* oldTree,
TreeType* treeOne,
TreeType* treeTwo,
@@ -66,6 +61,7 @@ class RTreeSplit
/**
* Assign nodes to the two new nodes.
*/
template<typename TreeType>
static void AssignNodeDestNode(TreeType* oldTree,
TreeType* treeOne,
TreeType* treeTwo,
@@ -75,15 +71,8 @@ class RTreeSplit
/**
* Insert a node into another node.
*/
template<typename TreeType>
static void InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode);
public:
/**
* Serialize the split.
*/
template<typename Archive>
void Serialize(Archive &, const unsigned int /* version */) { };
};
} // namespace tree
@@ -21,8 +21,7 @@ namespace tree {
* new nodes into the tree, spliting the parent if necessary.
*/
template<typename TreeType>
void RTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
std::vector<bool>& relevels)
void RTreeSplit::SplitLeafNode(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
@@ -35,8 +34,8 @@ void RTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
tree->Count() = 0;
tree->NullifyData();
// Because this was a leaf node, numChildren must be 0.
tree->Children()[(tree->NumChildren())++] = copy;
copy->Split().SplitLeafNode(copy, relevels);
tree->children[(tree->NumChildren())++] = copy;
RTreeSplit::SplitLeafNode(copy,relevels);
return;
}
@@ -47,7 +46,7 @@ void RTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
// rectangles, only points. We assume that the tree uses Euclidean Distance.
int i = 0;
int j = 0;
RTreeSplit<TreeType>::GetPointSeeds(tree,i, j);
RTreeSplit::GetPointSeeds(tree,i, j);
TreeType* treeOne = new TreeType(tree->Parent());
TreeType* treeTwo = new TreeType(tree->Parent());
@@ -58,16 +57,16 @@ void RTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
// Remove this node and insert treeOne and treeTwo.
TreeType* par = tree->Parent();
size_t index = 0;
while (par->Children()[index] != tree) { ++index; }
while (par->children[index] != tree) { ++index; }
par->Children()[index] = treeOne;
par->Children()[par->NumChildren()++] = treeTwo;
par->children[index] = treeOne;
par->children[par->NumChildren()++] = treeTwo;
// We only add one at a time, so we should only need to test for equality
// just in case, we use an assert.
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
if (par->NumChildren() == par->MaxNumChildren() + 1)
par->Split().SplitNonLeafNode(par, relevels);
RTreeSplit::SplitNonLeafNode(par,relevels);
assert(treeOne->Parent()->NumChildren() <= treeOne->MaxNumChildren());
assert(treeOne->Parent()->NumChildren() >= treeOne->MinNumChildren());
@@ -86,8 +85,7 @@ void RTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
* higher up the tree because they were already updated if necessary.
*/
template<typename TreeType>
bool RTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<bool>& relevels)
bool RTreeSplit::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
@@ -99,14 +97,14 @@ bool RTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
copy->Parent() = tree;
tree->NumChildren() = 0;
tree->NullifyData();
tree->Children()[(tree->NumChildren())++] = copy;
copy->Split().SplitNonLeafNode(copy, relevels);
tree->children[(tree->NumChildren())++] = copy;
RTreeSplit::SplitNonLeafNode(copy,relevels);
return true;
}
int i = 0;
int j = 0;
RTreeSplit<TreeType>::GetBoundSeeds(tree,i, j);
RTreeSplit::GetBoundSeeds(tree,i, j);
assert(i != j);
@@ -119,29 +117,29 @@ bool RTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
// Remove this node and insert treeOne and treeTwo.
TreeType* par = tree->Parent();
size_t index = 0;
while (par->Children()[index] != tree) { ++index; }
while (par->children[index] != tree) { ++index; }
assert(index != par->NumChildren());
par->Children()[index] = treeOne;
par->Children()[par->NumChildren()++] = treeTwo;
par->children[index] = treeOne;
par->children[par->NumChildren()++] = treeTwo;
for (size_t i = 0; i < par->NumChildren(); i++)
assert(par->Children()[i] != tree);
assert(par->children[i] != tree);
// We only add one at a time, so should only need to test for equality just in
// case, we use an assert.
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
if (par->NumChildren() == par->MaxNumChildren() + 1)
par->Split().SplitNonLeafNode(par, relevels);
RTreeSplit::SplitNonLeafNode(par,relevels);
// We have to update the children of each of these new nodes so that they
// record the correct parent.
for (size_t i = 0; i < treeOne->NumChildren(); i++)
treeOne->Children()[i]->Parent() = treeOne;
treeOne->children[i]->Parent() = treeOne;
for (size_t i = 0; i < treeTwo->NumChildren(); i++)
treeTwo->Children()[i]->Parent() = treeTwo;
treeTwo->children[i]->Parent() = treeTwo;
assert(treeOne->NumChildren() <= treeOne->MaxNumChildren());
assert(treeTwo->NumChildren() <= treeTwo->MaxNumChildren());
@@ -159,9 +157,7 @@ bool RTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
* The indices of these points will be stored in iRet and jRet.
*/
template<typename TreeType>
void RTreeSplit<TreeType>::GetPointSeeds(const TreeType* tree,
int& iRet,
int& jRet)
void RTreeSplit::GetPointSeeds(const TreeType *tree,int& iRet, int& jRet)
{
// Here we want to find the pair of points that it is worst to place in the
// same node. Because we are just using points, we will simply choose the two
@@ -172,7 +168,8 @@ void RTreeSplit<TreeType>::GetPointSeeds(const TreeType* tree,
for (size_t j = i + 1; j < tree->Count(); j++)
{
const typename TreeType::ElemType score = arma::prod(arma::abs(
tree->LocalDataset().col(i) - tree->LocalDataset().col(j)));
tree->Dataset().col(tree->Point(i)) -
tree->Dataset().col(tree->Point(j))));
if (score > worstPairScore)
{
@@ -189,9 +186,7 @@ void RTreeSplit<TreeType>::GetPointSeeds(const TreeType* tree,
* indices of the bounds will be stored in iRet and jRet.
*/
template<typename TreeType>
void RTreeSplit<TreeType>::GetBoundSeeds(const TreeType* tree,
int& iRet,
int& jRet)
void RTreeSplit::GetBoundSeeds(const TreeType *tree,int& iRet, int& jRet)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -204,10 +199,10 @@ void RTreeSplit<TreeType>::GetBoundSeeds(const TreeType* tree,
ElemType score = 1.0;
for (size_t k = 0; k < tree->Bound().Dim(); k++)
{
const ElemType hiMax = std::max(tree->Children()[i]->Bound()[k].Hi(),
tree->Children()[j]->Bound()[k].Hi());
const ElemType loMin = std::min(tree->Children()[i]->Bound()[k].Lo(),
tree->Children()[j]->Bound()[k].Lo());
const ElemType hiMax = std::max(tree->Child(i).Bound()[k].Hi(),
tree->Child(j).Bound()[k].Hi());
const ElemType loMin = std::min(tree->Child(i).Bound()[k].Lo(),
tree->Child(j).Bound()[k].Lo());
score *= (hiMax - loMin);
}
@@ -222,11 +217,11 @@ void RTreeSplit<TreeType>::GetBoundSeeds(const TreeType* tree,
}
template<typename TreeType>
void RTreeSplit<TreeType>::AssignPointDestNode(TreeType* oldTree,
TreeType* treeOne,
TreeType* treeTwo,
const int intI,
const int intJ)
void RTreeSplit::AssignPointDestNode(TreeType* oldTree,
TreeType* treeOne,
TreeType* treeTwo,
const int intI,
const int intJ)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -240,24 +235,20 @@ void RTreeSplit<TreeType>::AssignPointDestNode(TreeType* oldTree,
treeOne->Count() = 0;
treeTwo->Count() = 0;
treeOne->InsertPoint(oldTree->Points()[intI]);
treeTwo->InsertPoint(oldTree->Points()[intJ]);
treeOne->InsertPoint(oldTree->Point(intI));
treeTwo->InsertPoint(oldTree->Point(intJ));
// If intJ is the last point in the tree, we need to switch the order so that
// we remove the correct points.
if (intI > intJ)
{
oldTree->Points()[intI] = oldTree->Points()[--end]; // Decrement end.
oldTree->LocalDataset().col(intI) = oldTree->LocalDataset().col(end);
oldTree->Points()[intJ] = oldTree->Points()[--end]; // Decrement end.
oldTree->LocalDataset().col(intJ) = oldTree->LocalDataset().col(end);
oldTree->Point(intI) = oldTree->Point(--end); // Decrement end.
oldTree->Point(intJ) = oldTree->Point(--end); // Decrement end.
}
else
{
oldTree->Points()[intJ] = oldTree->Points()[--end]; // Decrement end.
oldTree->LocalDataset().col(intJ) = oldTree->LocalDataset().col(end);
oldTree->Points()[intI] = oldTree->Points()[--end]; // Decrement end.
oldTree->LocalDataset().col(intI) = oldTree->LocalDataset().col(end);
oldTree->Point(intJ) = oldTree->Point(--end); // Decrement end.
oldTree->Point(intI) = oldTree->Point(--end); // Decrement end.
}
size_t numAssignedOne = 1;
@@ -299,7 +290,7 @@ void RTreeSplit<TreeType>::AssignPointDestNode(TreeType* oldTree,
ElemType newVolTwo = 1.0;
for (size_t i = 0; i < oldTree->Bound().Dim(); i++)
{
ElemType c = oldTree->LocalDataset().col(index)[i];
ElemType c = oldTree->Dataset().col(oldTree->Point(index))[i];
newVolOne *= treeOne->Bound()[i].Contains(c) ?
treeOne->Bound()[i].Width() : (c < treeOne->Bound()[i].Lo() ?
(treeOne->Bound()[i].Hi() - c) : (c - treeOne->Bound()[i].Lo()));
@@ -333,17 +324,16 @@ void RTreeSplit<TreeType>::AssignPointDestNode(TreeType* oldTree,
// to the appropriate rectangle.
if (bestRect == 1)
{
treeOne->InsertPoint(oldTree->Points()[bestIndex]);
treeOne->InsertPoint(oldTree->Point(bestIndex));
numAssignedOne++;
}
else
{
treeTwo->InsertPoint(oldTree->Points()[bestIndex]);
treeTwo->InsertPoint(oldTree->Point(bestIndex));
numAssignedTwo++;
}
oldTree->Points()[bestIndex] = oldTree->Points()[--end]; // Decrement end.
oldTree->LocalDataset().col(bestIndex) = oldTree->LocalDataset().col(end);
oldTree->Point(bestIndex) = oldTree->Point(--end); // Decrement end.
}
// See if we need to satisfy the minimum fill.
@@ -352,22 +342,22 @@ void RTreeSplit<TreeType>::AssignPointDestNode(TreeType* oldTree,
if (numAssignedOne < numAssignedTwo)
{
for (size_t i = 0; i < end; i++)
treeOne->InsertPoint(oldTree->Points()[i]);
treeOne->InsertPoint(oldTree->Point(i));
}
else
{
for (size_t i = 0; i < end; i++)
treeTwo->InsertPoint(oldTree->Points()[i]);
treeTwo->InsertPoint(oldTree->Point(i));
}
}
}
template<typename TreeType>
void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
TreeType* treeOne,
TreeType* treeTwo,
const int intI,
const int intJ)
void RTreeSplit::AssignNodeDestNode(TreeType* oldTree,
TreeType* treeOne,
TreeType* treeTwo,
const int intI,
const int intJ)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -379,22 +369,22 @@ void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
for (size_t i = 0; i < oldTree->NumChildren(); i++)
for (size_t j = i + 1; j < oldTree->NumChildren(); j++)
assert(oldTree->Children()[i] != oldTree->Children()[j]);
assert(oldTree->children[i] != oldTree->children[j]);
InsertNodeIntoTree(treeOne, oldTree->Children()[intI]);
InsertNodeIntoTree(treeTwo, oldTree->Children()[intJ]);
InsertNodeIntoTree(treeOne, oldTree->children[intI]);
InsertNodeIntoTree(treeTwo, oldTree->children[intJ]);
// If intJ is the last node in the tree, we need to switch the order so that
// we remove the correct nodes.
if (intI > intJ)
{
oldTree->Children()[intI] = oldTree->Children()[--end];
oldTree->Children()[intJ] = oldTree->Children()[--end];
oldTree->children[intI] = oldTree->children[--end];
oldTree->children[intJ] = oldTree->children[--end];
}
else
{
oldTree->Children()[intJ] = oldTree->Children()[--end];
oldTree->Children()[intI] = oldTree->Children()[--end];
oldTree->children[intJ] = oldTree->children[--end];
oldTree->children[intI] = oldTree->children[--end];
}
assert(treeOne->NumChildren() == 1);
@@ -402,13 +392,13 @@ void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
for (size_t i = 0; i < end; i++)
for (size_t j = i + 1; j < end; j++)
assert(oldTree->Children()[i] != oldTree->Children()[j]);
assert(oldTree->children[i] != oldTree->children[j]);
for (size_t i = 0; i < end; i++)
assert(oldTree->Children()[i] != treeOne->Children()[0]);
assert(oldTree->children[i] != treeOne->children[0]);
for (size_t i = 0; i < end; i++)
assert(oldTree->Children()[i] != treeTwo->Children()[0]);
assert(oldTree->children[i] != treeTwo->children[0]);
size_t numAssignTreeOne = 1;
size_t numAssignTreeTwo = 1;
@@ -442,7 +432,7 @@ void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
// For each of the new rectangles, find the width in this dimension if
// we add the rectangle at index to the new rectangle.
const math::RangeType<ElemType>& range =
oldTree->Children()[index]->Bound()[i];
oldTree->Child(index).Bound()[i];
newVolOne *= treeOne->Bound()[i].Contains(range) ?
treeOne->Bound()[i].Width() : (range.Contains(treeOne->Bound()[i]) ?
range.Width() : (range.Lo() < treeOne->Bound()[i].Lo() ?
@@ -481,16 +471,16 @@ void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
// to the appropriate rectangle.
if (bestRect == 1)
{
InsertNodeIntoTree(treeOne, oldTree->Children()[bestIndex]);
InsertNodeIntoTree(treeOne, oldTree->children[bestIndex]);
numAssignTreeOne++;
}
else
{
InsertNodeIntoTree(treeTwo, oldTree->Children()[bestIndex]);
InsertNodeIntoTree(treeTwo, oldTree->children[bestIndex]);
numAssignTreeTwo++;
}
oldTree->Children()[bestIndex] = oldTree->Children()[--end];
oldTree->children[bestIndex] = oldTree->children[--end];
}
// See if we need to satisfy the minimum fill.
@@ -500,7 +490,7 @@ void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
{
for (size_t i = 0; i < end; i++)
{
InsertNodeIntoTree(treeOne, oldTree->Children()[i]);
InsertNodeIntoTree(treeOne, oldTree->children[i]);
numAssignTreeOne++;
}
}
@@ -508,7 +498,7 @@ void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
{
for (size_t i = 0; i < end; i++)
{
InsertNodeIntoTree(treeTwo, oldTree->Children()[i]);
InsertNodeIntoTree(treeTwo, oldTree->children[i]);
numAssignTreeTwo++;
}
}
@@ -516,11 +506,11 @@ void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
for (size_t i = 0; i < treeOne->NumChildren(); i++)
for (size_t j = i + 1; j < treeOne->NumChildren(); j++)
assert(treeOne->Children()[i] != treeOne->Children()[j]);
assert(treeOne->children[i] != treeOne->children[j]);
for (size_t i = 0; i < treeTwo->NumChildren(); i++)
for (size_t j = i + 1; j < treeTwo->NumChildren(); j++)
assert(treeTwo->Children()[i] != treeTwo->Children()[j]);
assert(treeTwo->children[i] != treeTwo->children[j]);
}
/**
@@ -528,11 +518,11 @@ void RTreeSplit<TreeType>::AssignNodeDestNode(TreeType* oldTree,
* numberOfChildren.
*/
template<typename TreeType>
void RTreeSplit<TreeType>::InsertNodeIntoTree(TreeType* destTree,
TreeType* srcNode)
void RTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode)
{
destTree->Bound() |= srcNode->Bound();
destTree->Children()[destTree->NumChildren()++] = srcNode;
destTree->numDescendants += srcNode->numDescendants;
destTree->children[destTree->NumChildren()++] = srcNode;
}
} // namespace tree
@@ -14,6 +14,7 @@
#include "../statistic.hpp"
#include "r_tree_split.hpp"
#include "r_tree_descent_heuristic.hpp"
#include "no_auxiliary_information.hpp"
namespace mlpack {
namespace tree /** Trees and tree-building procedures. */ {
@@ -34,13 +35,16 @@ namespace tree /** Trees and tree-building procedures. */ {
* @tparam SplitType The type of split to use when inserting points.
* @tparam DescentType The heuristic to use when descending the tree to insert
* points.
* @tparam AuxiliaryInformationType An auxiliary information contained
* in the node. This information depends on the type of the RectangleTree.
*/
template<typename MetricType = metric::EuclideanDistance,
typename StatisticType = EmptyStatistic,
typename MatType = arma::mat,
template<typename> class SplitType = RTreeSplit,
typename DescentType = RTreeDescentHeuristic>
typename SplitType = RTreeSplit,
typename DescentType = RTreeDescentHeuristic,
template<typename> class AuxiliaryInformationType = NoAuxiliaryInformation>
class RectangleTree
{
// The metric *must* be the euclidean distance.
@@ -52,7 +56,8 @@ class RectangleTree
typedef MatType Mat;
//! The element type held by the matrix type.
typedef typename MatType::elem_type ElemType;
//! The auxiliary information type held by the tree.
typedef AuxiliaryInformationType<RectangleTree> AuxiliaryInformation;
private:
//! The max number of child nodes a non-leaf node can have.
size_t maxNumChildren;
@@ -72,6 +77,8 @@ class RectangleTree
//! The number of points in the dataset contained in this node (and its
//! children).
size_t count;
//! The number of descendants of this node.
size_t numDescendants;
//! The max leaf size.
size_t maxLeafSize;
//! The minimum leaf size.
@@ -89,10 +96,8 @@ class RectangleTree
bool ownsDataset;
//! The mapping to the dataset
std::vector<size_t> points;
//! The local dataset
MatType* localDataset;
//! The class that performs the split of the node.
SplitType<RectangleTree> split;
//! A tree-specific information
AuxiliaryInformationType<RectangleTree> auxiliaryInfo;
public:
//! A single traverser for rectangle type trees. See
@@ -188,26 +193,23 @@ class RectangleTree
void SoftDelete();
/**
* Set dataset to null. Used for memory management. Be careful.
* Nullify the auxiliary information. Used for memory management.
* Be cafeful.
*/
void NullifyData();
/**
* Inserts a point into the tree. The point will be copied to the data matrix
* of the leaf node where it is finally inserted, but we pass by reference
* since it may be passed many times before it actually reaches a leaf.
* Inserts a point into the tree.
*
* @param point The point (arma::vec&) to be inserted.
* @param point The index of a point in the dataset.
*/
void InsertPoint(const size_t point);
/**
* Inserts a point into the tree, tracking which levels have been inserted
* into. The point will be copied to the data matrix of the leaf node where
* it is finally inserted, but we pass by reference since it may be passed
* many times before it actually reaches a leaf.
* into.
*
* @param point The point (arma::vec&) to be inserted.
* @param point The index of a point in the dataset.
* @param relevels The levels that have been reinserted to on this top level
* insertion.
*/
@@ -229,9 +231,8 @@ class RectangleTree
std::vector<bool>& relevels);
/**
* Deletes a point in the tree. The point will be removed from the data
* matrix of the leaf node where it is store and the bounding rectangles will
* be updated. However, the point will be kept in the centeral dataset. (The
* Deletes a point from the treeand, updates the bounding rectangle.
* However, the point will be kept in the centeral dataset. (The
* user may remove it from there if he wants, but he must not change the
* indices of the other points.) Returns true if the point is successfully
* removed and false if it is not. (ie. the point is not in the tree)
@@ -239,10 +240,9 @@ class RectangleTree
bool DeletePoint(const size_t point);
/**
* Deletes a point in the tree, tracking levels. The point will be removed
* from the data matrix of the leaf node where it is store and the bounding
* rectangles will be updated. However, the point will be kept in the
* centeral dataset. (The user may remove it from there if he wants, but he
* Deletes a point from the tree, updates the bounding rectangle,
* tracking levels. However, the point will be kept in the centeral dataset.
* (The user may remove it from there if he wants, but he
* must not change the indices of the other points.) Returns true if the point
* is successfully removed and false if it is not. (ie. the point is not in
* the tree)
@@ -291,10 +291,12 @@ class RectangleTree
//! Modify the statistic object for this node.
StatisticType& Stat() { return stat; }
//! Return the split object of this node.
const SplitType<RectangleTree>& Split() const { return split; }
//! Return the auxiliary information object of this node.
const AuxiliaryInformationType<RectangleTree> &AuxiliaryInfo() const
{ return auxiliaryInfo; }
//! Modify the split object of this node.
SplitType<RectangleTree>& Split() { return split; }
AuxiliaryInformationType<RectangleTree>& AuxiliaryInfo()
{ return auxiliaryInfo; }
//! Return whether or not this node is a leaf (true if it has no children).
bool IsLeaf() const;
@@ -329,16 +331,6 @@ class RectangleTree
//! Modify the dataset which the tree is built on. Be careful!
MatType& Dataset() { return const_cast<MatType&>(*dataset); }
//! Get the points vector for this node.
const std::vector<size_t>& Points() const { return points; }
//! Modify the points vector for this node. Be careful!
std::vector<size_t>& Points() { return points; }
//! Get the local dataset of this node.
const MatType& LocalDataset() const { return *localDataset; }
//! Modify the local dataset of this node.
MatType& LocalDataset() { return *localDataset; }
//! Get the metric which the tree uses.
MetricType Metric() const { return MetricType(); }
@@ -350,11 +342,6 @@ class RectangleTree
//! Modify the number of child nodes. Be careful.
size_t& NumChildren() { return numChildren; }
//! Get the children of this node.
const std::vector<RectangleTree*>& Children() const { return children; }
//! Modify the children of this node.
std::vector<RectangleTree*>& Children() { return children; }
/**
* Return the furthest distance to a point held in this node. If this is not
* a leaf node, then the distance is 0 because the node holds no points.
@@ -430,7 +417,11 @@ class RectangleTree
*
* @param index Index of point for which a dataset index is wanted.
*/
size_t Point(const size_t index) const;
size_t Point(const size_t index) const { return points[index]; }
//! Modify the index of a particular point in this node. Be very careful when
//! you do this! You may make the tree invalid.
size_t& Point(const size_t index) { return points[index]; }
//! Return the minimum distance to another node.
ElemType MinDistance(const RectangleTree* other) const
@@ -502,26 +493,6 @@ class RectangleTree
static bool HasSelfChildren() { return false; }
private:
/**
* Private copy constructor, available only to fill (pad) the tree to a
* specified level. TO BE REMOVED
*/
RectangleTree(const size_t begin,
const size_t count,
bound::HRectBound<MetricType> bound,
StatisticType stat,
const int maxLeafSize = 20) :
begin(begin),
count(count),
bound(bound),
stat(stat),
maxLeafSize(maxLeafSize) { }
RectangleTree* CopyMe()
{
return new RectangleTree(begin, count, bound, stat, maxLeafSize);
}
/**
* Splits the current node, recursing up the tree.
*
@@ -541,6 +512,12 @@ class RectangleTree
//! Friend access is given for the default constructor.
friend class boost::serialization::access;
//! Give friend access for SplitType.
friend SplitType;
//! Give friend access for AuxiliaryInformationType.
friend AuxiliaryInformation;
public:
/**
* Condense the bounding rectangles for this node based on the removal of the
@@ -19,9 +19,11 @@ namespace tree {
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
RectangleTree(const MatType& data,
const size_t maxLeafSize,
const size_t minLeafSize,
@@ -35,6 +37,7 @@ RectangleTree(const MatType& data,
parent(NULL),
begin(0),
count(0),
numDescendants(0),
maxLeafSize(maxLeafSize),
minLeafSize(minLeafSize),
bound(data.n_rows),
@@ -42,13 +45,10 @@ RectangleTree(const MatType& data,
dataset(new MatType(data)),
ownsDataset(true),
points(maxLeafSize + 1), // Add one to make splitting the node simpler.
localDataset(new MatType(arma::zeros<MatType>(data.n_rows,
maxLeafSize + 1)))
auxiliaryInfo(this)
{
stat = StatisticType(*this);
split = SplitType<RectangleTree>(this);
// For now, just insert the points in order.
RectangleTree* root = this;
@@ -59,9 +59,11 @@ RectangleTree(const MatType& data,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
RectangleTree(MatType&& data,
const size_t maxLeafSize,
const size_t minLeafSize,
@@ -75,6 +77,7 @@ RectangleTree(MatType&& data,
parent(NULL),
begin(0),
count(0),
numDescendants(0),
maxLeafSize(maxLeafSize),
minLeafSize(minLeafSize),
bound(data.n_rows),
@@ -82,13 +85,10 @@ RectangleTree(MatType&& data,
dataset(new MatType(std::move(data))),
ownsDataset(true),
points(maxLeafSize + 1), // Add one to make splitting the node simpler.
localDataset(new MatType(arma::zeros<MatType>(dataset->n_rows,
maxLeafSize + 1)))
auxiliaryInfo(this)
{
stat = StatisticType(*this);
split = SplitType<RectangleTree>(this);
// For now, just insert the points in order.
RectangleTree* root = this;
@@ -99,20 +99,24 @@ RectangleTree(MatType&& data,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
RectangleTree(
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>*
parentNode, const size_t numMaxChildren) :
maxNumChildren(numMaxChildren > 0 ? numMaxChildren :
parentNode->MaxNumChildren()),
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>*
parentNode,const size_t numMaxChildren) :
maxNumChildren(numMaxChildren > 0 ? numMaxChildren :
parentNode->MaxNumChildren()),
minNumChildren(parentNode->MinNumChildren()),
numChildren(0),
children(maxNumChildren + 1),
parent(parentNode),
begin(0),
count(0),
numDescendants(0),
maxLeafSize(parentNode->MaxLeafSize()),
minLeafSize(parentNode->MinLeafSize()),
bound(parentNode->Bound().Dim()),
@@ -120,11 +124,9 @@ RectangleTree(
dataset(&parentNode->Dataset()),
ownsDataset(false),
points(maxLeafSize + 1), // Add one to make splitting the node simpler.
localDataset(new MatType(arma::zeros<MatType>(parentNode->Bound().Dim(),
maxLeafSize + 1)))
auxiliaryInfo(this)
{
stat = StatisticType(*this);
split = SplitType<RectangleTree>(this);
}
/**
@@ -134,9 +136,11 @@ RectangleTree(
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
RectangleTree(
const RectangleTree& other,
const bool deepCopy) :
@@ -147,36 +151,26 @@ RectangleTree(
parent(other.Parent()),
begin(other.Begin()),
count(other.Count()),
numDescendants(other.numDescendants),
maxLeafSize(other.MaxLeafSize()),
minLeafSize(other.MinLeafSize()),
bound(other.bound),
parentDistance(other.ParentDistance()),
dataset(deepCopy ? new MatType(*other.dataset) : &other.Dataset()),
ownsDataset(deepCopy),
points(other.Points()),
localDataset(NULL)
points(other.points),
auxiliaryInfo(other.auxiliaryInfo)
{
split = SplitType<RectangleTree>(other);
if (deepCopy)
{
if (numChildren > 0)
{
for (size_t i = 0; i < numChildren; i++)
{
children[i] = new RectangleTree(*(other.Children()[i]));
}
}
else
{
localDataset = new MatType(other.LocalDataset());
children[i] = new RectangleTree(other.Child(i));
}
}
else
{
children = other.Children();
arma::mat& otherData = const_cast<arma::mat&>(other.LocalDataset());
localDataset = &otherData;
}
children = other.children;
}
/**
@@ -185,10 +179,12 @@ RectangleTree(
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
template<typename Archive>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
RectangleTree(
Archive& ar,
const typename boost::enable_if<typename Archive::is_loading>::type*) :
@@ -206,9 +202,11 @@ RectangleTree(
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
~RectangleTree()
{
for (size_t i = 0; i < numChildren; i++)
@@ -217,7 +215,6 @@ RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
if (ownsDataset)
delete dataset;
delete localDataset;
}
/**
@@ -227,9 +224,11 @@ RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
SoftDelete()
{
parent = NULL;
@@ -242,17 +241,19 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
}
/**
* Set the local dataset to null.
* Nullify the auxiliary information.
*/
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
NullifyData()
{
localDataset = NULL;
auxiliaryInfo.NullifyData();
}
/**
@@ -262,14 +263,18 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
InsertPoint(const size_t point)
{
// Expand the bound regardless of whether it is a leaf node.
bound |= dataset->col(point);
numDescendants++;
std::vector<bool> lvls(TreeDepth());
for (size_t i = 0; i < lvls.size(); i++)
lvls[i] = true;
@@ -277,49 +282,52 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
// If this is a leaf node, we stop here and add the point.
if (numChildren == 0)
{
localDataset->col(count) = dataset->col(point);
points[count++] = point;
if (!auxiliaryInfo.HandlePointInsertion(this, point))
points[count++] = point;
SplitNode(lvls);
return;
}
// If it is not a leaf node, we use the DescentHeuristic to choose a child
// to which we recurse.
const size_t descentNode = DescentType::ChooseDescentNode(this,
dataset->col(point));
auxiliaryInfo.HandlePointInsertion(this, point);
const size_t descentNode = DescentType::ChooseDescentNode(this, point);
children[descentNode]->InsertPoint(point, lvls);
}
/**
* Inserts a point into the tree, tracking which levels have been inserted into.
* The point will be copied to the data matrix of the leaf node where it is
* finally inserted, but we pass by reference since it may be passed many times
* before it actually reaches a leaf.
*/
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
InsertPoint(const size_t point, std::vector<bool>& relevels)
{
// Expand the bound regardless of whether it is a leaf node.
bound |= dataset->col(point);
numDescendants++;
// If this is a leaf node, we stop here and add the point.
if (numChildren == 0)
{
localDataset->col(count) = dataset->col(point);
points[count++] = point;
if (!auxiliaryInfo.HandlePointInsertion(this, point))
points[count++] = point;
SplitNode(relevels);
return;
}
// If it is not a leaf node, we use the DescentHeuristic to choose a child
// to which we recurse.
const size_t descentNode = DescentType::ChooseDescentNode(this,
dataset->col(point));
auxiliaryInfo.HandlePointInsertion(this, point);
const size_t descentNode = DescentType::ChooseDescentNode(this,point);
children[descentNode]->InsertPoint(point, relevels);
}
@@ -334,23 +342,30 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
InsertNode(RectangleTree* node,
const size_t level,
std::vector<bool>& relevels)
{
// Expand the bound regardless of the level.
bound |= node->Bound();
numDescendants += node->numDescendants;
if (level == TreeDepth())
{
children[numChildren++] = node;
node->Parent() = this;
if (!auxiliaryInfo.HandleNodeInsertion(this, node, true))
{
children[numChildren++] = node;
node->Parent() = this;
}
SplitNode(relevels);
}
else
{
auxiliaryInfo.HandleNodeInsertion(this, node, false);
const size_t descentNode = DescentType::ChooseDescentNode(this, node);
children[descentNode]->InsertNode(node, level, relevels);
}
@@ -363,9 +378,11 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
DeletePoint(const size_t point)
{
// It is possible that this will cause a reinsertion, so we need to handle the
@@ -384,8 +401,15 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
{
if (points[i] == point)
{
localDataset->col(i) = localDataset->col(--count); // Decrement count.
points[i] = points[count];
if (!auxiliaryInfo.HandlePointDeletion(this, i))
points[i] = points[--count];
RectangleTree* tree = this;
while (tree != NULL)
{
tree->numDescendants--;
tree = tree->Parent();
}
// This function wil ensure that minFill is satisfied.
CondenseTree(dataset->col(point), lvls, true);
return true;
@@ -408,9 +432,11 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
DeletePoint(const size_t point, std::vector<bool>& relevels)
{
if (numChildren == 0)
@@ -419,8 +445,15 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
{
if (points[i] == point)
{
localDataset->col(i) = localDataset->col(--count);
points[i] = points[count];
if (!auxiliaryInfo.HandlePointDeletion(this, i))
points[i] = points[--count];
RectangleTree* tree = this;
while (tree != NULL)
{
tree->numDescendants--;
tree = tree->Parent();
}
// This function will ensure that minFill is satisfied.
CondenseTree(dataset->col(point), relevels, true);
return true;
@@ -436,6 +469,7 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
return false;
}
/**
* Recurse through the tree to remove the node. Once we find the node, we
* shrink the rectangles if necessary.
@@ -443,23 +477,34 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
RemoveNode(const RectangleTree* node, std::vector<bool>& relevels)
{
for (size_t i = 0; i < numChildren; i++)
{
if (children[i] == node)
{
children[i] = children[--numChildren]; // Decrement numChildren.
if (!auxiliaryInfo.HandleNodeRemoval(this, i))
{
children[i] = children[--numChildren]; // Decrement numChildren.
}
RectangleTree* tree = this;
while (tree != NULL)
{
tree->numDescendants -= node->numDescendants;
tree = tree->Parent();
}
CondenseTree(arma::vec(), relevels, false);
return true;
}
bool contains = true;
for (size_t j = 0; j < node->Bound().Dim(); j++)
contains &= Children()[i]->Bound()[j].Contains(node->Bound()[j]);
contains &= Child(i).Bound()[j].Contains(node->Bound()[j]);
if (contains)
if (children[i]->RemoveNode(node, relevels))
@@ -472,10 +517,11 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::TreeSize() const
DescentType, AuxiliaryInformationType>::TreeSize() const
{
int n = 0;
for (int i = 0; i < numChildren; i++)
@@ -487,17 +533,18 @@ size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::TreeDepth() const
DescentType, AuxiliaryInformationType>::TreeDepth() const
{
int n = 1;
RectangleTree* currentNode = const_cast<RectangleTree*> (this);
while (!currentNode->IsLeaf())
{
currentNode = currentNode->Children()[0];
currentNode = currentNode->children[0];
n++;
}
@@ -507,10 +554,11 @@ size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
inline bool RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::IsLeaf() const
DescentType, AuxiliaryInformationType>::IsLeaf() const
{
return (numChildren == 0);
}
@@ -522,13 +570,14 @@ inline bool RectangleTree<MetricType, StatisticType, MatType, SplitType,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
inline
typename RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::ElemType
DescentType, AuxiliaryInformationType>::ElemType
RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::FurthestPointDistance() const
DescentType, AuxiliaryInformationType>::FurthestPointDistance() const
{
if (!IsLeaf())
return 0.0;
@@ -547,13 +596,14 @@ RectangleTree<MetricType, StatisticType, MatType, SplitType,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
inline
typename RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::ElemType
DescentType, AuxiliaryInformationType>::ElemType
RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::FurthestDescendantDistance() const
DescentType, AuxiliaryInformationType>::FurthestDescendantDistance() const
{
// Return the distance from the centroid to a corner of the bound.
return 0.5 * bound.Diameter();
@@ -566,10 +616,11 @@ RectangleTree<MetricType, StatisticType, MatType, SplitType,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::NumPoints() const
DescentType, AuxiliaryInformationType>::NumPoints() const
{
if (numChildren != 0) // This is not a leaf node.
return 0;
@@ -583,22 +634,13 @@ inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::NumDescendants() const
DescentType, AuxiliaryInformationType>::NumDescendants() const
{
if (numChildren == 0)
{
return count;
}
else
{
size_t n = 0;
for (size_t i = 0; i < numChildren; i++)
n += children[i]->NumDescendants();
return n;
}
return numDescendants;
}
/**
@@ -607,10 +649,11 @@ inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::Descendant(const size_t index) const
DescentType, AuxiliaryInformationType>::Descendant(const size_t index) const
{
// I think this may be inefficient...
if (numChildren == 0)
@@ -633,20 +676,6 @@ inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
}
}
/**
* Return the index of a particular point contained in this node.
*/
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::Point(const size_t index) const
{
return points[index];
}
/**
* Split the tree. This calls the SplitType code to split a node. This method
* should only be called on a leaf node.
@@ -654,9 +683,11 @@ inline size_t RectangleTree<MetricType, StatisticType, MatType, SplitType,
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
SplitNode(std::vector<bool>& relevels)
{
if (numChildren == 0)
@@ -667,7 +698,7 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
// If we are full, then we need to split (or at least try). The SplitType
// takes care of this and of moving up the tree if necessary.
split.SplitLeafNode(this, relevels);
SplitType::SplitLeafNode(this,relevels);
}
else
{
@@ -677,7 +708,7 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
// If we are full, then we need to split (or at least try). The SplitType
// takes care of this and of moving up the tree if necessary.
split.SplitNonLeafNode(this, relevels);
SplitType::SplitNonLeafNode(this,relevels);
}
}
@@ -685,9 +716,11 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
RectangleTree() :
maxNumChildren(0), // Try to give sensible defaults, but it shouldn't matter
minNumChildren(0), // because this tree isn't valid anyway and is only used
@@ -699,8 +732,7 @@ RectangleTree() :
minLeafSize(0),
parentDistance(0.0),
dataset(NULL),
ownsDataset(false),
localDataset(NULL)
ownsDataset(false)
{
// Nothing to do.
}
@@ -712,9 +744,11 @@ RectangleTree() :
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
CondenseTree(const arma::vec& point,
std::vector<bool>& relevels,
const bool usePoint)
@@ -726,10 +760,13 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
// We can't delete the root node.
for (size_t i = 0; i < parent->NumChildren(); i++)
{
if (parent->Children()[i] == this)
if (parent->children[i] == this)
{
// Decrement numChildren.
parent->Children()[i] = parent->Children()[--parent->NumChildren()];
if (!auxiliaryInfo.HandleNodeRemoval(parent, i))
{
parent->children[i] = parent->children[--parent->NumChildren()];
}
// We find the root and shrink bounds at the same time.
bool stillShrinking = true;
@@ -743,7 +780,25 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
if (stillShrinking)
stillShrinking = root->ShrinkBoundForBound(bound);
// Reinsert the points at the root node.
root = parent;
while (root != NULL)
{
root->numDescendants -= numDescendants;
root = root->Parent();
}
stillShrinking = true;
root = parent;
while (root->Parent() != NULL)
{
if (stillShrinking)
stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root);
root = root->Parent();
}
if (stillShrinking)
stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root);
// Reinsert the points at the root node.
for (size_t j = 0; j < count; j++)
root->InsertPoint(points[j], relevels);
@@ -765,10 +820,13 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
// The normal case. We need to be careful with the root.
for (size_t j = 0; j < parent->NumChildren(); j++)
{
if (parent->Children()[j] == this)
if (parent->children[j] == this)
{
// Decrement numChildren.
parent->Children()[j] = parent->Children()[--parent->NumChildren()];
if (!auxiliaryInfo.HandleNodeRemoval(parent,j))
{
parent->children[j] = parent->children[--parent->NumChildren()];
}
size_t level = TreeDepth();
// We find the root and shrink bounds at the same time.
@@ -783,6 +841,24 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
if (stillShrinking)
stillShrinking = root->ShrinkBoundForBound(bound);
root = parent;
while (root != NULL)
{
root->numDescendants -= numDescendants;
root = root->Parent();
}
stillShrinking = true;
root = parent;
while (root->Parent() != NULL)
{
if (stillShrinking)
stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root);
root = root->Parent();
}
if (stillShrinking)
stillShrinking = root->AuxiliaryInfo().UpdateAuxiliaryInfo(root);
// Reinsert the nodes at the root node.
for (size_t i = 0; i < numChildren; i++)
root->InsertNode(children[i], level, relevels);
@@ -802,14 +878,14 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
RectangleTree* child = children[0];
// Required for the X tree.
if(child->NumChildren() > maxNumChildren)
if (child->NumChildren() > maxNumChildren)
{
maxNumChildren = child->MaxNumChildren();
children.resize(maxNumChildren+1);
}
for (size_t i = 0; i < child->NumChildren(); i++) {
children[i] = child->Children()[i];
children[i] = child->children[i];
children[i]->Parent() = this;
}
@@ -818,10 +894,11 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
for (size_t i = 0; i < child->Count(); i++)
{
// In case the tree has a height of two.
points[i] = child->Points()[i];
localDataset->col(i) = child->LocalDataset().col(i);
points[i] = child->Point(i);
}
auxiliaryInfo = child->AuxiliaryInfo();
count = child->Count();
child->SoftDelete();
return;
@@ -829,9 +906,13 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
}
// If we didn't delete it, shrink the bound if we need to.
if (usePoint && ShrinkBoundForPoint(point) && parent != NULL)
if (usePoint &&
(ShrinkBoundForPoint(point) || auxiliaryInfo.UpdateAuxiliaryInfo(this)) &&
parent != NULL)
parent->CondenseTree(point, relevels, usePoint);
else if (!usePoint && ShrinkBoundForBound(bound) && parent != NULL)
else if (!usePoint &&
(ShrinkBoundForBound(bound) || auxiliaryInfo.UpdateAuxiliaryInfo(this)) &&
parent != NULL)
parent->CondenseTree(point, relevels, usePoint);
}
@@ -841,9 +922,11 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
ShrinkBoundForPoint(const arma::vec& point)
{
bool shrunk = false;
@@ -856,8 +939,8 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
ElemType min = std::numeric_limits<ElemType>::max();
for (size_t j = 0; j < count; j++)
{
if (localDataset->col(j)[i] < min)
min = localDataset->col(j)[i];
if (dataset->col(points[j])[i] < min)
min = dataset->col(points[j])[i];
}
if (bound[i].Lo() < min)
@@ -875,8 +958,8 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
ElemType max = std::numeric_limits<ElemType>::lowest();
for (size_t j = 0; j < count; j++)
{
if (localDataset->col(j)[i] > max)
max = localDataset->col(j)[i];
if (dataset->col(points[j])[i] > max)
max = dataset->col(points[j])[i];
}
if (bound[i].Hi() > max)
@@ -937,9 +1020,11 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
ShrinkBoundForBound(const bound::HRectBound<MetricType>& /* b */)
{
// Using the sum is safe since none of the dimensions can increase.
@@ -971,10 +1056,12 @@ bool RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
template<typename Archive>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
Serialize(Archive& ar,
const unsigned int /* version */)
{
@@ -990,8 +1077,6 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
if (ownsDataset && dataset)
delete dataset;
if (localDataset)
delete localDataset;
}
ar & CreateNVP(maxNumChildren, "maxNumChildren");
@@ -1014,6 +1099,7 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
ar & CreateNVP(begin, "begin");
ar & CreateNVP(count, "count");
ar & CreateNVP(numDescendants, "numDescendants");
ar & CreateNVP(maxLeafSize, "maxLeafSize");
ar & CreateNVP(minLeafSize, "minLeafSize");
ar & CreateNVP(bound, "bound");
@@ -1026,8 +1112,7 @@ void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
ownsDataset = true;
ar & CreateNVP(points, "points");
ar & CreateNVP(localDataset, "localDataset");
ar & CreateNVP(split, "split");
ar & CreateNVP(auxiliaryInfo, "auxiliaryInfo");
// Because 'children' holds mlpack types (that have Serialize()), we can't use
// the std::vector serialization.
@@ -19,11 +19,12 @@ namespace tree {
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
template<typename RuleType>
class RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>::SingleTreeTraverser
DescentType, AuxiliaryInformationType>::SingleTreeTraverser
{
public:
/**
@@ -20,10 +20,12 @@ namespace tree {
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
template<typename RuleType>
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
SingleTreeTraverser<RuleType>::SingleTreeTraverser(RuleType& rule) :
rule(rule),
numPrunes(0)
@@ -32,10 +34,12 @@ SingleTreeTraverser<RuleType>::SingleTreeTraverser(RuleType& rule) :
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
template<typename RuleType>
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType>::
void RectangleTree<MetricType, StatisticType, MatType, SplitType, DescentType,
AuxiliaryInformationType>::
SingleTreeTraverser<RuleType>::Traverse(
const size_t queryIndex,
const RectangleTree& referenceNode)
@@ -45,7 +49,7 @@ SingleTreeTraverser<RuleType>::Traverse(
if (referenceNode.IsLeaf())
{
for (size_t i = 0; i < referenceNode.Count(); i++)
rule.BaseCase(queryIndex, referenceNode.Points()[i]);
rule.BaseCase(queryIndex, referenceNode.Point(i));
return;
}
@@ -55,7 +59,7 @@ SingleTreeTraverser<RuleType>::Traverse(
std::vector<NodeAndScore> nodesAndScores(referenceNode.NumChildren());
for (size_t i = 0; i < referenceNode.NumChildren(); i++)
{
nodesAndScores[i].node = referenceNode.Children()[i];
nodesAndScores[i].node = &(referenceNode.Child(i));
nodesAndScores[i].score = rule.Score(queryIndex, *nodesAndScores[i].node);
}
@@ -21,10 +21,11 @@ namespace tree {
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename> class SplitType,
typename DescentType>
typename SplitType,
typename DescentType,
template<typename> class AuxiliaryInformationType>
class TreeTraits<RectangleTree<MetricType, StatisticType, MatType, SplitType,
DescentType>>
DescentType, AuxiliaryInformationType>>
{
public:
/**
@@ -38,7 +38,8 @@ using RTree = RectangleTree<MetricType,
StatisticType,
MatType,
RTreeSplit,
RTreeDescentHeuristic>;
RTreeDescentHeuristic,
NoAuxiliaryInformation>;
/**
* The R*-tree, a more recent variant of the R tree. This template typedef
@@ -65,7 +66,8 @@ using RStarTree = RectangleTree<MetricType,
StatisticType,
MatType,
RStarTreeSplit,
RStarTreeDescentHeuristic>;
RStarTreeDescentHeuristic,
NoAuxiliaryInformation>;
/**
* The X-tree, a variant of the R tree with supernodes. This template typedef
@@ -90,7 +92,44 @@ using XTree = RectangleTree<MetricType,
StatisticType,
MatType,
XTreeSplit,
RTreeDescentHeuristic>;
RTreeDescentHeuristic,
XTreeAuxiliaryInformation>;
/**
* The Hilbert R-tree, a variant of the R tree with an ordering along
* the Hilbert curve. This template typedef satisfies the TreeType policy API.
*
* @code
* @inproceedings{kamel1994r,
* author = {Kamel, Ibrahim and Faloutsos, Christos},
* title = {Hilbert R-tree: An Improved R-tree Using Fractals},
* booktitle = {Proceedings of the 20th International Conference on Very Large Data Bases},
* series = {VLDB '94},
* year = {1994},
* isbn = {1-55860-153-8},
* pages = {500--509},
* numpages = {10},
* url = {http://dl.acm.org/citation.cfm?id=645920.673001},
* acmid = {673001},
* publisher = {Morgan Kaufmann Publishers Inc.},
* address = {San Francisco, CA, USA}
* }
* @endcode
*
* @see @ref trees, RTree, DiscreteHilbertRTree
*/
template<typename TreeType>
using DiscreteHilbertRTreeAuxiliaryInformation =
HilbertRTreeAuxiliaryInformation<TreeType,DiscreteHilbertValue>;
template<typename MetricType, typename StatisticType, typename MatType>
using HilbertRTree = RectangleTree<MetricType,
StatisticType,
MatType,
HilbertRTreeSplit<2>,
HilbertRTreeDescentHeuristic,
DiscreteHilbertRTreeAuxiliaryInformation>;
} // namespace tree
} // namespace mlpack
@@ -0,0 +1,186 @@
/**
* @file no_auxiliary_information.hpp
* @author Mikhail Lozhnikov
*
* Definition of the XTreeAuxiliaryInformation class, a class that provides
* some x-tree specific information about the nodes.
*/
#ifndef MLPACK_CORE_TREE_RECTANGLE_TREE_X_TREE_AUXILIARY_INFORMATION_HPP
#define MLPACK_CORE_TREE_RECTANGLE_TREE_X_TREE_AUXILIARY_INFORMATION_HPP
namespace mlpack {
namespace tree {
/**
* The XTreeAuxiliaryInformation class provides information specific to X trees
* for each node in a RectangleTree.
*/
template<typename TreeType>
class XTreeAuxiliaryInformation
{
public:
//! Default constructor
XTreeAuxiliaryInformation() :
normalNodeMaxNumChildren(0),
splitHistory(0)
{ };
/**
* Construct this whith the specified node.
*
* @param node The node that stores this auxiliary information.
*/
XTreeAuxiliaryInformation(const TreeType* node) :
normalNodeMaxNumChildren(node->Parent() ?
node->Parent()->AuxiliaryInfo().NormalNodeMaxNumChildren() :
node->MaxNumChildren()),
splitHistory(node->Bound().Dim())
{ };
/**
* Create an auxiliary information object by copying from the other node.
*
* @param other The node from which the information will be copied.
*/
XTreeAuxiliaryInformation(const TreeType& other) :
normalNodeMaxNumChildren(
other.AuxiliaryInfo().NormalNodeMaxNumChildren()),
splitHistory(other.AuxiliaryInfo().SplitHistory())
{ };
/**
* 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 */)
{
return false;
}
/**
* Some tree types require to save some properties at the insertion process.
* This method allows the auxiliary information the option of manipulating the
* tree in order to perform the insertion process. If the auxiliary
* 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 */)
{
return false;
}
/**
* 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* , const size_t)
{
return false;
}
/**
* 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* , const size_t)
{
return false;
}
/**
* 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* )
{
return false;
}
/**
* Nullify the auxiliary information in order to prevent an invalid free.
*/
void NullifyData()
{ }
/**
* The X tree requires that the tree records it's "split history". To make
* this easy, we use the following structure.
*/
typedef struct SplitHistoryStruct
{
int lastDimension;
std::vector<bool> history;
SplitHistoryStruct(int dim) : lastDimension(0), history(dim)
{
for (int i = 0; i < dim; i++)
history[i] = false;
}
template<typename Archive>
void Serialize(Archive& ar, const unsigned int /* version */)
{
ar & data::CreateNVP(lastDimension, "lastDimension");
ar & data::CreateNVP(history, "history");
}
} SplitHistoryStruct;
private:
//! The max number of child nodes a non-leaf normal node can have.
size_t normalNodeMaxNumChildren;
//! A struct to store the "split history" for X trees.
SplitHistoryStruct splitHistory;
public:
//! Return the maximum number of a normal node's children.
size_t NormalNodeMaxNumChildren() const { return normalNodeMaxNumChildren; }
//! Modify the maximum number of a normal node's children.
size_t& NormalNodeMaxNumChildren() { return normalNodeMaxNumChildren; }
//! Return the split history of the node assosiated with this object.
const SplitHistoryStruct& SplitHistory() const { return splitHistory; }
//! Modify the split history of the node assosiated with this object.
SplitHistoryStruct& SplitHistory() { return splitHistory; }
/**
* Serialize the information.
*/
template<typename Archive>
void Serialize(Archive& ar, const unsigned int /* version */)
{
using data::CreateNVP;
ar & CreateNVP(normalNodeMaxNumChildren, "normalNodeMaxNumChildren");
ar & CreateNVP(splitHistory, "splitHistory");
}
};
} // namespace tree
} // namespace mlpack
#endif // MLPACK_CORE_TREE_RECTANGLE_TREE_X_TREE_AUXILIARY_INFORMATION_HPP
@@ -28,61 +28,25 @@ const double MAX_OVERLAP = 0.2;
* nodes overflow, we split them, moving up the tree and splitting nodes
* as necessary.
*/
template<typename TreeType>
class XTreeSplit
{
public:
//! Default constructor
XTreeSplit();
//! Construct this with the specified node.
XTreeSplit(const TreeType* node);
//! Create a copy of the other.split.
XTreeSplit(const TreeType& other);
/**
* Split a leaf node using the algorithm described in "The R*-tree: An
* Efficient and Robust Access method for Points and Rectangles." If
* necessary, this split will propagate upwards through the tree.
*/
void SplitLeafNode(TreeType* tree, std::vector<bool>& relevels);
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.
*/
bool SplitNonLeafNode(TreeType* tree, std::vector<bool>& relevels);
/**
* The X tree requires that the tree records it's "split history". To make
* this easy, we use the following structure.
*/
typedef struct SplitHistoryStruct
{
int lastDimension;
std::vector<bool> history;
SplitHistoryStruct(int dim) : lastDimension(0), history(dim)
{
for (int i = 0; i < dim; i++)
history[i] = false;
}
template<typename Archive>
void Serialize(Archive& ar, const unsigned int /* version */)
{
ar & data::CreateNVP(lastDimension, "lastDimension");
ar & data::CreateNVP(history, "history");
}
} SplitHistoryStruct;
template<typename TreeType>
static bool SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels);
private:
//! The max number of child nodes a non-leaf normal node can have.
size_t normalNodeMaxNumChildren;
//! A struct to store the "split history" for X trees.
SplitHistoryStruct splitHistory;
/**
* Class to allow for faster sorting.
*/
@@ -107,23 +71,8 @@ class XTreeSplit
/**
* Insert a node into another node.
*/
template<typename TreeType>
static void InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode);
public:
//! Return the maximum number of a normal node's children.
size_t NormalNodeMaxNumChildren() const { return normalNodeMaxNumChildren; }
//! Modify the maximum number of a normal node's children.
size_t& NormalNodeMaxNumChildren() { return normalNodeMaxNumChildren; }
//! Return the split history of the node assosiated with this object.
const SplitHistoryStruct& SplitHistory() const { return splitHistory; }
//! Modify the split history of the node assosiated with this object.
SplitHistoryStruct& SplitHistory() { return splitHistory; }
/**
* Serialize the split.
*/
template<typename Archive>
void Serialize(Archive& ar, const unsigned int /* version */);
};
} // namespace tree
@@ -14,32 +14,6 @@
namespace mlpack {
namespace tree {
template<typename TreeType>
XTreeSplit<TreeType>::XTreeSplit() :
normalNodeMaxNumChildren(0),
splitHistory(0)
{
}
template<typename TreeType>
XTreeSplit<TreeType>::XTreeSplit(const TreeType*node) :
normalNodeMaxNumChildren(node->Parent() ?
node->Parent()->Split().NormalNodeMaxNumChildren() :
node->MaxNumChildren()),
splitHistory(node->Bound().Dim())
{
}
template<typename TreeType>
XTreeSplit<TreeType>::XTreeSplit(const TreeType &other) :
normalNodeMaxNumChildren(other.Split().NormalNodeMaxNumChildren()),
splitHistory(other.Split().SplitHistory())
{
}
/**
* We call GetPointSeeds to get the two points which will be the initial points
* in the new nodes We then call AssignPointDestNode to assign the remaining
@@ -47,8 +21,7 @@ XTreeSplit<TreeType>::XTreeSplit(const TreeType &other) :
* new nodes into the tree, spliting the parent if necessary.
*/
template<typename TreeType>
void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
std::vector<bool>& relevels)
void XTreeSplit::SplitLeafNode(TreeType *tree,std::vector<bool>& relevels)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -64,9 +37,9 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
tree->Count() = 0;
tree->NullifyData();
// Because this was a leaf node, numChildren must be 0.
tree->Children()[(tree->NumChildren())++] = copy;
tree->children[(tree->NumChildren())++] = copy;
assert(tree->NumChildren() == 1);
copy->Split().SplitLeafNode(copy, relevels);
XTreeSplit::SplitLeafNode(copy,relevels);
return;
}
@@ -84,7 +57,7 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
size_t p = tree->MaxLeafSize() * 0.3;
if (p == 0)
{
tree->Split().SplitLeafNode(tree, relevels);
XTreeSplit::SplitLeafNode(tree,relevels);
return;
}
@@ -94,17 +67,19 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Metric().Evaluate(center,
tree->LocalDataset().col(i));
tree->Dataset().col(tree->Point(i)));
sorted[i].n = i;
}
std::sort(sorted.begin(), sorted.end(), structComp<ElemType>);
std::vector<int> pointIndices(p);
std::vector<size_t> pointIndices(p);
for (size_t i = 0; i < p; i++)
{
// We start from the end of sorted.
pointIndices[i] = tree->Points()[sorted[sorted.size() - 1 - i].n];
root->DeletePoint(tree->Points()[sorted[sorted.size() - 1 - i].n],
pointIndices[i] = tree->Point(sorted[sorted.size() - 1 - i].n);
root->DeletePoint(tree->Point(sorted[sorted.size() - 1 - i].n),
relevels);
}
@@ -140,7 +115,7 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
// Since we only have points in the leaf nodes, we only need to sort once.
std::vector<sortStruct<ElemType>> sorted(tree->Count());
for (size_t i = 0; i < sorted.size(); i++) {
sorted[i].d = tree->LocalDataset().col(i)[j];
sorted[i].d = tree->Dataset().col(tree->Point(i))[j];
sorted[i].n = i;
}
@@ -173,25 +148,25 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
std::vector<ElemType> minG2(maxG1.size());
for (size_t k = 0; k < tree->Bound().Dim(); k++)
{
minG1[k] = maxG1[k] = tree->LocalDataset().col(sorted[0].n)[k];
minG2[k] = maxG2[k] = tree->LocalDataset().col(
sorted[sorted.size() - 1].n)[k];
minG1[k] = maxG1[k] = tree->Dataset().col(tree->Point(sorted[0].n))[k];
minG2[k] = maxG2[k] = tree->Dataset().col(
tree->Point(sorted[sorted.size() - 1].n))[k];
for (size_t l = 1; l < tree->Count() - 1; l++)
{
if (l < cutOff)
{
if (tree->LocalDataset().col(sorted[l].n)[k] < minG1[k])
minG1[k] = tree->LocalDataset().col(sorted[l].n)[k];
else if (tree->LocalDataset().col(sorted[l].n)[k] > maxG1[k])
maxG1[k] = tree->LocalDataset().col(sorted[l].n)[k];
if (tree->Dataset().col(tree->Point(sorted[l].n))[k] < minG1[k])
minG1[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k];
else if (tree->Dataset().col(tree->Point(sorted[l].n))[k] > maxG1[k])
maxG1[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k];
}
else
{
if (tree->LocalDataset().col(sorted[l].n)[k] < minG2[k])
minG2[k] = tree->LocalDataset().col(sorted[l].n)[k];
else if (tree->LocalDataset().col(sorted[l].n)[k] > maxG2[k])
maxG2[k] = tree->LocalDataset().col(sorted[l].n)[k];
if (tree->Dataset().col(tree->Point(sorted[l].n))[k] < minG2[k])
minG2[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k];
else if (tree->Dataset().col(tree->Point(sorted[l].n))[k] > maxG2[k])
maxG2[k] = tree->Dataset().col(tree->Point(sorted[l].n))[k];
}
}
}
@@ -239,14 +214,16 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
std::vector<sortStruct<ElemType>> sorted(tree->Count());
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->LocalDataset().col(i)[bestAxis];
sorted[i].d = tree->Dataset().col(tree->Point(i))[bestAxis];
sorted[i].n = i;
}
std::sort(sorted.begin(), sorted.end(), structComp<ElemType>);
TreeType* treeOne = new TreeType(tree->Parent(), NormalNodeMaxNumChildren());
TreeType* treeTwo = new TreeType(tree->Parent(), NormalNodeMaxNumChildren());
TreeType* treeOne = new TreeType(tree->Parent(),
tree->AuxiliaryInfo().NormalNodeMaxNumChildren());
TreeType* treeTwo = new TreeType(tree->Parent(),
tree->AuxiliaryInfo().NormalNodeMaxNumChildren());
// The leaf nodes should never have any overlap introduced by the above method
// since a split axis is chosen and then points are assigned based on their
@@ -256,9 +233,9 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
for (size_t i = 0; i < tree->Count(); i++)
{
if (i < bestAreaIndexOnBestAxis + tree->MinLeafSize())
treeOne->InsertPoint(tree->Points()[sorted[i].n]);
treeOne->InsertPoint(tree->Point(sorted[i].n));
else
treeTwo->InsertPoint(tree->Points()[sorted[i].n]);
treeTwo->InsertPoint(tree->Point(sorted[i].n));
}
}
else
@@ -266,9 +243,9 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
for (size_t i = 0; i < tree->Count(); i++)
{
if (i < bestOverlapIndexOnBestAxis + tree->MinLeafSize())
treeOne->InsertPoint(tree->Points()[sorted[i].n]);
treeOne->InsertPoint(tree->Point(sorted[i].n));
else
treeTwo->InsertPoint(tree->Points()[sorted[i].n]);
treeTwo->InsertPoint(tree->Point(sorted[i].n));
}
}
@@ -277,27 +254,27 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
size_t index = par->NumChildren();
for (size_t i = 0; i < par->NumChildren(); i++)
{
if (par->Children()[i] == tree)
if (par->children[i] == tree)
{
index = i;
break;
}
}
assert(index != par->NumChildren());
par->Children()[index] = treeOne;
par->Children()[par->NumChildren()++] = treeTwo;
par->children[index] = treeOne;
par->children[par->NumChildren()++] = treeTwo;
// We now update the split history of each new node.
treeOne->Split().SplitHistory().history[bestAxis] = true;
treeOne->Split().SplitHistory().lastDimension = bestAxis;
treeTwo->Split().SplitHistory().history[bestAxis] = true;
treeTwo->Split().SplitHistory().lastDimension = bestAxis;
treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
// We only add one at a time, so we should only need to test for equality just
// in case, we use an assert.
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
if (par->NumChildren() == par->MaxNumChildren() + 1)
par->Split().SplitNonLeafNode(par, relevels);
XTreeSplit::SplitNonLeafNode(par,relevels);
assert(treeOne->Parent()->NumChildren() <=
treeOne->Parent()->MaxNumChildren());
@@ -319,8 +296,7 @@ void XTreeSplit<TreeType>::SplitLeafNode(TreeType* tree,
* higher up the tree because they were already updated if necessary.
*/
template<typename TreeType>
bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<bool>& relevels)
bool XTreeSplit::SplitNonLeafNode(TreeType *tree,std::vector<bool>& relevels)
{
// Convenience typedef.
typedef typename TreeType::ElemType ElemType;
@@ -336,8 +312,8 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
copy->Parent() = tree;
tree->NumChildren() = 0;
tree->NullifyData();
tree->Children()[(tree->NumChildren())++] = copy;
copy->Split().SplitNonLeafNode(copy, relevels);
tree->children[(tree->NumChildren())++] = copy;
XTreeSplit::SplitNonLeafNode(copy,relevels);
return true;
}
@@ -352,7 +328,8 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<bool> axes(tree->Bound().Dim());
std::vector<int> dimensionsLastUsed(tree->NumChildren());
for (size_t i = 0; i < tree->NumChildren(); i++)
dimensionsLastUsed[i] = tree->Child(i).Split().SplitHistory().lastDimension;
dimensionsLastUsed[i] =
tree->Child(i).AuxiliaryInfo().SplitHistory().lastDimension;
std::sort(dimensionsLastUsed.begin(), dimensionsLastUsed.end());
size_t lastDim = dimensionsLastUsed[dimensionsLastUsed.size()/2];
@@ -363,7 +340,8 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
{
axes[i] = true;
for (size_t j = 0; j < tree->NumChildren(); j++)
axes[i] = axes[i] & tree->Child(j).Split().SplitHistory().history[i];
axes[i] = axes[i] &
tree->Child(j).AuxiliaryInfo().SplitHistory().history[i];
if (axes[i] == true)
{
minOverlapSplitDimension = i;
@@ -376,7 +354,8 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
{
axes[i] = true;
for (size_t j = 0; j < tree->NumChildren(); j++)
axes[i] = axes[i] & tree->Child(j).Split().SplitHistory().history[i];
axes[i] = axes[i] &
tree->Child(j).AuxiliaryInfo().SplitHistory().history[i];
if (axes[i] == true)
{
minOverlapSplitDimension = i;
@@ -409,7 +388,7 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<sortStruct<ElemType>> sorted(tree->NumChildren());
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Children()[i]->Bound()[j].Lo();
sorted[i].d = tree->Child(i).Bound()[j].Lo();
sorted[i].n = i;
}
@@ -443,27 +422,27 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<ElemType> minG2(maxG1.size());
for (size_t k = 0; k < tree->Bound().Dim(); k++)
{
minG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Lo();
maxG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Hi();
minG1[k] = tree->Child(sorted[0].n).Bound()[k].Lo();
maxG1[k] = tree->Child(sorted[0].n).Bound()[k].Hi();
minG2[k] =
tree->Children()[sorted[sorted.size() - 1].n]->Bound()[k].Lo();
tree->Child(sorted[sorted.size() - 1].n).Bound()[k].Lo();
maxG2[k] =
tree->Children()[sorted[sorted.size() - 1].n]->Bound()[k].Hi();
tree->Child(sorted[sorted.size() - 1].n).Bound()[k].Hi();
for (size_t l = 1; l < tree->NumChildren() - 1; l++)
{
if (l < cutOff)
{
if (tree->Children()[sorted[l].n]->Bound()[k].Lo() < minG1[k])
minG1[k] = tree->Children()[sorted[l].n]->Bound()[k].Lo();
else if (tree->Children()[sorted[l].n]->Bound()[k].Hi() > maxG1[k])
maxG1[k] = tree->Children()[sorted[l].n]->Bound()[k].Hi();
if (tree->Child(sorted[l].n).Bound()[k].Lo() < minG1[k])
minG1[k] = tree->Child(sorted[l].n).Bound()[k].Lo();
else if (tree->Child(sorted[l].n).Bound()[k].Hi() > maxG1[k])
maxG1[k] = tree->Child(sorted[l].n).Bound()[k].Hi();
}
else
{
if (tree->Children()[sorted[l].n]->Bound()[k].Lo() < minG2[k])
minG2[k] = tree->Children()[sorted[l].n]->Bound()[k].Lo();
else if (tree->Children()[sorted[l].n]->Bound()[k].Hi() > maxG2[k])
maxG2[k] = tree->Children()[sorted[l].n]->Bound()[k].Hi();
if (tree->Child(sorted[l].n).Bound()[k].Lo() < minG2[k])
minG2[k] = tree->Child(sorted[l].n).Bound()[k].Lo();
else if (tree->Child(sorted[l].n).Bound()[k].Hi() > maxG2[k])
maxG2[k] = tree->Child(sorted[l].n).Bound()[k].Hi();
}
}
}
@@ -540,7 +519,7 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<sortStruct<ElemType>> sorted(tree->NumChildren());
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Children()[i]->Bound()[j].Hi();
sorted[i].d = tree->Child(i).Bound()[j].Hi();
sorted[i].n = i;
}
@@ -574,27 +553,25 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
std::vector<ElemType> minG2(maxG1.size());
for (size_t k = 0; k < tree->Bound().Dim(); k++)
{
minG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Lo();
maxG1[k] = tree->Children()[sorted[0].n]->Bound()[k].Hi();
minG2[k] =
tree->Children()[sorted[sorted.size() - 1].n]->Bound()[k].Lo();
maxG2[k] =
tree->Children()[sorted[sorted.size() - 1].n]->Bound()[k].Hi();
minG1[k] = tree->Child(sorted[0].n).Bound()[k].Lo();
maxG1[k] = tree->Child(sorted[0].n).Bound()[k].Hi();
minG2[k] = tree->Child(sorted[sorted.size() - 1].n).Bound()[k].Lo();
maxG2[k] = tree->Child(sorted[sorted.size() - 1].n).Bound()[k].Hi();
for (size_t l = 1; l < tree->NumChildren() - 1; l++)
{
if (l < cutOff)
{
if (tree->Children()[sorted[l].n]->Bound()[k].Lo() < minG1[k])
minG1[k] = tree->Children()[sorted[l].n]->Bound()[k].Lo();
else if (tree->Children()[sorted[l].n]->Bound()[k].Hi() > maxG1[k])
maxG1[k] = tree->Children()[sorted[l].n]->Bound()[k].Hi();
if (tree->Child(sorted[l].n).Bound()[k].Lo() < minG1[k])
minG1[k] = tree->Child(sorted[l].n).Bound()[k].Lo();
else if (tree->Child(sorted[l].n).Bound()[k].Hi() > maxG1[k])
maxG1[k] = tree->Child(sorted[l].n).Bound()[k].Hi();
}
else
{
if (tree->Children()[sorted[l].n]->Bound()[k].Lo() < minG2[k])
minG2[k] = tree->Children()[sorted[l].n]->Bound()[k].Lo();
else if (tree->Children()[sorted[l].n]->Bound()[k].Hi() > maxG2[k])
maxG2[k] = tree->Children()[sorted[l].n]->Bound()[k].Hi();
if (tree->Child(sorted[l].n).Bound()[k].Lo() < minG2[k])
minG2[k] = tree->Child(sorted[l].n).Bound()[k].Lo();
else if (tree->Child(sorted[l].n).Bound()[k].Hi() > maxG2[k])
maxG2[k] = tree->Child(sorted[l].n).Bound()[k].Hi();
}
}
}
@@ -670,7 +647,7 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
{
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Children()[i]->Bound()[bestAxis].Lo();
sorted[i].d = tree->Child(i).Bound()[bestAxis].Lo();
sorted[i].n = i;
}
}
@@ -678,7 +655,7 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
{
for (size_t i = 0; i < sorted.size(); i++)
{
sorted[i].d = tree->Children()[i]->Bound()[bestAxis].Hi();
sorted[i].d = tree->Child(i).Bound()[bestAxis].Hi();
sorted[i].n = i;
}
}
@@ -697,9 +674,9 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
for (size_t i = 0; i < tree->NumChildren(); i++)
{
if (i < bestAreaIndexOnBestAxis + tree->MinNumChildren())
InsertNodeIntoTree(treeOne, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeOne, tree->children[sorted[i].n]);
else
InsertNodeIntoTree(treeTwo, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeTwo, tree->children[sorted[i].n]);
}
}
else
@@ -712,9 +689,9 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
for (size_t i = 0; i < tree->NumChildren(); i++)
{
if (i < bestOverlapIndexOnBestAxis + tree->MinNumChildren())
InsertNodeIntoTree(treeOne, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeOne, tree->children[sorted[i].n]);
else
InsertNodeIntoTree(treeTwo, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeTwo, tree->children[sorted[i].n]);
}
}
else
@@ -735,7 +712,7 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
{
for (size_t i = 0; i < sorted2.size(); i++)
{
sorted2[i].d = tree->Children()[i]->Bound()[bestAxis].Hi();
sorted2[i].d = tree->Child(i).Bound()[bestAxis].Hi();
sorted2[i].n = i;
}
}
@@ -743,7 +720,7 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
{
for (size_t i = 0; i < sorted2.size(); i++)
{
sorted2[i].d = tree->Children()[i]->Bound()[bestAxis].Lo();
sorted2[i].d = tree->Child(i).Bound()[bestAxis].Lo();
sorted2[i].n = i;
}
}
@@ -751,9 +728,9 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
for (size_t i = 0; i < tree->NumChildren(); i++)
{
if (i < bestIndexMinOverlapSplit + tree->MinNumChildren())
InsertNodeIntoTree(treeOne, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeOne, tree->children[sorted[i].n]);
else
InsertNodeIntoTree(treeTwo, tree->Children()[sorted[i].n]);
InsertNodeIntoTree(treeTwo, tree->children[sorted[i].n]);
}
}
else
@@ -771,13 +748,13 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
(tree->Parent()->NumChildren() == 1))
{
// We make the root a supernode instead.
tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() +
NormalNodeMaxNumChildren();
tree->Parent()->Children().resize(tree->Parent()->MaxNumChildren() + 1);
tree->Parent()->MaxNumChildren() = tree->MaxNumChildren() +
tree->AuxiliaryInfo().NormalNodeMaxNumChildren();
tree->Parent()->children.resize(tree->Parent()->MaxNumChildren() + 1);
tree->Parent()->NumChildren() = tree->NumChildren();
for (size_t i = 0; i < tree->NumChildren(); i++)
{
tree->Parent()->Children()[i] = tree->Children()[i];
tree->Parent()->children[i] = tree->children[i];
tree->Child(i).Parent() = tree->Parent();
}
@@ -789,8 +766,9 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
}
// If we don't have to worry about the root, we just enlarge this node.
tree->MaxNumChildren() += NormalNodeMaxNumChildren();
tree->Children().resize(tree->MaxNumChildren() + 1);
tree->MaxNumChildren() +=
tree->AuxiliaryInfo().NormalNodeMaxNumChildren();
tree->children.resize(tree->MaxNumChildren() + 1);
for (size_t i = 0; i < tree->NumChildren(); i++)
tree->Child(i).Parent() = tree;
@@ -802,25 +780,25 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
}
// Update the split history of each child.
treeOne->Split().SplitHistory().history[bestAxis] = true;
treeOne->Split().SplitHistory().lastDimension = bestAxis;
treeTwo->Split().SplitHistory().history[bestAxis] = true;
treeTwo->Split().SplitHistory().lastDimension = bestAxis;
treeOne->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
treeOne->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
treeTwo->AuxiliaryInfo().SplitHistory().history[bestAxis] = true;
treeTwo->AuxiliaryInfo().SplitHistory().lastDimension = bestAxis;
// Remove this node and insert treeOne and treeTwo
TreeType* par = tree->Parent();
size_t index = 0;
for (size_t i = 0; i < par->NumChildren(); i++)
{
if (par->Children()[i] == tree)
if (par->children[i] == tree)
{
index = i;
break;
}
}
par->Children()[index] = treeOne;
par->Children()[par->NumChildren()++] = treeTwo;
par->children[index] = treeOne;
par->children[par->NumChildren()++] = treeTwo;
// we only add one at a time, so we should only need to test for equality
// just in case, we use an assert.
@@ -831,16 +809,14 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
assert(par->NumChildren() <= par->MaxNumChildren() + 1);
if (par->NumChildren() == par->MaxNumChildren() + 1)
{
par->Split().SplitNonLeafNode(par, relevels);
}
XTreeSplit::SplitNonLeafNode(par,relevels);
// We have to update the children of each of these new nodes so that they
// record the correct parent.
for (size_t i = 0; i < treeOne->NumChildren(); i++)
treeOne->Children()[i]->Parent() = treeOne;
treeOne->Child(i).Parent() = treeOne;
for (size_t i = 0; i < treeTwo->NumChildren(); i++)
treeTwo->Children()[i]->Parent() = treeTwo;
treeTwo->Child(i).Parent() = treeTwo;
assert(treeOne->Parent()->NumChildren() <=
treeOne->Parent()->MaxNumChildren());
@@ -861,27 +837,14 @@ bool XTreeSplit<TreeType>::SplitNonLeafNode(TreeType* tree,
* numberOfChildren.
*/
template<typename TreeType>
void XTreeSplit<TreeType>::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode)
void XTreeSplit::InsertNodeIntoTree(TreeType* destTree, TreeType* srcNode)
{
destTree->Bound() |= srcNode->Bound();
destTree->Children()[destTree->NumChildren()] = srcNode;
destTree->numDescendants += srcNode->numDescendants;
destTree->children[destTree->NumChildren()] = srcNode;
destTree->NumChildren()++;
}
/**
* Serialize the split.
*/
template<typename TreeType>
template<typename Archive>
void XTreeSplit<TreeType>::Serialize(Archive& ar,const unsigned int /* version */)
{
using data::CreateNVP;
ar & CreateNVP(normalNodeMaxNumChildren, "normalNodeMaxNumChildren");
ar & CreateNVP(splitHistory, "splitHistory");
}
} // namespace tree
} // namespace mlpack
@@ -4,6 +4,7 @@ set(SOURCES
random_init.hpp
random_acol_init.hpp
average_init.hpp
given_init.hpp
)
# Add directory name to sources.
@@ -0,0 +1,75 @@
/**
* @file given_initialization.hpp
* @author Ryan Curtin
*
* Initialization rule for alternating matrix factorization (AMF). This simple
* initialization is performed by assigning a given matrix to W and H.
*/
#ifndef MLPACK_METHODS_AMF_INIT_RULES_GIVEN_INIT_HPP
#define MLPACK_METHODS_AMF_INIT_RULES_GIVEN_INIT_HPP
#include <mlpack/core.hpp>
namespace mlpack {
namespace amf {
/**
* This initialization rule for AMF simply fills the W and H matrices with the
* matrices given to the constructor of this object. Note that this object does
* not use std::move() during the Initialize() method, so it can be reused for
* multiple AMF objects, but will incur copies of the W and H matrices.
*/
class GivenInitialization
{
public:
// Empty constructor required for the InitializeRule template.
GivenInitialization() { }
// Initialize the GivenInitialization object with the given matrices.
GivenInitialization(const arma::mat& w, const arma::mat& h) : w(w), h(h) { }
// Initialize the GivenInitialization object, taking control of the given
// matrices.
GivenInitialization(const arma::mat&& w, const arma::mat&& h) :
w(std::move(w)),
h(std::move(h))
{ }
/**
* Fill W and H with random uniform noise.
*
* @param V Input matrix.
* @param r Rank of decomposition.
* @param W W matrix, to be filled with random noise.
* @param H H matrix, to be filled with random noise.
*/
template<typename MatType>
inline void Initialize(const MatType& /* V */,
const size_t /* r */,
arma::mat& W,
arma::mat& H)
{
// Initialize to the given matrices.
W = w;
H = h;
}
//! Serialize the object (in this case, there is nothing to serialize).
template<typename Archive>
void Serialize(Archive& ar, const unsigned int /* version */)
{
ar & data::CreateNVP(w, "w");
ar & data::CreateNVP(h, "h");
}
private:
//! The W matrix for initialization.
arma::mat w;
//! The H matrix for initialization.
arma::mat h;
};
} // namespace amf
} // namespace mlpack
#endif
+5 -8
View File
@@ -5,15 +5,9 @@
* This file computes the approximate nearest-neighbors using 2-stable
* Locality-sensitive Hashing.
*/
#include <time.h>
#include <mlpack/core.hpp>
#include <mlpack/core/metrics/lmetric.hpp>
#include <string>
#include <fstream>
#include <iostream>
#include "lsh_search.hpp"
using namespace std;
@@ -64,6 +58,8 @@ PARAM_INT("tables", "The number of hash tables to be used.", "L", 30);
PARAM_DOUBLE("hash_width", "The hash width for the first-level hashing in the "
"LSH preprocessing. By default, the LSH class automatically estimates a "
"hash width for its use.", "H", 0.0);
PARAM_INT("num_probes", "Number of additional probes for Multiprobe LSH;"
" if 0, traditional LSH is used.", "T", 0);
PARAM_INT("second_hash_size", "The size of the second level hash table.", "S",
99901);
PARAM_INT("bucket_size", "The maximum size of a bucket in the second level "
@@ -137,6 +133,7 @@ int main(int argc, char *argv[])
const size_t numProj = CLI::GetParam<int>("projections");
const size_t numTables = CLI::GetParam<int>("tables");
const double hashWidth = CLI::GetParam<double>("hash_width");
const size_t numProbes = (size_t) CLI::GetParam<int>("num_probes");
arma::Mat<size_t> neighbors;
arma::mat distances;
@@ -180,11 +177,11 @@ int main(int argc, char *argv[])
Log::Info << "Loaded query data from '" << queryFile << "' ("
<< queryData.n_rows << " x " << queryData.n_cols << ")." << endl;
}
allkann.Search(queryData, k, neighbors, distances);
allkann.Search(queryData, k, neighbors, distances, 0, numProbes);
}
else
{
allkann.Search(k, neighbors, distances);
allkann.Search(k, neighbors, distances, 0, numProbes);
}
}
+82 -4
View File
@@ -19,6 +19,21 @@
* organization={ACM}
* }
*
* Additionally, the class implements Multiprobe LSH, which improves
* approximation results during the search for approximate nearest neighbors.
* The Multiprobe LSH algorithm was presented in the paper:
*
* @inproceedings{Lv2007multiprobe,
* tile={Multi-probe LSH: efficient indexing for high-dimensional similarity
* search},
* author={Lv, Qin and Josephson, William and Wang, Zhe and Charikar, Moses and
* Li, Kai},
* booktitle={Proceedings of the 33rd international conference on Very large
* data bases},
* year={2007},
* pages={950--961}
* }
*
*/
#ifndef MLPACK_METHODS_NEIGHBOR_SEARCH_LSH_SEARCH_HPP
#define MLPACK_METHODS_NEIGHBOR_SEARCH_LSH_SEARCH_HPP
@@ -159,12 +174,15 @@ class LSHSearch
* available without having to build hashing for every table size.
* By default, this is set to zero in which case all tables are
* considered.
* @param T The number of additional probing bins to examine with multiprobe
* LSH. If T = 0, classic single-probe LSH is run (default).
*/
void Search(const arma::mat& querySet,
const size_t k,
arma::Mat<size_t>& resultingNeighbors,
arma::mat& distances,
const size_t numTablesToSearch = 0);
const size_t numTablesToSearch = 0,
const size_t T = 0);
/**
* Compute the nearest neighbors and store the output in the given matrices.
@@ -187,7 +205,8 @@ class LSHSearch
void Search(const size_t k,
arma::Mat<size_t>& resultingNeighbors,
arma::mat& distances,
const size_t numTablesToSearch = 0);
const size_t numTablesToSearch = 0,
size_t T = 0);
/**
@@ -266,12 +285,16 @@ class LSHSearch
* @param referenceIndices The list of neighbor candidates obtained from
* hashing the query into all the hash tables and eventually into
* multiple buckets of the second hash table.
* @param numTablesToSearch The number of tables to perform the search in. If
* 0, all tables are searched.
* @param T The number of additional probing bins for multiprobe LSH. If 0,
* single-probe is used.
*/
template<typename VecType>
void ReturnIndicesFromTable(const VecType& queryPoint,
arma::uvec& referenceIndices,
size_t numTablesToSearch);
size_t numTablesToSearch,
const size_t T) const;
/**
* This is a helper function that computes the distance of the query to the
@@ -330,6 +353,61 @@ class LSHSearch
const size_t neighbor,
const double distance) const;
/**
* This function implements the core idea behind Multiprobe LSH. It is called
* by ReturnIndicesFromTables when T > 0. Given a query's code and its
* projection location, GetAdditionalProbingBins will calculate the T most
* likely alternative bin codes (other than queryCode) where a query's
* neighbors might be found in.
*
* @param queryCode vector containing the numProj-dimensional query code.
* @param queryCodeNotFloored vector containing the projection location of the
* query.
* @param T number of additional probing bins.
* @param additionalProbingBins matrix. Each column will hold one additional
* bin.
*/
void GetAdditionalProbingBins(const arma::vec& queryCode,
const arma::vec& queryCodeNotFloored,
const size_t T,
arma::mat& additionalProbingBins) const;
/**
* Returns the score of a perturbation vector generated by perturbation set A.
* The score of a pertubation set (vector) is the sum of scores of the
* participating actions.
* @param A perturbation set to compute the score of.
* @param scores vector containing score of each perturbation.
*/
double PerturbationScore(const std::vector<bool>& A,
const arma::vec& scores) const;
/**
* Inline function used by GetAdditionalProbingBins. The vector shift operation
* replaces the largest element of a vector A with (largest element) + 1.
* Returns true if resulting vector is valid, otherwise false.
* @param A perturbation set to shift.
*/
bool PerturbationShift(std::vector<bool>& A) const;
/**
* Inline function used by GetAdditionalProbingBins. The vector expansion
* operation adds the element [1 + (largest_element)] to a vector A, where
* largest_element is the largest element of A. Returns true if resulting vector
* is valid, otherwise false.
* @param A perturbation set to expand.
*/
bool PerturbationExpand(std::vector<bool>& A) const;
/**
* Return true if perturbation set A is valid. A perturbation set is invalid if
* it contains two (or more) actions for the same dimension or dimensions that
* are larger than the queryCode's dimensions.
* @param A perturbation set to validate.
*/
bool PerturbationValid(const std::vector<bool>& A) const;
//! Reference dataset.
const arma::mat* referenceSet;
//! If true, we own the reference set.
+390 -43
View File
@@ -123,8 +123,9 @@ void LSHSearch<SortPolicy>::Train(const arma::mat& referenceSet,
if (hashWidth == 0.0) // The user has not provided any value.
{
const size_t numSamples = 25;
// Compute a heuristic hash width from the data.
for (size_t i = 0; i < 25; i++)
for (size_t i = 0; i < numSamples; i++)
{
size_t p1 = (size_t) math::RandInt(referenceSet.n_cols);
size_t p2 = (size_t) math::RandInt(referenceSet.n_cols);
@@ -133,7 +134,7 @@ void LSHSearch<SortPolicy>::Train(const arma::mat& referenceSet,
referenceSet.unsafe_col(p1), referenceSet.unsafe_col(p2)));
}
hashWidth /= 25;
hashWidth /= numSamples;
}
Log::Info << "Hash width chosen as: " << hashWidth << std::endl;
@@ -184,7 +185,8 @@ void LSHSearch<SortPolicy>::Train(const arma::mat& referenceSet,
}
// We will store the second hash vectors in this matrix; the second hash
// vector for table i will be held in row i.
// vector for table i will be held in row i. We have to use int and not
// size_t, otherwise negative numbers are cast to 0.
arma::Mat<size_t> secondHashVectors(numTables, referenceSet.n_cols);
for (size_t i = 0; i < numTables; i++)
@@ -207,15 +209,25 @@ void LSHSearch<SortPolicy>::Train(const arma::mat& referenceSet,
hashMat /= hashWidth;
// Step V: Putting the points in the 'secondHashTable' by hashing the key.
// Now we hash every key, point ID to its corresponding bucket.
secondHashVectors.row(i) = arma::conv_to<arma::Row<size_t>>::from(
secondHashWeights.t() * arma::floor(hashMat));
// Now we hash every key, point ID to its corresponding bucket. We must
// also normalize the hashes to the range [0, secondHashSize).
arma::rowvec unmodVector = secondHashWeights.t() * arma::floor(hashMat);
for (size_t j = 0; j < unmodVector.n_elem; ++j)
{
double shs = (double) secondHashSize; // Convenience cast.
if (unmodVector[j] >= 0.0)
{
secondHashVectors(i, j) = size_t(fmod(unmodVector[j], shs));
}
else
{
const double mod = fmod(-unmodVector[j], shs);
secondHashVectors(i, j) = (mod < 1.0) ? 0 : secondHashSize -
size_t(mod);
}
}
}
// Normalize hashes (take modulus with secondHashSize).
secondHashVectors.transform([secondHashSize](size_t val)
{ return val % secondHashSize; });
// Now, using the hash vectors for each table, count the number of rows we
// have in the second hash table.
arma::Row<size_t> secondHashBinCounts(secondHashSize, arma::fill::zeros);
@@ -424,12 +436,276 @@ void LSHSearch<SortPolicy>::BaseCase(const size_t queryIndex,
referenceIndex, distance);
}
}
template<typename SortPolicy>
inline force_inline
double LSHSearch<SortPolicy>::PerturbationScore(
const std::vector<bool>& A,
const arma::vec& scores) const
{
double score = 0.0;
for (size_t i = 0; i < A.size(); ++i)
if (A[i])
score += scores(i); // add scores of non-zero indices
return score;
}
template<typename SortPolicy>
inline force_inline
bool LSHSearch<SortPolicy>::PerturbationShift(std::vector<bool>& A) const
{
size_t maxPos = 0;
for (size_t i = 0; i < A.size(); ++i)
if (A[i] == 1) // marked true
maxPos=i;
if ( maxPos + 1 < A.size()) // otherwise, this is an invalid vector
{
A[maxPos] = 0;
A[maxPos + 1] = 1;
return true; // valid
}
return false; // invalid
}
template<typename SortPolicy>
inline force_inline
bool LSHSearch<SortPolicy>::PerturbationExpand(std::vector<bool>& A) const
{
// Find the last '1' in A
size_t maxPos = 0;
for (size_t i = 0; i < A.size(); ++i)
if (A[i]) // marked true
maxPos = i;
if (maxPos + 1 < A.size()) // otherwise, this is an invalid vector
{
A[maxPos + 1] = 1;
return true;
}
return false;
}
template<typename SortPolicy>
inline force_inline
bool LSHSearch<SortPolicy>::PerturbationValid(
const std::vector<bool>& A) const
{
// Use check to mark dimensions we have seen before in A. If a dimension is
// seen twice (or more), A is not a valid perturbation.
std::vector<bool> check(numProj);
if (A.size() > 2 * numProj)
return false; // This should never happen.
// Check that we only see each dimension once. If not, vector is not valid.
for (size_t i = 0; i < A.size(); ++i)
{
// Only check dimensions that were included.
if (!A[i])
continue;
// If dimesnion is unseen thus far, mark it as seen.
if (check[i % numProj] == false)
check[i % numProj] = true;
else
return false; // If dimension was seen before, set is not valid.
}
// If we didn't fail, set is valid.
return true;
}
// Compute additional probing bins for a query
template<typename SortPolicy>
void LSHSearch<SortPolicy>::GetAdditionalProbingBins(
const arma::vec& queryCode,
const arma::vec& queryCodeNotFloored,
const size_t T,
arma::mat& additionalProbingBins) const
{
// No additional bins requested. Our work is done.
if (T == 0)
return;
// Each column of additionalProbingBins is the code of a bin.
additionalProbingBins.set_size(numProj, T);
// Copy the query's code, then in the end we will add/subtract according
// to perturbations we calculated.
for (size_t c = 0; c < T; ++c)
additionalProbingBins.col(c) = queryCode;
// Calculate query point's projection position.
arma::mat projection = queryCodeNotFloored;
// Use projection to calculate query's distance from hash limits.
arma::vec limLow = projection - queryCode * hashWidth;
arma::vec limHigh = hashWidth - limLow;
// Calculate scores. score = distance^2.
arma::vec scores(2 * numProj);
scores.rows(0, numProj - 1) = arma::pow(limLow, 2);
scores.rows(numProj, (2 * numProj) - 1) = arma::pow(limHigh, 2);
// Actions vector describes what perturbation (-1/+1) corresponds to a score.
arma::Col<short int> actions(2 * numProj); // will be [-1 ... 1 ...]
actions.rows(0, numProj - 1) = // First numProj rows.
-1 * arma::ones< arma::Col<short int> > (numProj); // -1s
actions.rows(numProj, (2 * numProj) - 1) = // Last numProj rows.
arma::ones< arma::Col<short int> > (numProj); // 1s
// Acting dimension vector shows which coordinate to transform according to
// actions (actions are described by actions vector above).
arma::Col<size_t> positions(2 * numProj); // Will be [0 1 2 ... 0 1 2 ...].
positions.rows(0, numProj - 1) =
arma::linspace< arma::Col<size_t> >(0, numProj - 1, numProj);
positions.rows(numProj, 2 * numProj - 1) =
arma::linspace< arma::Col<size_t> >(0, numProj - 1, numProj);
// Special case: No need to create heap for 1 or 2 codes.
if (T <= 2)
{
// First, find location of minimum score, generate 1 perturbation vector,
// and add its code to additionalProbingBins column 0.
// Find location and value of smallest element of scores vector.
double minscore = scores[0];
size_t minloc = 0;
for (size_t s = 1; s < (2 * numProj); ++s)
{
if (minscore > scores[s])
{
minscore = scores[s];
minloc = s;
}
}
// Add or subtract 1 to dimension corresponding to minimum score.
additionalProbingBins(positions[minloc], 0) += actions[minloc];
if (T == 1)
return; // Done if asked for only 1 code.
// Now, find location of second smallest score and generate one more vector.
// The second perturbation vector still can't comprise of more than one
// change in the bin codes, because of the way perturbation vectors
// are generated: First we create the one with the smallest score (Ao) and
// then we either add 1 extra dimension to it (Ae) or shift it by one (As).
// Since As contains the second smallest score, and Ae contains both the
// smallest and the second smallest, it's obvious that score(Ae) >
// score(As). Therefore the second perturbation vector is ALWAYS the vector
// containing only the second-lowest scoring perturbation.
double minscore2 = scores[0];
size_t minloc2 = 0;
for (size_t s = 0; s < (2 * numProj); ++s) // here we can't start from 1
{
if (minscore2 > scores[s] && s != minloc) //second smallest
{
minscore2 = scores[s];
minloc2 = s;
}
}
// Add or subtract 1 to create second-lowest scoring vector.
additionalProbingBins(positions[minloc2], 1) += actions[minloc2];
return;
}
// General case: more than 2 perturbation vectors require use of minheap.
// Sort everything in increasing order.
arma::uvec sortidx = arma::sort_index(scores);
scores = scores(sortidx);
actions = actions(sortidx);
positions = positions(sortidx);
// Theory:
// A probing sequence is a sequence of T probing bins where a query's
// neighbors are most likely to be. Likelihood is dependent only on a bin's
// score, which is the sum of scores of all dimension-action pairs, so we
// need to calculate the T smallest sums of scores that are not conflicting.
//
// Method:
// Store each perturbation set (pair of (dimension, action)) in a
// std::vector. Create a minheap of scores, with each node pointing to its
// relevant perturbation set. Each perturbation set popped from the minheap
// is the next most likely perturbation set.
// Transform perturbation set to perturbation vector by setting the
// dimensions specified by the set to queryCode+action (action is {-1, 1}).
// Perturbation sets (A) mark with 1 the (score, action, dimension) positions
// included in a given perturbation vector. Other spaces are 0.
std::vector<bool> Ao(2 * numProj);
Ao[0] = 1; // Smallest vector includes only smallest score.
std::vector< std::vector<bool> > perturbationSets;
perturbationSets.push_back(Ao); // Storage of perturbation sets.
std::priority_queue<
std::pair<double, size_t>, // contents: pairs of (score, index)
std::vector< // container: vector of pairs
std::pair<double, size_t>
>,
std::greater< std::pair<double, size_t> > // comparator of pairs
> minHeap; // our minheap
// Start by adding the lowest scoring set to the minheap.
minHeap.push( std::make_pair(PerturbationScore(Ao, scores), 0) );
// Loop invariable: after pvec iterations, additionalProbingBins contains pvec
// valid codes of the lowest-scoring bins (bins most likely to contain
// neighbors of the query).
for (size_t pvec = 0; pvec < T; ++pvec)
{
std::vector<bool> Ai;
do
{
// Get the perturbation set corresponding to the minimum score.
Ai = perturbationSets[ minHeap.top().second ];
minHeap.pop(); // .top() returns, .pop() removes
// Shift operation on Ai (replace max with max+1).
std::vector<bool> As = Ai;
if (PerturbationShift(As) && PerturbationValid(As))
// Don't add invalid sets.
{
perturbationSets.push_back(As); // add shifted set to sets
minHeap.push(
std::make_pair(PerturbationScore(As, scores),
perturbationSets.size() - 1));
}
// Expand operation on Ai (add max+1 to set).
std::vector<bool> Ae = Ai;
if (PerturbationExpand(Ae) && PerturbationValid(Ae))
// Don't add invalid sets.
{
perturbationSets.push_back(Ae); // add expanded set to sets
minHeap.push(
std::make_pair(PerturbationScore(Ae, scores),
perturbationSets.size() - 1));
}
} while (!PerturbationValid(Ai));//Discard invalid perturbations
// Found valid perturbation set Ai. Construct perturbation vector from set.
for (size_t pos = 0; pos < Ai.size(); ++pos)
// If Ai[pos] is marked, add action to probing vector.
additionalProbingBins(positions(pos), pvec)
+= Ai[pos] ? actions(pos) : 0;
}
}
template<typename SortPolicy>
template<typename VecType>
void LSHSearch<SortPolicy>::ReturnIndicesFromTable(
const VecType& queryPoint,
arma::uvec& referenceIndices,
size_t numTablesToSearch)
size_t numTablesToSearch,
const size_t T) const
{
// Decide on the number of tables to look into.
if (numTablesToSearch == 0) // If no user input is given, search all.
@@ -447,29 +723,59 @@ void LSHSearch<SortPolicy>::ReturnIndicesFromTable(
// Compute the projection of the query in each table.
arma::mat allProjInTables(numProj, numTablesToSearch);
arma::mat queryCodesNotFloored(numProj, numTablesToSearch);
for (size_t i = 0; i < numTablesToSearch; i++)
//allProjInTables.unsafe_col(i) = projections[i].t() * queryPoint;
allProjInTables.unsafe_col(i) = projections.slice(i).t() * queryPoint;
allProjInTables += offsets.cols(0, numTablesToSearch - 1);
allProjInTables /= hashWidth;
queryCodesNotFloored.unsafe_col(i) = projections.slice(i).t() * queryPoint;
queryCodesNotFloored += offsets.cols(0, numTablesToSearch - 1);
allProjInTables = arma::floor(queryCodesNotFloored / hashWidth);
// Compute the hash value of each key of the query into a bucket of the
// 'secondHashTable' using the 'secondHashWeights'.
arma::rowvec hashVec = secondHashWeights.t() * arma::floor(allProjInTables);
// Use hashMat to store the primary probing codes and any additional codes
// from multiprobe LSH.
arma::Mat<size_t> hashMat;
hashMat.set_size(T + 1, numTablesToSearch);
for (size_t i = 0; i < hashVec.n_elem; i++)
hashVec[i] = (double) ((size_t) hashVec[i] % secondHashSize);
// Compute the primary hash value of each key of the query into a bucket of
// the secondHashTable using the secondHashWeights.
hashMat.row(0) = arma::conv_to<arma::Row<size_t>> // Floor by typecasting
::from(secondHashWeights.t() * allProjInTables);
// Mod to compute 2nd-level codes.
for (size_t i = 0; i < numTablesToSearch; i++)
hashMat(0, i) = (hashMat(0, i) % secondHashSize);
// Compute hash codes of additional probing bins.
if (T > 0)
{
for (size_t i = 0; i < numTablesToSearch; ++i)
{
// Construct this table's probing sequence of length T.
arma::mat additionalProbingBins;
GetAdditionalProbingBins(allProjInTables.unsafe_col(i),
queryCodesNotFloored.unsafe_col(i),
T,
additionalProbingBins);
// Map each probing bin to a bin in secondHashTable (just like we did for
// the primary hash table).
hashMat(arma::span(1, T), i) = // Compute code of rows 1:end of column i
arma::conv_to< arma::Col<size_t> >:: // floor by typecasting to size_t
from( secondHashWeights.t() * additionalProbingBins );
for (size_t p = 1; p < T + 1; ++p)
hashMat(p, i) = (hashMat(p, i) % secondHashSize);
}
}
Log::Assert(hashVec.n_elem == numTablesToSearch);
// Count number of points hashed in the same bucket as the query.
size_t maxNumPoints = 0;
for (size_t i = 0; i < numTablesToSearch; ++i)
{
const size_t hashInd = (size_t) hashVec[i];
const size_t tableRow = bucketRowInHashTable[hashInd];
if (tableRow != secondHashSize)
maxNumPoints += bucketContentSize[tableRow];
for (size_t p = 0; p < T + 1; ++p)
{
const size_t hashInd = hashMat(p, i); // find query's bucket
const size_t tableRow = bucketRowInHashTable[hashInd];
if (tableRow < secondHashSize)
maxNumPoints += bucketContentSize[tableRow]; // count bucket contents
}
}
// There are two ways to proceed here:
@@ -491,16 +797,19 @@ void LSHSearch<SortPolicy>::ReturnIndicesFromTable(
arma::Col<size_t> refPointsConsidered;
refPointsConsidered.zeros(referenceSet->n_cols);
for (long long int i = 0; i < numTablesToSearch; ++i)
for (size_t i = 0; i < numTablesToSearch; ++i) // for all tables
{
for (size_t p = 0; p < T + 1; ++p) // For entire probing sequence.
{
// get the sequence code
size_t hashInd = hashMat(p, i);
size_t tableRow = bucketRowInHashTable[hashInd];
const size_t hashInd = (size_t) hashVec[i];
const size_t tableRow = bucketRowInHashTable[hashInd];
// Pick the indices in the bucket corresponding to 'hashInd'.
if (tableRow != secondHashSize)
for (size_t j = 0; j < bucketContentSize[tableRow]; j++)
refPointsConsidered[secondHashTable[tableRow](j)]++;
if (tableRow < secondHashSize && bucketContentSize[tableRow] > 0)
// Pick the indices in the bucket corresponding to hashInd.
for (size_t j = 0; j < bucketContentSize[tableRow]; ++j)
refPointsConsidered[ secondHashTable[tableRow](j) ]++;
}
}
// Only keep reference points found in at least one bucket.
@@ -520,13 +829,16 @@ void LSHSearch<SortPolicy>::ReturnIndicesFromTable(
for (long long int i = 0; i < numTablesToSearch; ++i) // For all tables
{
const size_t hashInd = (size_t) hashVec[i]; // Find the query's bucket.
const size_t tableRow = bucketRowInHashTable[hashInd];
for (size_t p = 0; p < T + 1; ++p)
{
const size_t hashInd = hashMat(p, i); // Find the query's bucket.
const size_t tableRow = bucketRowInHashTable[hashInd];
// Store all secondHashTable points in the candidates set.
if (tableRow != secondHashSize)
for (size_t j = 0; j < bucketContentSize[tableRow]; ++j)
refPointsConsideredSmall(start++) = secondHashTable[tableRow][j];
if (tableRow < secondHashSize)
// Store all secondHashTable points in the candidates set.
for (size_t j = 0; j < bucketContentSize[tableRow]; ++j)
refPointsConsideredSmall(start++) = secondHashTable[tableRow](j);
}
}
// Keep only one copy of each candidate.
@@ -541,7 +853,8 @@ void LSHSearch<SortPolicy>::Search(const arma::mat& querySet,
const size_t k,
arma::Mat<size_t>& resultingNeighbors,
arma::mat& distances,
const size_t numTablesToSearch)
const size_t numTablesToSearch,
const size_t T)
{
// Ensure the dimensionality of the query set is correct.
if (querySet.n_rows != referenceSet->n_rows)
@@ -572,6 +885,22 @@ void LSHSearch<SortPolicy>::Search(const arma::mat& querySet,
if (k == 0)
return;
// If the user requested more than the available number of additional probing
// bins, set Teffective to maximum T. Maximum T is 2^numProj - 1
size_t Teffective = T;
if (T > ((size_t) ((1 << numProj) - 1)))
{
Teffective = (1 << numProj) - 1;
Log::Warn << "Requested " << T << " additional bins are more than "
<< "theoretical maximum. Using " << Teffective << " instead."
<< std::endl;
}
// If the user set multiprobe, log it
if (Teffective > 0)
Log::Info << "Running multiprobe LSH with " << Teffective
<<" additional probing bins per table per query." << std::endl;
size_t avgIndicesReturned = 0;
Timer::Start("computing_neighbors");
@@ -591,7 +920,8 @@ void LSHSearch<SortPolicy>::Search(const arma::mat& querySet,
// Hash every query into every hash table and eventually into the
// 'secondHashTable' to obtain the neighbor candidates.
arma::uvec refIndices;
ReturnIndicesFromTable(querySet.col(i), refIndices, numTablesToSearch);
ReturnIndicesFromTable(querySet.col(i), refIndices, numTablesToSearch,
Teffective);
// An informative book-keeping for the number of neighbor candidates
// returned on average.
@@ -628,7 +958,8 @@ void LSHSearch<SortPolicy>::
Search(const size_t k,
arma::Mat<size_t>& resultingNeighbors,
arma::mat& distances,
const size_t numTablesToSearch)
const size_t numTablesToSearch,
size_t T)
{
// This is monochromatic search; the query set is the reference set.
resultingNeighbors.set_size(k, referenceSet->n_cols);
@@ -636,7 +967,22 @@ Search(const size_t k,
distances.fill(SortPolicy::WorstDistance());
resultingNeighbors.fill(referenceSet->n_cols);
// If the user requested more than the available number of additional probing
// bins, set Teffective to maximum T. Maximum T is 2^numProj - 1
size_t Teffective = T;
if (T > ((size_t) ((1 << numProj) - 1)))
{
Teffective = (1 << numProj) - 1;
Log::Warn << "Requested " << T << " additional bins are more than "
<< "theoretical maximum. Using " << Teffective << " instead."
<< std::endl;
}
// If the user set multiprobe, log it
if (T > 0)
Log::Info << "Running multiprobe LSH with " << Teffective <<
" additional probing bins per table per query."<< std::endl;
size_t avgIndicesReturned = 0;
Timer::Start("computing_neighbors");
@@ -655,7 +1001,8 @@ Search(const size_t k,
// Hash every query into every hash table and eventually into the
// 'secondHashTable' to obtain the neighbor candidates.
arma::uvec refIndices;
ReturnIndicesFromTable(referenceSet->col(i), refIndices, numTablesToSearch);
ReturnIndicesFromTable(referenceSet->col(i), refIndices, numTablesToSearch,
Teffective);
// An informative book-keeping for the number of neighbor candidates
// returned on average. Make atomic to avoid race conditions when multiple
@@ -62,7 +62,7 @@ PARAM_INT("k", "Number of furthest neighbors to find.", "k", 0);
// The user may specify the type of tree to use, and a few pararmeters for tree
// building.
PARAM_STRING("tree_type", "Type of tree to use: 'kd', 'cover', 'r', 'r-star', "
"'x', 'ball'.", "t", "kd");
"'x', 'ball', 'hilbert-r'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building.", "l", 20);
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
"random orthogonal basis.", "R");
@@ -72,6 +72,12 @@ PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
PARAM_FLAG("naive", "If true, O(n^2) naive mode is used for computation.", "N");
PARAM_FLAG("single_mode", "If true, single-tree search is used (as opposed to "
"dual-tree search).", "s");
PARAM_DOUBLE("epsilon", "If specified, will do approximate furthest neighbor "
"search with given relative error. Must be in the range [0,1).", "e", 0);
PARAM_DOUBLE("percentage", "If specified, will do approximate furthest neighbor"
" search. Must be in the range (0,1] (decimal form). Resultant neighbors "
"will be at least (p*100) % of the distance as the true furthest neighbor.",
"p", 1);
// Convenience typedef.
typedef NSModel<FurthestNeighborSort> KFNModel;
@@ -138,6 +144,24 @@ int main(int argc, char *argv[])
Log::Fatal << "Invalid leaf size: " << lsInt << ". Must be greater than 0."
<< endl;
// Sanity check on epsilon.
double epsilon = CLI::GetParam<double>("epsilon");
if (epsilon < 0 || epsilon >= 1)
Log::Fatal << "Invalid epsilon: " << epsilon << ". Must be in the range "
<< "[0,1)." << endl;
// Sanity check on percentage.
const double percentage = CLI::GetParam<double>("percentage");
if (percentage <= 0 || percentage > 1)
Log::Fatal << "Invalid percentage: " << percentage << ". Must be in the "
<< "range (0,1] (decimal form)." << endl;
if (CLI::HasParam("percentage") && CLI::HasParam("epsilon"))
Log::Fatal << "Cannot provide both epsilon and percentage." << endl;
if (CLI::HasParam("percentage"))
epsilon = 1 - percentage;
// We either have to load the reference data, or we have to load the model.
NSModel<FurthestNeighborSort> kfn;
const bool naive = CLI::HasParam("naive");
@@ -162,9 +186,11 @@ int main(int argc, char *argv[])
tree = KFNModel::BALL_TREE;
else if (treeType == "x")
tree = KFNModel::X_TREE;
else if (treeType == "hilbert-r")
tree = KFNModel::HILBERT_R_TREE;
else
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
<< "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl;
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << endl;
kfn.TreeType() = tree;
kfn.RandomBasis() = randomBasis;
@@ -175,7 +201,8 @@ int main(int argc, char *argv[])
Log::Info << "Loaded reference data from '" << referenceFile << "' ("
<< referenceSet.n_rows << "x" << referenceSet.n_cols << ")." << endl;
kfn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode);
kfn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode,
epsilon);
}
else
{
@@ -191,6 +218,7 @@ int main(int argc, char *argv[])
kfn.SingleMode() = CLI::HasParam("single_mode");
kfn.Naive() = CLI::HasParam("naive");
kfn.LeafSize() = size_t(lsInt);
kfn.Epsilon() = epsilon;
}
// Perform search, if desired.
@@ -63,7 +63,7 @@ PARAM_INT("k", "Number of nearest neighbors to find.", "k", 0);
// The user may specify the type of tree to use, and a few parameters for tree
// building.
PARAM_STRING("tree_type", "Type of tree to use: 'kd', 'cover', 'r', 'r-star', "
"'x', 'ball'.", "t", "kd");
"'x', 'ball', 'hilbert-r'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building (used for kd-trees, R "
"trees, and R* trees).", "l", 20);
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
@@ -74,6 +74,8 @@ PARAM_INT("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
PARAM_FLAG("naive", "If true, O(n^2) naive mode is used for computation.", "N");
PARAM_FLAG("single_mode", "If true, single-tree search is used (as opposed to "
"dual-tree search).", "S");
PARAM_DOUBLE("epsilon", "If specified, will do approximate nearest neighbor "
"search with given relative error.", "e", 0);
// Convenience typedef.
typedef NSModel<NearestNeighborSort> KNNModel;
@@ -137,10 +139,14 @@ int main(int argc, char *argv[])
// Sanity check on leaf size.
const int lsInt = CLI::GetParam<int>("leaf_size");
if (lsInt < 1)
{
Log::Fatal << "Invalid leaf size: " << lsInt << ". Must be greater "
"than 0." << endl;
}
// Sanity check on epsilon.
const double epsilon = CLI::GetParam<double>("epsilon");
if (epsilon < 0)
Log::Fatal << "Invalid epsilon: " << epsilon << ". Must be non-negative. "
<< endl;
// We either have to load the reference data, or we have to load the model.
NSModel<NearestNeighborSort> knn;
@@ -166,9 +172,11 @@ int main(int argc, char *argv[])
tree = KNNModel::BALL_TREE;
else if (treeType == "x")
tree = KNNModel::X_TREE;
else if (treeType == "hilbert-r")
tree = KNNModel::HILBERT_R_TREE;
else
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
<< "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl;
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << endl;
knn.TreeType() = tree;
knn.RandomBasis() = randomBasis;
@@ -180,7 +188,8 @@ int main(int argc, char *argv[])
<< referenceSet.n_rows << " x " << referenceSet.n_cols << ")."
<< endl;
knn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode);
knn.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode,
epsilon);
}
else
{
@@ -196,6 +205,7 @@ int main(int argc, char *argv[])
knn.SingleMode() = CLI::HasParam("single_mode");
knn.Naive() = CLI::HasParam("naive");
knn.LeafSize() = size_t(lsInt);
knn.Epsilon() = epsilon;
}
// Perform search, if desired.
@@ -84,11 +84,13 @@ class NeighborSearch
* dual-tree search). This overrides singleMode (if it is set to true).
* @param singleMode If true, single-tree search will be used (as opposed to
* dual-tree search).
* @param epsilon Relative approximate error (non-negative).
* @param metric An optional instance of the MetricType class.
*/
NeighborSearch(const MatType& referenceSet,
const bool naive = false,
const bool singleMode = false,
const double epsilon = 0,
const MetricType metric = MetricType());
/**
@@ -108,11 +110,13 @@ class NeighborSearch
* dual-tree search). This overrides singleMode (if it is set to true).
* @param singleMode If true, single-tree search will be used (as opposed to
* dual-tree search).
* @param epsilon Relative approximate error (non-negative).
* @param metric An optional instance of the MetricType class.
*/
NeighborSearch(MatType&& referenceSet,
const bool naive = false,
const bool singleMode = false,
const double epsilon = 0,
const MetricType metric = MetricType());
/**
@@ -138,10 +142,12 @@ class NeighborSearch
* @param referenceSet Set of reference points corresponding to referenceTree.
* @param singleMode Whether single-tree computation should be used (as
* opposed to dual-tree computation).
* @param epsilon Relative approximate error (non-negative).
* @param metric Instantiated distance metric.
*/
NeighborSearch(Tree* referenceTree,
const bool singleMode = false,
const double epsilon = 0,
const MetricType metric = MetricType());
/**
@@ -152,10 +158,12 @@ class NeighborSearch
* @param naive Whether to use naive search.
* @param singleMode Whether single-tree computation should be used (as
* opposed to dual-tree computation).
* @param epsilon Relative approximate error (non-negative).
* @param metric Instantiated metric.
*/
NeighborSearch(const bool naive = false,
const bool singleMode = false,
const double epsilon = 0,
const MetricType metric = MetricType());
@@ -270,6 +278,11 @@ class NeighborSearch
//! Modify whether or not search is done in single-tree mode.
bool& SingleMode() { return singleMode; }
//! Access the relative error to be considered in approximate search.
double Epsilon() const { return epsilon; }
//! Modify the relative error to be considered in approximate search.
double& Epsilon() { return epsilon; }
//! Access the reference dataset.
const MatType& ReferenceSet() const { return *referenceSet; }
@@ -294,6 +307,8 @@ class NeighborSearch
bool naive;
//! Indicates if single-tree search is being used (as opposed to dual-tree).
bool singleMode;
//! Indicates the relative error to be considered in approximate search.
double epsilon;
//! Instantiation of metric.
MetricType metric;
@@ -75,6 +75,7 @@ NeighborSearch<SortPolicy, MetricType, MatType, TreeType, TraversalType>::
NeighborSearch(const MatType& referenceSetIn,
const bool naive,
const bool singleMode,
const double epsilon,
const MetricType metric) :
referenceTree(naive ? NULL :
BuildTree<MatType, Tree>(referenceSetIn, oldFromNewReferences)),
@@ -83,12 +84,14 @@ NeighborSearch(const MatType& referenceSetIn,
setOwner(false),
naive(naive),
singleMode(!naive && singleMode), // No single mode if naive.
epsilon(epsilon),
metric(metric),
baseCases(0),
scores(0),
treeNeedsReset(false)
{
// Nothing to do.
if (epsilon < 0)
throw std::invalid_argument("epsilon must be non-negative");
}
// Construct the object.
@@ -103,6 +106,7 @@ NeighborSearch<SortPolicy, MetricType, MatType, TreeType, TraversalType>::
NeighborSearch(MatType&& referenceSetIn,
const bool naive,
const bool singleMode,
const double epsilon,
const MetricType metric) :
referenceTree(naive ? NULL :
BuildTree<MatType, Tree>(std::move(referenceSetIn),
@@ -113,12 +117,14 @@ NeighborSearch(MatType&& referenceSetIn,
setOwner(naive),
naive(naive),
singleMode(!naive && singleMode),
epsilon(epsilon),
metric(metric),
baseCases(0),
scores(0),
treeNeedsReset(false)
{
// Nothing to do.
if (epsilon < 0)
throw std::invalid_argument("epsilon must be non-negative");
}
// Construct the object.
@@ -132,6 +138,7 @@ template<typename SortPolicy,
NeighborSearch<SortPolicy, MetricType, MatType, TreeType, TraversalType>::
NeighborSearch(Tree* referenceTree,
const bool singleMode,
const double epsilon,
const MetricType metric) :
referenceTree(referenceTree),
referenceSet(&referenceTree->Dataset()),
@@ -139,12 +146,14 @@ NeighborSearch(Tree* referenceTree,
setOwner(false),
naive(false),
singleMode(singleMode),
epsilon(epsilon),
metric(metric),
baseCases(0),
scores(0),
treeNeedsReset(false)
{
// Nothing else to initialize.
if (epsilon < 0)
throw std::invalid_argument("epsilon must be non-negative");
}
// Construct the object without a reference dataset.
@@ -158,6 +167,7 @@ template<typename SortPolicy,
NeighborSearch<SortPolicy, MetricType, MatType, TreeType, TraversalType>::
NeighborSearch(const bool naive,
const bool singleMode,
const double epsilon,
const MetricType metric) :
referenceTree(NULL),
referenceSet(new MatType()), // Empty matrix.
@@ -165,11 +175,14 @@ NeighborSearch<SortPolicy, MetricType, MatType, TreeType, TraversalType>::
setOwner(true),
naive(naive),
singleMode(singleMode),
epsilon(epsilon),
metric(metric),
baseCases(0),
scores(0),
treeNeedsReset(false)
{
if (epsilon < 0)
throw std::invalid_argument("epsilon must be non-negative");
// Build the tree on the empty dataset, if necessary.
if (!naive)
{
@@ -364,7 +377,8 @@ Search(const MatType& querySet,
if (naive)
{
// Create the helper object for the tree traversal.
RuleType rules(*referenceSet, querySet, *neighborPtr, *distancePtr, metric);
RuleType rules(*referenceSet, querySet, *neighborPtr, *distancePtr, metric,
epsilon);
// The naive brute-force traversal.
for (size_t i = 0; i < querySet.n_cols; ++i)
@@ -376,7 +390,8 @@ Search(const MatType& querySet,
else if (singleMode)
{
// Create the helper object for the tree traversal.
RuleType rules(*referenceSet, querySet, *neighborPtr, *distancePtr, metric);
RuleType rules(*referenceSet, querySet, *neighborPtr, *distancePtr, metric,
epsilon);
// Create the traverser.
typename Tree::template SingleTreeTraverser<RuleType> traverser(rules);
@@ -402,7 +417,7 @@ Search(const MatType& querySet,
// Create the helper object for the tree traversal.
RuleType rules(*referenceSet, queryTree->Dataset(), *neighborPtr,
*distancePtr, metric);
*distancePtr, metric, epsilon);
// Create the traverser.
TraversalType<RuleType> traverser(rules);
@@ -527,7 +542,8 @@ Search(Tree* queryTree,
// Create the helper object for the traversal.
typedef NeighborSearchRules<SortPolicy, MetricType, Tree> RuleType;
RuleType rules(*referenceSet, querySet, *neighborPtr, distances, metric);
RuleType rules(*referenceSet, querySet, *neighborPtr, distances, metric,
epsilon);
// Create the traverser.
TraversalType<RuleType> traverser(rules);
@@ -598,7 +614,7 @@ Search(const size_t k,
// Create the helper object for the traversal.
typedef NeighborSearchRules<SortPolicy, MetricType, Tree> RuleType;
RuleType rules(*referenceSet, *referenceSet, *neighborPtr, *distancePtr,
metric, true /* don't return the same point as nearest neighbor */);
metric, epsilon, true /* don't return the same point as nearest neighbor */);
if (naive)
{
@@ -22,6 +22,7 @@ class NeighborSearchRules
arma::Mat<size_t>& neighbors,
arma::mat& distances,
MetricType& metric,
const double epsilon = 0,
const bool sameSet = false);
/**
* Get the distance from the query point to the reference point.
@@ -120,6 +121,9 @@ class NeighborSearchRules
//! Denotes whether or not the reference and query sets are the same.
bool sameSet;
//! Relative error to be considered in approximate search.
const double epsilon;
//! The last query point BaseCase() was called with.
size_t lastQueryIndex;
//! The last reference point BaseCase() was called with.
@@ -1,8 +1,8 @@
/**
* @file nearest_neighbor_rules_impl.hpp
* @file neighbor_search_rules_impl.hpp
* @author Ryan Curtin
*
* Implementation of NearestNeighborRules.
* Implementation of NeighborSearchRules.
*/
#ifndef MLPACK_METHODS_NEIGHBOR_SEARCH_NEAREST_NEIGHBOR_RULES_IMPL_HPP
#define MLPACK_METHODS_NEIGHBOR_SEARCH_NEAREST_NEIGHBOR_RULES_IMPL_HPP
@@ -20,6 +20,7 @@ NeighborSearchRules<SortPolicy, MetricType, TreeType>::NeighborSearchRules(
arma::Mat<size_t>& neighbors,
arma::mat& distances,
MetricType& metric,
const double epsilon,
const bool sameSet) :
referenceSet(referenceSet),
querySet(querySet),
@@ -27,6 +28,7 @@ NeighborSearchRules<SortPolicy, MetricType, TreeType>::NeighborSearchRules(
distances(distances),
metric(metric),
sameSet(sameSet),
epsilon(epsilon),
lastQueryIndex(querySet.n_cols),
lastReferenceIndex(referenceSet.n_cols),
baseCases(0),
@@ -112,7 +114,8 @@ inline double NeighborSearchRules<SortPolicy, MetricType, TreeType>::Score(
}
// Compare against the best k'th distance for this query point so far.
const double bestDistance = distances(distances.n_rows - 1, queryIndex);
double bestDistance = distances(distances.n_rows - 1, queryIndex);
bestDistance = SortPolicy::Relax(bestDistance, epsilon);
return (SortPolicy::IsBetter(distance, bestDistance)) ? distance : DBL_MAX;
}
@@ -128,7 +131,8 @@ inline double NeighborSearchRules<SortPolicy, MetricType, TreeType>::Rescore(
return oldScore;
// Just check the score again against the distances.
const double bestDistance = distances(distances.n_rows - 1, queryIndex);
double bestDistance = distances(distances.n_rows - 1, queryIndex);
bestDistance = SortPolicy::Relax(bestDistance, epsilon);
return (SortPolicy::IsBetter(oldScore, bestDistance)) ? oldScore : DBL_MAX;
}
@@ -419,6 +423,8 @@ inline double NeighborSearchRules<SortPolicy, MetricType, TreeType>::
queryNode.Stat().SecondBound() = bestDistance;
queryNode.Stat().AuxBound() = auxDistance;
worstDistance = SortPolicy::Relax(worstDistance, epsilon);
if (SortPolicy::IsBetter(worstDistance, bestDistance))
return worstDistance;
else
@@ -59,17 +59,26 @@ struct NSModelName<FurthestNeighborSort>
class MonoSearchVisitor : public boost::static_visitor<void>
{
private:
//! Number of neighbors to search for.
const size_t k;
//! Result matrix for neighbors.
arma::Mat<size_t>& neighbors;
//! Result matrix for distances.
arma::mat& distances;
public:
//! Perform monochromatic nearest neighbor search.
template<typename NSType>
void operator()(NSType* ns) const;
//! Construct the MonoSearchVisitor object with the given parameters.
MonoSearchVisitor(const size_t k,
arma::Mat<size_t>& neighbors,
arma::mat& distances);
arma::mat& distances) :
k(k),
neighbors(neighbors),
distances(distances)
{};
};
/**
@@ -82,10 +91,15 @@ template<typename SortPolicy>
class BiSearchVisitor : public boost::static_visitor<void>
{
private:
//! The query set for the bichromatic search.
const arma::mat& querySet;
//! The number of neighbors to search for.
const size_t k;
//! The result matrix for neighbors.
arma::Mat<size_t>& neighbors;
//! The result matrix for distances.
arma::mat& distances;
//! The number of points in a leaf (for BinarySpaceTrees).
const size_t leafSize;
//! Bichromatic neighbor search on the given NSType considering the leafSize.
@@ -111,6 +125,7 @@ class BiSearchVisitor : public boost::static_visitor<void>
//! Bichromatic neighbor search on the given NSType specialized for BallTrees.
void operator()(NSTypeT<tree::BallTree>* ns) const;
//! Construct the BiSearchVisitor.
BiSearchVisitor(const arma::mat& querySet,
const size_t k,
arma::Mat<size_t>& neighbors,
@@ -128,7 +143,9 @@ template<typename SortPolicy>
class TrainVisitor : public boost::static_visitor<void>
{
private:
//! The reference set to use for training.
arma::mat&& referenceSet;
//! The leaf size, used only by BinarySpaceTree.
size_t leafSize;
//! Train on the given NSType considering the leafSize.
@@ -154,6 +171,8 @@ class TrainVisitor : public boost::static_visitor<void>
//! Train on the given NSType specialized for BallTrees.
void operator()(NSTypeT<tree::BallTree>* ns) const;
//! Construct the TrainVisitor object with the given reference set and leaf
//! size for BinarySpaceTrees.
TrainVisitor(arma::mat&& referenceSet, const size_t leafSize);
};
@@ -163,6 +182,7 @@ class TrainVisitor : public boost::static_visitor<void>
class SingleModeVisitor : public boost::static_visitor<bool&>
{
public:
//! Return whether or not single-tree search is enabled.
template<typename NSType>
bool& operator()(NSType* ns) const;
};
@@ -173,16 +193,29 @@ class SingleModeVisitor : public boost::static_visitor<bool&>
class NaiveVisitor : public boost::static_visitor<bool&>
{
public:
//! Return whether or not naive search is enabled.
template<typename NSType>
bool& operator()(NSType *ns) const;
};
/**
* EpsilonVisitor exposes the Epsilon method of the given NSType.
*/
class EpsilonVisitor : public boost::static_visitor<double&>
{
public:
//! Return epsilon, the approximation parameter.
template<typename NSType>
double& operator()(NSType *ns) const;
};
/**
* ReferenceSetVisitor exposes the referenceSet of the given NSType.
*/
class ReferenceSetVisitor : public boost::static_visitor<const arma::mat&>
{
public:
//! Return the reference set.
template<typename NSType>
const arma::mat& operator()(NSType *ns) const;
};
@@ -193,13 +226,18 @@ class ReferenceSetVisitor : public boost::static_visitor<const arma::mat&>
class DeleteVisitor : public boost::static_visitor<void>
{
public:
//! Delete the NSType object.
template<typename NSType>
void operator()(NSType *ns) const;
};
/**
* The NSModel class provides an easy way to serialize a model, abstracts away
* the different types of trees, and also reflects the NeighborSearch API.
* the different types of trees, and also reflects the NeighborSearch API. This
* class is meant to be used by the command-line mlpack_knn and mlpack_kfn
* programs, and thus does not have the same complete functionality and
* flexibility as the NeighborSearch class. So if you are using it outside of
* mlpack_knn and mlpack_kfn, be aware that it is limited!
*
* @tparam SortPolicy The sort policy for distances; see NearestNeighborSort.
*/
@@ -215,7 +253,8 @@ class NSModel
R_TREE,
R_STAR_TREE,
BALL_TREE,
X_TREE
X_TREE,
HILBERT_R_TREE
};
private:
@@ -225,8 +264,9 @@ class NSModel
//! For tree types that accept the maxLeafSize parameter.
size_t leafSize;
//! For random projections.
//! If true, random projections are used.
bool randomBasis;
//! This is the random projection matrix; only used if randomBasis is true.
arma::mat q;
/**
@@ -239,7 +279,8 @@ class NSModel
NSType<SortPolicy, tree::RTree>*,
NSType<SortPolicy, tree::RStarTree>*,
NSType<SortPolicy, tree::BallTree>*,
NSType<SortPolicy, tree::XTree>*> nSearch;
NSType<SortPolicy, tree::XTree>*,
NSType<SortPolicy, tree::HilbertRTree>*> nSearch;
public:
/**
@@ -266,6 +307,10 @@ class NSModel
bool Naive() const;
bool& Naive();
//! Expose Epsilon.
double Epsilon() const;
double& Epsilon();
//! Expose leafSize.
size_t LeafSize() const { return leafSize; }
size_t& LeafSize() { return leafSize; }
@@ -282,7 +327,8 @@ class NSModel
void BuildModel(arma::mat&& referenceSet,
const size_t leafSize,
const bool naive,
const bool singleMode);
const bool singleMode,
const double epsilon = 0);
//! Perform neighbor search. The query set will be reordered.
void Search(arma::mat&& querySet,
@@ -18,15 +18,6 @@
namespace mlpack {
namespace neighbor {
//! Save parameters for monochromatic neighbor search.
MonoSearchVisitor::MonoSearchVisitor(const size_t k,
arma::Mat<size_t>& neighbors,
arma::mat& distances) :
k(k),
neighbors(neighbors),
distances(distances)
{}
//! Monochromatic neighbor search on the given NSType instance.
template<typename NSType>
void MonoSearchVisitor::operator()(NSType *ns) const
@@ -185,6 +176,15 @@ bool& NaiveVisitor::operator()(NSType* ns) const
throw std::runtime_error("no neighbor search model initialized");
}
//! Expose the Epsilon method of the given NSType.
template<typename NSType>
double& EpsilonVisitor::operator()(NSType* ns) const
{
if (ns)
return ns->Epsilon();
throw std::runtime_error("no neighbor search model initialized");
}
//! Expose the referenceSet of the given NSType.
template<typename NSType>
const arma::mat& ReferenceSetVisitor::operator()(NSType* ns) const
@@ -293,12 +293,25 @@ bool& NSModel<SortPolicy>::Naive()
return boost::apply_visitor(NaiveVisitor(), nSearch);
}
template<typename SortPolicy>
double NSModel<SortPolicy>::Epsilon() const
{
return boost::apply_visitor(EpsilonVisitor(), nSearch);
}
template<typename SortPolicy>
double& NSModel<SortPolicy>::Epsilon()
{
return boost::apply_visitor(EpsilonVisitor(), nSearch);
}
//! Build the reference tree.
template<typename SortPolicy>
void NSModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
const size_t leafSize,
const bool naive,
const bool singleMode)
const bool singleMode,
const double epsilon)
{
// Initialize random basis if necessary.
if (randomBasis)
@@ -348,23 +361,30 @@ void NSModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
switch (treeType)
{
case KD_TREE:
nSearch = new NSType<SortPolicy, tree::KDTree>(naive, singleMode);
nSearch = new NSType<SortPolicy, tree::KDTree>(naive, singleMode,
epsilon);
break;
case COVER_TREE:
nSearch = new NSType<SortPolicy, tree::StandardCoverTree>(naive,
singleMode);
singleMode, epsilon);
break;
case R_TREE:
nSearch = new NSType<SortPolicy, tree::RTree>(naive, singleMode);
nSearch = new NSType<SortPolicy, tree::RTree>(naive, singleMode, epsilon);
break;
case R_STAR_TREE:
nSearch = new NSType<SortPolicy, tree::RStarTree>(naive, singleMode);
nSearch = new NSType<SortPolicy, tree::RStarTree>(naive, singleMode,
epsilon);
break;
case BALL_TREE:
nSearch = new NSType<SortPolicy, tree::BallTree>(naive, singleMode);
nSearch = new NSType<SortPolicy, tree::BallTree>(naive, singleMode,
epsilon);
break;
case X_TREE:
nSearch = new NSType<SortPolicy, tree::XTree>(naive, singleMode);
nSearch = new NSType<SortPolicy, tree::XTree>(naive, singleMode, epsilon);
break;
case HILBERT_R_TREE:
nSearch = new NSType<SortPolicy, tree::HilbertRTree>(naive, singleMode,
epsilon);
break;
}
@@ -389,13 +409,16 @@ void NSModel<SortPolicy>::Search(arma::mat&& querySet,
if (randomBasis)
querySet = q * querySet;
Log::Info << "Searching for " << k << " nearest neighbors with ";
Log::Info << "Searching for " << k << " neighbors with ";
if (!Naive() && !SingleMode())
Log::Info << "dual-tree " << TreeName() << " search..." << std::endl;
else if (!Naive())
Log::Info << "single-tree " << TreeName() << " search..." << std::endl;
else
Log::Info << "brute-force (naive) search..." << std::endl;
if (Epsilon() != 0 && !Naive())
Log::Info << "Maximum of " << Epsilon() * 100 << "% relative error."
<< std::endl;
BiSearchVisitor<SortPolicy> search(querySet, k, neighbors, distances,
leafSize);
@@ -408,13 +431,16 @@ void NSModel<SortPolicy>::Search(const size_t k,
arma::Mat<size_t>& neighbors,
arma::mat& distances)
{
Log::Info << "Searching for " << k << " nearest neighbors with ";
Log::Info << "Searching for " << k << " neighbors with ";
if (!Naive() && !SingleMode())
Log::Info << "dual-tree " << TreeName() << " search..." << std::endl;
else if (!Naive())
Log::Info << "single-tree " << TreeName() << " search..." << std::endl;
else
Log::Info << "brute-force (naive) search..." << std::endl;
if (Epsilon() != 0 && !Naive())
Log::Info << "Maximum of " << Epsilon() * 100 << "% relative error."
<< std::endl;
MonoSearchVisitor search(k, neighbors, distances);
boost::apply_visitor(search, nSearch);
@@ -438,6 +464,8 @@ std::string NSModel<SortPolicy>::TreeName() const
return "ball tree";
case X_TREE:
return "X tree";
case HILBERT_R_TREE:
return "Hilbert R tree";
default:
return "unknown tree";
}
@@ -1,5 +1,5 @@
/***
* @file nearest_neighbor_sort.cpp
* @file furthest_neighbor_sort.cpp
* @author Ryan Curtin
*
* Implementation of the simple FurthestNeighborSort policy class.
@@ -12,7 +12,7 @@ size_t FurthestNeighborSort::SortDistance(const arma::vec& list,
const arma::Col<size_t>& indices,
double newDistance)
{
// The first element in the list is the nearest neighbor. We only want to
// The first element in the list is the furthest neighbor. We only want to
// insert if the new distance is greater than the last element in the list.
if (newDistance < list[list.n_elem - 1])
return (size_t() - 1); // Do not insert.
@@ -145,6 +145,23 @@ class FurthestNeighborSort
*/
static inline double CombineWorst(const double a, const double b)
{ return std::max(a - b, 0.0); }
/**
* Return the given value relaxed.
*
* @param value Value to relax.
* @param epsilon Relative error (non-negative).
*
* @return double Value relaxed.
*/
static inline double Relax(const double value, const double epsilon)
{
if (value == 0)
return 0;
if (value == DBL_MAX || epsilon >= 1)
return DBL_MAX;
return (1 / (1 - epsilon)) * value;
}
};
} // namespace neighbor
@@ -150,6 +150,21 @@ class NearestNeighborSort
return DBL_MAX;
return a + b;
}
/**
* Return the given value relaxed.
*
* @param value Value to relax.
* @param epsilon Relative error (non-negative).
*
* @return double Value relaxed.
*/
static inline double Relax(const double value, const double epsilon)
{
if (value == DBL_MAX)
return DBL_MAX;
return (1 / (1 + epsilon)) * value;
}
};
} // namespace neighbor
@@ -68,7 +68,7 @@ int main(int argc, char** argv)
const double testRatio = CLI::GetParam<double>("test_ratio");
// Check on label parameters.
if (CLI::HasParam("input_labels"))
if (CLI::HasParam("input_labels_file"))
{
if (!CLI::HasParam("training_labels_file"))
{
@@ -70,7 +70,7 @@ PARAM_DOUBLE("min", "Lower bound in range.", "L", 0.0);
// The user may specify the type of tree to use, and a few parameters for tree
// building.
PARAM_STRING("tree_type", "Type of tree to use: 'kd', 'cover', 'r', 'r-star', "
"'x', 'ball'.", "t", "kd");
"'x', 'ball', 'hilbert-r'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building.", "l", 20);
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
"random orthogonal basis.", "R");
@@ -173,9 +173,11 @@ int main(int argc, char *argv[])
tree = RSModel::BALL_TREE;
else if (treeType == "x")
tree = RSModel::X_TREE;
else if (treeType == "hilbert-r")
tree = RSModel::HILBERT_R_TREE;
else
Log::Fatal << "Unknown tree type '" << treeType << "; valid choices are "
<< "'kd', 'cover', 'r', 'r-star', 'x' and 'ball'." << endl;
<< "'kd', 'cover', 'r', 'r-star', 'x', 'ball' and 'hilbert-r'." << endl;
rs.TreeType() = tree;
rs.RandomBasis() = randomBasis;
+20 -1
View File
@@ -22,7 +22,8 @@ RSModel::RSModel(TreeTypes treeType, bool randomBasis) :
rTreeRS(NULL),
rStarTreeRS(NULL),
ballTreeRS(NULL),
xTreeRS(NULL)
xTreeRS(NULL),
hilbertRTreeRS(NULL)
{
// Nothing to do.
}
@@ -122,6 +123,11 @@ void RSModel::BuildModel(arma::mat&& referenceSet,
xTreeRS = new RSType<tree::XTree>(move(referenceSet), naive,
singleMode);
break;
case HILBERT_R_TREE:
hilbertRTreeRS = new RSType<tree::HilbertRTree>(move(referenceSet), naive,
singleMode);
break;
}
if (!naive)
@@ -231,6 +237,10 @@ void RSModel::Search(arma::mat&& querySet,
case X_TREE:
xTreeRS->Search(querySet, range, neighbors, distances);
break;
case HILBERT_R_TREE:
hilbertRTreeRS->Search(querySet, range, neighbors, distances);
break;
}
}
@@ -273,6 +283,10 @@ void RSModel::Search(const math::Range& range,
case X_TREE:
xTreeRS->Search(range, neighbors, distances);
break;
case HILBERT_R_TREE:
hilbertRTreeRS->Search(range, neighbors, distances);
break;
}
}
@@ -293,6 +307,8 @@ std::string RSModel::TreeName() const
return "ball tree";
case X_TREE:
return "X tree";
case HILBERT_R_TREE:
return "Hilbert R tree";
default:
return "unknown tree";
}
@@ -313,6 +329,8 @@ void RSModel::CleanMemory()
delete ballTreeRS;
if (xTreeRS)
delete xTreeRS;
if (hilbertRTreeRS)
delete hilbertRTreeRS;
kdTreeRS = NULL;
coverTreeRS = NULL;
@@ -320,4 +338,5 @@ void RSModel::CleanMemory()
rStarTreeRS = NULL;
ballTreeRS = NULL;
xTreeRS = NULL;
hilbertRTreeRS = NULL;
}
+4 -1
View File
@@ -29,7 +29,8 @@ class RSModel
R_TREE,
R_STAR_TREE,
BALL_TREE,
X_TREE
X_TREE,
HILBERT_R_TREE
};
private:
@@ -60,6 +61,8 @@ class RSModel
RSType<tree::BallTree>* ballTreeRS;
//! X tree based range search object (NULL if not in use).
RSType<tree::XTree>* xTreeRS;
//! Hilbert R tree based range search object (NULL if not in use).
RSType<tree::HilbertRTree>* hilbertRTreeRS;
public:
/**
@@ -53,6 +53,10 @@ void RSModel::Serialize(Archive& ar, const unsigned int /* version */)
case X_TREE:
ar & CreateNVP(xTreeRS, "range_search_model");
break;
case HILBERT_R_TREE:
ar & CreateNVP(hilbertRTreeRS, "range_search_model");
break;
}
}
@@ -70,6 +74,8 @@ inline const arma::mat& RSModel::Dataset() const
return ballTreeRS->ReferenceSet();
else if (xTreeRS)
return xTreeRS->ReferenceSet();
else if (hilbertRTreeRS)
return hilbertRTreeRS->ReferenceSet();
throw std::runtime_error("no range search model initialized");
}
@@ -88,6 +94,8 @@ inline bool RSModel::SingleMode() const
return ballTreeRS->SingleMode();
else if (xTreeRS)
return xTreeRS->SingleMode();
else if (hilbertRTreeRS)
return hilbertRTreeRS->SingleMode();
throw std::runtime_error("no range search model initialized");
}
@@ -106,6 +114,8 @@ inline bool& RSModel::SingleMode()
return ballTreeRS->SingleMode();
else if (xTreeRS)
return xTreeRS->SingleMode();
else if (hilbertRTreeRS)
return hilbertRTreeRS->SingleMode();
throw std::runtime_error("no range search model initialized");
}
@@ -124,6 +134,8 @@ inline bool RSModel::Naive() const
return ballTreeRS->Naive();
else if (xTreeRS)
return xTreeRS->Naive();
else if (hilbertRTreeRS)
return hilbertRTreeRS->Naive();
throw std::runtime_error("no range search model initialized");
}
@@ -142,6 +154,8 @@ inline bool& RSModel::Naive()
return ballTreeRS->Naive();
else if (xTreeRS)
return xTreeRS->Naive();
else if (hilbertRTreeRS)
return hilbertRTreeRS->Naive();
throw std::runtime_error("no range search model initialized");
}
+4 -2
View File
@@ -64,7 +64,7 @@ PARAM_INT("k", "Number of nearest neighbors to find.", "k", 0);
// The user may specify the type of tree to use, and a few parameters for tree
// building.
PARAM_STRING("tree_type", "Type of tree to use: 'kd', 'cover', 'r', or "
"'x', 'r-star'.", "t", "kd");
"'x', 'r-star', 'hilbert-r'.", "t", "kd");
PARAM_INT("leaf_size", "Leaf size for tree building (used for kd-trees, R "
"trees, and R* trees).", "l", 20);
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
@@ -172,9 +172,11 @@ int main(int argc, char *argv[])
tree = RANNModel::R_STAR_TREE;
else if (treeType == "x")
tree = RANNModel::X_TREE;
else if (treeType == "hilbert-r")
tree = RANNModel::HILBERT_R_TREE;
else
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
<< "'kd', 'cover', 'r', 'r-star' and 'x'." << endl;
<< "'kd', 'cover', 'r', 'r-star', 'x' and 'hilbert-r'." << endl;
rann.TreeType() = tree;
rann.RandomBasis() = randomBasis;
+4 -1
View File
@@ -40,7 +40,8 @@ class RAModel
COVER_TREE,
R_TREE,
R_STAR_TREE,
X_TREE
X_TREE,
HILBERT_R_TREE
};
private:
@@ -73,6 +74,8 @@ class RAModel
RAType<tree::RStarTree>* rStarTreeRA;
//! Non-NULL if the X tree is used.
RAType<tree::XTree>* xTreeRA;
//! Non-NULL if the Hilbert R tree is used.
RAType<tree::HilbertRTree>* hilbertRTreeRA;
public:
/**
+55 -1
View File
@@ -22,7 +22,8 @@ RAModel<SortPolicy>::RAModel(const TreeTypes treeType, const bool randomBasis) :
coverTreeRA(NULL),
rTreeRA(NULL),
rStarTreeRA(NULL),
xTreeRA(NULL)
xTreeRA(NULL),
hilbertRTreeRA(NULL)
{
// Nothing to do.
}
@@ -40,6 +41,8 @@ RAModel<SortPolicy>::~RAModel()
delete rStarTreeRA;
if (xTreeRA)
delete xTreeRA;
if (hilbertRTreeRA)
delete hilbertRTreeRA;
}
template<typename SortPolicy>
@@ -64,6 +67,8 @@ void RAModel<SortPolicy>::Serialize(Archive& ar,
delete rStarTreeRA;
if (xTreeRA)
delete xTreeRA;
if (hilbertRTreeRA)
delete hilbertRTreeRA;
// Set all the pointers to NULL.
kdTreeRA = NULL;
@@ -71,6 +76,7 @@ void RAModel<SortPolicy>::Serialize(Archive& ar,
rTreeRA = NULL;
rStarTreeRA = NULL;
xTreeRA = NULL;
hilbertRTreeRA = NULL;
}
// We only need to serialize one of the kRANN objects.
@@ -91,6 +97,9 @@ void RAModel<SortPolicy>::Serialize(Archive& ar,
case X_TREE:
ar & data::CreateNVP(xTreeRA, "ra_model");
break;
case HILBERT_R_TREE:
ar & data::CreateNVP(hilbertRTreeRA, "ra_model");
break;
}
}
@@ -107,6 +116,8 @@ const arma::mat& RAModel<SortPolicy>::Dataset() const
return rStarTreeRA->ReferenceSet();
else if (xTreeRA)
return xTreeRA->ReferenceSet();
else if (hilbertRTreeRA)
return hilbertRTreeRA->ReferenceSet();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -125,6 +136,8 @@ bool RAModel<SortPolicy>::Naive() const
return rStarTreeRA->Naive();
else if (xTreeRA)
return xTreeRA->Naive();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Naive();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -143,6 +156,8 @@ bool& RAModel<SortPolicy>::Naive()
return rStarTreeRA->Naive();
else if (xTreeRA)
return xTreeRA->Naive();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Naive();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -161,6 +176,8 @@ bool RAModel<SortPolicy>::SingleMode() const
return rStarTreeRA->SingleMode();
else if (xTreeRA)
return xTreeRA->SingleMode();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SingleMode();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -179,6 +196,8 @@ bool& RAModel<SortPolicy>::SingleMode()
return rStarTreeRA->SingleMode();
else if (xTreeRA)
return xTreeRA->SingleMode();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SingleMode();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -197,6 +216,8 @@ double RAModel<SortPolicy>::Tau() const
return rStarTreeRA->Tau();
else if (xTreeRA)
return xTreeRA->Tau();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Tau();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -215,6 +236,8 @@ double& RAModel<SortPolicy>::Tau()
return rStarTreeRA->Tau();
else if (xTreeRA)
return xTreeRA->Tau();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Tau();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -233,6 +256,8 @@ double RAModel<SortPolicy>::Alpha() const
return rStarTreeRA->Alpha();
else if (xTreeRA)
return xTreeRA->Alpha();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Alpha();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -251,6 +276,8 @@ double& RAModel<SortPolicy>::Alpha()
return rStarTreeRA->Alpha();
else if (xTreeRA)
return xTreeRA->Alpha();
else if (hilbertRTreeRA)
return hilbertRTreeRA->Alpha();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -269,6 +296,8 @@ bool RAModel<SortPolicy>::SampleAtLeaves() const
return rStarTreeRA->SampleAtLeaves();
else if (xTreeRA)
return xTreeRA->SampleAtLeaves();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SampleAtLeaves();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -287,6 +316,8 @@ bool& RAModel<SortPolicy>::SampleAtLeaves()
return rStarTreeRA->SampleAtLeaves();
else if (xTreeRA)
return xTreeRA->SampleAtLeaves();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SampleAtLeaves();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -305,6 +336,8 @@ bool RAModel<SortPolicy>::FirstLeafExact() const
return rStarTreeRA->FirstLeafExact();
else if (xTreeRA)
return xTreeRA->FirstLeafExact();
else if (hilbertRTreeRA)
return hilbertRTreeRA->FirstLeafExact();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -323,6 +356,8 @@ bool& RAModel<SortPolicy>::FirstLeafExact()
return rStarTreeRA->FirstLeafExact();
else if (xTreeRA)
return xTreeRA->FirstLeafExact();
else if (hilbertRTreeRA)
return hilbertRTreeRA->FirstLeafExact();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -341,6 +376,8 @@ size_t RAModel<SortPolicy>::SingleSampleLimit() const
return rStarTreeRA->SingleSampleLimit();
else if (xTreeRA)
return xTreeRA->SingleSampleLimit();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SingleSampleLimit();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -359,6 +396,8 @@ size_t& RAModel<SortPolicy>::SingleSampleLimit()
return rStarTreeRA->SingleSampleLimit();
else if (xTreeRA)
return xTreeRA->SingleSampleLimit();
else if (hilbertRTreeRA)
return hilbertRTreeRA->SingleSampleLimit();
throw std::runtime_error("no rank-approximate nearest neighbor search model "
"initialized");
@@ -424,6 +463,8 @@ void RAModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
delete rStarTreeRA;
if (xTreeRA)
delete xTreeRA;
if (hilbertRTreeRA)
delete hilbertRTreeRA;
if (randomBasis)
referenceSet = q * referenceSet;
@@ -472,6 +513,10 @@ void RAModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
xTreeRA = new RAType<tree::XTree>(std::move(referenceSet), naive,
singleMode);
break;
case HILBERT_R_TREE:
hilbertRTreeRA = new RAType<tree::HilbertRTree>(std::move(referenceSet),
naive, singleMode);
break;
}
if (!naive)
@@ -549,6 +594,10 @@ void RAModel<SortPolicy>::Search(arma::mat&& querySet,
// No mapping necessary.
xTreeRA->Search(querySet, k, neighbors, distances);
break;
case HILBERT_R_TREE:
// No mapping necessary.
hilbertRTreeRA->Search(querySet, k, neighbors, distances);
break;
}
}
@@ -583,6 +632,9 @@ void RAModel<SortPolicy>::Search(const size_t k,
case X_TREE:
xTreeRA->Search(k, neighbors, distances);
break;
case HILBERT_R_TREE:
hilbertRTreeRA->Search(k, neighbors, distances);
break;
}
}
@@ -601,6 +653,8 @@ std::string RAModel<SortPolicy>::TreeName() const
return "R* tree";
case X_TREE:
return "X tree";
case HILBERT_R_TREE:
return "Hilbert R tree";
default:
return "unknown tree";
}
+1
View File
@@ -24,6 +24,7 @@
#include <iostream>
#include <stdexcept>
#include <tuple>
#include <queue>
// Defining _USE_MATH_DEFINES should set M_PI.
#define _USE_MATH_DEFINES
+2
View File
@@ -28,9 +28,11 @@ add_executable(mlpack_test
kernel_pca_test.cpp
kernel_traits_test.cpp
kfn_test.cpp
akfn_test.cpp
kmeans_test.cpp
knn_test.cpp
krann_search_test.cpp
aknn_test.cpp
lars_test.cpp
lbfgs_test.cpp
lin_alg_test.cpp
@@ -25,7 +25,7 @@
#include <mlpack/methods/ann/layer/hard_tanh_layer.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::ann;
+1 -1
View File
@@ -12,7 +12,7 @@
#include <mlpack/methods/logistic_regression/logistic_regression.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace arma;
using namespace mlpack::optimization;
+1 -2
View File
@@ -8,7 +8,7 @@
#include <mlpack/methods/adaboost/adaboost.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
#include "serialization.hpp"
using namespace arma;
@@ -555,7 +555,6 @@ BOOST_AUTO_TEST_CASE(WeakLearnerErrorNonLinearSepData_DS)
*/
BOOST_AUTO_TEST_CASE(ClassifyTest_VERTEBRALCOL)
{
mlpack::math::RandomSeed(std::time(NULL));
arma::mat inputData;
if (!data::Load("vc2.csv", inputData))
BOOST_FAIL("Cannot load test dataset vc2.csv!");
+1 -1
View File
@@ -11,7 +11,7 @@
#include <mlpack/methods/logistic_regression/logistic_regression.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace arma;
using namespace mlpack::optimization;
+240
View File
@@ -0,0 +1,240 @@
/**
* @file akfn_test.cpp
*
* Tests for KFN (k-furthest-neighbors) with different values of epsilon.
*/
#include <mlpack/core.hpp>
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
#include <mlpack/core/tree/cover_tree.hpp>
#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::neighbor;
using namespace mlpack::tree;
using namespace mlpack::metric;
using namespace mlpack::bound;
BOOST_AUTO_TEST_SUITE(AKFNTest);
/**
* Test the dual-tree furthest-neighbors method with different values for
* epsilon. This uses both a query and reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(ApproxVsExact1)
{
arma::mat dataset;
if (!data::Load("test_data_3_1000.csv", dataset))
BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!");
KFN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(dataset, 15, neighborsExact, distancesExact);
for (size_t c = 0; c < 4; c++)
{
KFN* akfn;
double epsilon;
switch (c)
{
case 0: // Use the dual-tree method with e=0.02.
epsilon = 0.02;
break;
case 1: // Use the dual-tree method with e=0.05.
epsilon = 0.05;
break;
case 2: // Use the dual-tree method with e=0.10.
epsilon = 0.10;
break;
case 3: // Use the dual-tree method with e=0.20.
epsilon = 0.20;
break;
}
// Now perform the actual calculation.
akfn = new KFN(dataset, false, false, epsilon);
arma::Mat<size_t> neighborsApprox;
arma::mat distancesApprox;
akfn->Search(dataset, 15, neighborsApprox, distancesApprox);
for (size_t i = 0; i < neighborsApprox.n_elem; i++)
REQUIRE_RELATIVE_ERR(distancesApprox(i), distancesExact(i), epsilon);
// Clean the memory.
delete akfn;
}
}
/**
* Test the dual-tree furthest-neighbors method with the exact method. This
* uses only a reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(ApproxVsExact2)
{
arma::mat dataset;
if (!data::Load("test_data_3_1000.csv", dataset))
BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!");
KFN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(15, neighborsExact, distancesExact);
KFN akfn(dataset, false, false, 0.05);
arma::Mat<size_t> neighborsApprox;
arma::mat distancesApprox;
akfn.Search(15, neighborsApprox, distancesApprox);
for (size_t i = 0; i < neighborsApprox.n_elem; i++)
REQUIRE_RELATIVE_ERR(distancesApprox[i], distancesExact[i], 0.05);
}
/**
* Test the single-tree furthest-neighbors method with the exact method. This
* uses only a reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(SingleTreeVsExact)
{
arma::mat dataset;
if (!data::Load("test_data_3_1000.csv", dataset))
BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!");
KFN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(15, neighborsExact, distancesExact);
KFN akfn(dataset, false, true, 0.05);
arma::Mat<size_t> neighborsApprox;
arma::mat distancesApprox;
akfn.Search(15, neighborsApprox, distancesApprox);
for (size_t i = 0; i < neighborsApprox.n_elem; i++)
REQUIRE_RELATIVE_ERR(distancesApprox[i], distancesExact[i], 0.05);
}
/**
* Test the cover tree single-tree furthest-neighbors method against the exact
* method. This uses only a random reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(SingleCoverTreeTest)
{
arma::mat dataset;
dataset.randu(75, 1000); // 75 dimensional, 1000 points.
KFN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(dataset, 15, neighborsExact, distancesExact);
StandardCoverTree<EuclideanDistance, NeighborSearchStat<FurthestNeighborSort>,
arma::mat> tree(dataset);
NeighborSearch<FurthestNeighborSort, LMetric<2>, arma::mat, StandardCoverTree>
coverTreeSearch(&tree, true, 0.05);
arma::Mat<size_t> neighborsCoverTree;
arma::mat distancesCoverTree;
coverTreeSearch.Search(dataset, 15, neighborsCoverTree, distancesCoverTree);
for (size_t i = 0; i < neighborsCoverTree.n_elem; ++i)
REQUIRE_RELATIVE_ERR(distancesCoverTree[i], distancesExact[i], 0.05);
}
/**
* Test the cover tree dual-tree furthest neighbors method against the exact
* method.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(DualCoverTreeTest)
{
arma::mat dataset;
data::Load("test_data_3_1000.csv", dataset);
KFN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(dataset, 15, neighborsExact, distancesExact);
StandardCoverTree<EuclideanDistance, NeighborSearchStat<FurthestNeighborSort>,
arma::mat> referenceTree(dataset);
NeighborSearch<FurthestNeighborSort, LMetric<2>, arma::mat, StandardCoverTree>
coverTreeSearch(&referenceTree, false, 0.05);
arma::Mat<size_t> neighborsCoverTree;
arma::mat distancesCoverTree;
coverTreeSearch.Search(dataset, 15, neighborsCoverTree, distancesCoverTree);
for (size_t i = 0; i < neighborsCoverTree.n_elem; ++i)
REQUIRE_RELATIVE_ERR(distancesCoverTree[i], distancesExact[i], 0.05);
}
/**
* Test the ball tree single-tree furthest-neighbors method against the exact
* method. This uses only a random reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(SingleBallTreeTest)
{
arma::mat dataset;
dataset.randu(75, 1000); // 75 dimensional, 1000 points.
KFN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(dataset, 15, neighborsExact, distancesExact);
NeighborSearch<FurthestNeighborSort, EuclideanDistance, arma::mat, BallTree>
ballTreeSearch(dataset, false, true, 0.05);
arma::Mat<size_t> neighborsBallTree;
arma::mat distancesBallTree;
ballTreeSearch.Search(dataset, 15, neighborsBallTree, distancesBallTree);
for (size_t i = 0; i < neighborsBallTree.n_elem; ++i)
REQUIRE_RELATIVE_ERR(distancesBallTree(i), distancesExact(i), 0.05);
}
/**
* Test the ball tree dual-tree furthest neighbors method against the exact
* method.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(DualBallTreeTest)
{
arma::mat dataset;
data::Load("test_data_3_1000.csv", dataset);
KFN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(15, neighborsExact, distancesExact);
NeighborSearch<FurthestNeighborSort, EuclideanDistance, arma::mat, BallTree>
ballTreeSearch(dataset, false, false, 0.05);
arma::Mat<size_t> neighborsBallTree;
arma::mat distancesBallTree;
ballTreeSearch.Search(15, neighborsBallTree, distancesBallTree);
for (size_t i = 0; i < neighborsBallTree.n_elem; ++i)
REQUIRE_RELATIVE_ERR(distancesBallTree(i), distancesExact(i), 0.05);
}
BOOST_AUTO_TEST_SUITE_END();
+405
View File
@@ -0,0 +1,405 @@
/**
* @file aknn_test.cpp
*
* Test file for KNN class with different values of epsilon.
*/
#include <mlpack/core.hpp>
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
#include <mlpack/methods/neighbor_search/unmap.hpp>
#include <mlpack/methods/neighbor_search/ns_model.hpp>
#include <mlpack/core/tree/cover_tree.hpp>
#include <mlpack/core/tree/example_tree.hpp>
#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::neighbor;
using namespace mlpack::tree;
using namespace mlpack::metric;
using namespace mlpack::bound;
BOOST_AUTO_TEST_SUITE(AKNNTest);
/**
* Test the dual-tree nearest-neighbors method with different values for
* epsilon. This uses both a query and reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(ApproxVsExact1)
{
arma::mat dataset;
if (!data::Load("test_data_3_1000.csv", dataset))
BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!");
KNN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(dataset, 15, neighborsExact, distancesExact);
for (size_t c = 0; c < 4; c++)
{
KNN* aknn;
double epsilon;
switch (c)
{
case 0: // Use the dual-tree method with e=0.02.
epsilon = 0.02;
break;
case 1: // Use the dual-tree method with e=0.05.
epsilon = 0.05;
break;
case 2: // Use the dual-tree method with e=0.10.
epsilon = 0.10;
break;
case 3: // Use the dual-tree method with e=0.20.
epsilon = 0.20;
break;
}
// Now perform the actual calculation.
aknn = new KNN(dataset, false, false, epsilon);
arma::Mat<size_t> neighborsApprox;
arma::mat distancesApprox;
aknn->Search(dataset, 15, neighborsApprox, distancesApprox);
for (size_t i = 0; i < neighborsApprox.n_elem; i++)
REQUIRE_RELATIVE_ERR(distancesApprox(i), distancesExact(i), epsilon);
// Clean the memory.
delete aknn;
}
}
/**
* Test the dual-tree nearest-neighbors method with the exact method. This uses
* only a reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(ApproxVsExact2)
{
arma::mat dataset;
if (!data::Load("test_data_3_1000.csv", dataset))
BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!");
KNN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(15, neighborsExact, distancesExact);
KNN aknn(dataset, false, false, 0.05);
arma::Mat<size_t> neighborsApprox;
arma::mat distancesApprox;
aknn.Search(15, neighborsApprox, distancesApprox);
for (size_t i = 0; i < neighborsApprox.n_elem; i++)
REQUIRE_RELATIVE_ERR(distancesApprox(i), distancesExact(i), 0.05);
}
/**
* Test the single-tree nearest-neighbors method with the exact method. This
* uses only a reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(SingleTreeApproxVsExact)
{
arma::mat dataset;
if (!data::Load("test_data_3_1000.csv", dataset))
BOOST_FAIL("Cannot load test dataset test_data_3_1000.csv!");
KNN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(15, neighborsExact, distancesExact);
KNN aknn(dataset, false, true, 0.05);
arma::Mat<size_t> neighborsApprox;
arma::mat distancesApprox;
aknn.Search(15, neighborsApprox, distancesApprox);
for (size_t i = 0; i < neighborsApprox.n_elem; i++)
REQUIRE_RELATIVE_ERR(distancesApprox[i], distancesExact[i], 0.05);
}
/**
* Test the cover tree single-tree nearest-neighbors method against the exact
* method. This uses only a random reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(SingleCoverTreeTest)
{
arma::mat dataset;
dataset.randu(75, 1000); // 75 dimensional, 1000 points.
KNN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(dataset, 15, neighborsExact, distancesExact);
StandardCoverTree<EuclideanDistance, NeighborSearchStat<NearestNeighborSort>,
arma::mat> tree(dataset);
NeighborSearch<NearestNeighborSort, LMetric<2>, arma::mat, StandardCoverTree>
coverTreeSearch(&tree, true, 0.05);
arma::Mat<size_t> neighborsCoverTree;
arma::mat distancesCoverTree;
coverTreeSearch.Search(dataset, 15, neighborsCoverTree, distancesCoverTree);
for (size_t i = 0; i < neighborsCoverTree.n_elem; ++i)
REQUIRE_RELATIVE_ERR(distancesCoverTree[i], distancesExact[i], 0.05);
}
/**
* Test the cover tree dual-tree nearest neighbors method against the exact
* method.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(DualCoverTreeTest)
{
arma::mat dataset;
data::Load("test_data_3_1000.csv", dataset);
KNN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(dataset, 15, neighborsExact, distancesExact);
StandardCoverTree<EuclideanDistance, NeighborSearchStat<NearestNeighborSort>,
arma::mat> referenceTree(dataset);
NeighborSearch<NearestNeighborSort, EuclideanDistance, arma::mat,
StandardCoverTree> coverTreeSearch(&referenceTree, false, 0.05);
arma::Mat<size_t> neighborsCoverTree;
arma::mat distancesCoverTree;
coverTreeSearch.Search(&referenceTree, 15, neighborsCoverTree,
distancesCoverTree);
for (size_t i = 0; i < neighborsCoverTree.n_elem; ++i)
REQUIRE_RELATIVE_ERR(distancesCoverTree[i], distancesExact[i], 0.05);
}
/**
* Test the ball tree single-tree nearest-neighbors method against the exact
* method. This uses only a random reference dataset.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(SingleBallTreeTest)
{
arma::mat dataset;
dataset.randu(50, 300); // 50 dimensional, 300 points.
KNN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(dataset, 15, neighborsExact, distancesExact);
NeighborSearch<NearestNeighborSort, EuclideanDistance, arma::mat, BallTree>
ballTreeSearch(dataset, false, true, 0.05);
arma::Mat<size_t> neighborsBallTree;
arma::mat distancesBallTree;
ballTreeSearch.Search(dataset, 15, neighborsBallTree, distancesBallTree);
for (size_t i = 0; i < neighborsBallTree.n_elem; ++i)
REQUIRE_RELATIVE_ERR(distancesBallTree(i), distancesExact(i), 0.05);
}
/**
* Test the ball tree dual-tree nearest neighbors method against the exact
* method.
*
* Errors are produced if the results are not according to relative error.
*/
BOOST_AUTO_TEST_CASE(DualBallTreeTest)
{
arma::mat dataset;
data::Load("test_data_3_1000.csv", dataset);
KNN exact(dataset);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(15, neighborsExact, distancesExact);
NeighborSearch<NearestNeighborSort, EuclideanDistance, arma::mat, BallTree>
ballTreeSearch(dataset, false, false, 0.05);
arma::Mat<size_t> neighborsBallTree;
arma::mat distancesBallTree;
ballTreeSearch.Search(15, neighborsBallTree, distancesBallTree);
for (size_t i = 0; i < neighborsBallTree.n_elem; ++i)
REQUIRE_RELATIVE_ERR(distancesBallTree(i), distancesExact(i), 0.05);
}
/**
* Make sure sparse nearest neighbors works with kd trees.
*/
BOOST_AUTO_TEST_CASE(SparseKNNKDTreeTest)
{
// The dimensionality of these datasets must be high so that the probability
// of a completely empty point is very low. In this case, with dimensionality
// 70, the probability of all 70 dimensions being zero is 0.8^70 = 1.65e-7 in
// the reference set and 0.9^70 = 6.27e-4 in the query set.
arma::sp_mat queryDataset;
queryDataset.sprandu(70, 200, 0.2);
arma::sp_mat referenceDataset;
referenceDataset.sprandu(70, 500, 0.1);
arma::mat denseQuery(queryDataset);
arma::mat denseReference(referenceDataset);
typedef NeighborSearch<NearestNeighborSort, EuclideanDistance, arma::sp_mat,
KDTree> SparseKNN;
SparseKNN aknn(referenceDataset, false, false, 0.05);
arma::mat distancesSparse;
arma::Mat<size_t> neighborsSparse;
aknn.Search(queryDataset, 10, neighborsSparse, distancesSparse);
KNN exact(denseReference);
arma::mat distancesExact;
arma::Mat<size_t> neighborsExact;
exact.Search(denseQuery, 10, neighborsExact, distancesExact);
for (size_t i = 0; i < neighborsExact.n_cols; ++i)
for (size_t j = 0; j < neighborsExact.n_rows; ++j)
REQUIRE_RELATIVE_ERR(distancesSparse(j, i), distancesExact(j, i), 0.05);
}
/**
* Ensure that we can build an NSModel<NearestNeighborSearch> and get correct
* results.
*/
BOOST_AUTO_TEST_CASE(KNNModelTest)
{
typedef NSModel<NearestNeighborSort> KNNModel;
arma::mat queryData = arma::randu<arma::mat>(10, 50);
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
KNNModel models[14];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
models[3] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false);
models[4] = KNNModel(KNNModel::TreeTypes::R_TREE, true);
models[5] = KNNModel(KNNModel::TreeTypes::R_TREE, false);
models[6] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, true);
models[7] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, false);
models[8] = KNNModel(KNNModel::TreeTypes::X_TREE, true);
models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false);
models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true);
models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false);
models[12] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true);
models[13] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false);
for (size_t j = 0; j < 3; ++j)
{
// Get a baseline.
KNN aknn(referenceData);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
aknn.Search(queryData, 3, neighborsExact, distancesExact);
for (size_t i = 0; i < 14; ++i)
{
// We only have std::move() constructors so make a copy of our data.
arma::mat referenceCopy(referenceData);
arma::mat queryCopy(queryData);
if (j == 0)
models[i].BuildModel(std::move(referenceCopy), 20, false, false, 0.05);
if (j == 1)
models[i].BuildModel(std::move(referenceCopy), 20, false, true, 0.05);
if (j == 2)
models[i].BuildModel(std::move(referenceCopy), 20, true, false);
arma::Mat<size_t> neighborsApprox;
arma::mat distancesApprox;
models[i].Search(std::move(queryCopy), 3, neighborsApprox,
distancesApprox);
BOOST_REQUIRE_EQUAL(neighborsApprox.n_rows, neighborsExact.n_rows);
BOOST_REQUIRE_EQUAL(neighborsApprox.n_cols, neighborsExact.n_cols);
BOOST_REQUIRE_EQUAL(neighborsApprox.n_elem, neighborsExact.n_elem);
BOOST_REQUIRE_EQUAL(distancesApprox.n_rows, distancesExact.n_rows);
BOOST_REQUIRE_EQUAL(distancesApprox.n_cols, distancesExact.n_cols);
BOOST_REQUIRE_EQUAL(distancesApprox.n_elem, distancesExact.n_elem);
for (size_t k = 0; k < distancesApprox.n_elem; ++k)
REQUIRE_RELATIVE_ERR(distancesApprox[k], distancesExact[k], 0.05);
}
}
}
/**
* Ensure that we can build an NSModel<NearestNeighborSearch> and get correct
* results, in the case where the reference set is the same as the query set.
*/
BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
{
typedef NSModel<NearestNeighborSort> KNNModel;
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
KNNModel models[14];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
models[3] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false);
models[4] = KNNModel(KNNModel::TreeTypes::R_TREE, true);
models[5] = KNNModel(KNNModel::TreeTypes::R_TREE, false);
models[6] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, true);
models[7] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, false);
models[8] = KNNModel(KNNModel::TreeTypes::X_TREE, true);
models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false);
models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true);
models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false);
models[12] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true);
models[13] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false);
for (size_t j = 0; j < 2; ++j)
{
// Get a baseline.
KNN exact(referenceData);
arma::Mat<size_t> neighborsExact;
arma::mat distancesExact;
exact.Search(3, neighborsExact, distancesExact);
for (size_t i = 0; i < 14; ++i)
{
// We only have a std::move() constructor... so copy the data.
arma::mat referenceCopy(referenceData);
if (j == 0)
models[i].BuildModel(std::move(referenceCopy), 20, false, false, 0.05);
if (j == 1)
models[i].BuildModel(std::move(referenceCopy), 20, false, true, 0.05);
arma::Mat<size_t> neighborsApprox;
arma::mat distancesApprox;
models[i].Search(3, neighborsApprox, distancesApprox);
BOOST_REQUIRE_EQUAL(neighborsApprox.n_rows, neighborsExact.n_rows);
BOOST_REQUIRE_EQUAL(neighborsApprox.n_cols, neighborsExact.n_cols);
BOOST_REQUIRE_EQUAL(neighborsApprox.n_elem, neighborsExact.n_elem);
BOOST_REQUIRE_EQUAL(distancesApprox.n_rows, distancesExact.n_rows);
BOOST_REQUIRE_EQUAL(distancesApprox.n_cols, distancesExact.n_cols);
BOOST_REQUIRE_EQUAL(distancesApprox.n_elem, distancesExact.n_elem);
for (size_t k = 0; k < distancesApprox.n_elem; ++k)
REQUIRE_RELATIVE_ERR(distancesApprox[k], distancesExact[k], 0.05);
}
}
}
BOOST_AUTO_TEST_SUITE_END();
+1 -1
View File
@@ -7,7 +7,7 @@
#include <mlpack/core.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace arma;
+1 -1
View File
@@ -2,7 +2,7 @@
#include <mlpack/methods/cf/svd_wrapper.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
BOOST_AUTO_TEST_SUITE(ArmadilloSVDTest);
+1 -1
View File
@@ -10,7 +10,7 @@
#include <mlpack/core/optimizers/aug_lagrangian/aug_lagrangian.hpp>
#include <mlpack/core/optimizers/aug_lagrangian/aug_lagrangian_test_functions.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::optimization;
+1 -1
View File
@@ -9,7 +9,7 @@
#include <mlpack/core/math/random.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace arma;
+1 -1
View File
@@ -10,7 +10,7 @@
#include <iostream>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
#include "serialization.hpp"
BOOST_AUTO_TEST_SUITE(CFTest);
+1 -1
View File
@@ -22,7 +22,7 @@
#define DEFAULT_INT 42
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
#define BASH_RED "\033[0;31m"
#define BASH_GREEN "\033[0;32m"
+1 -1
View File
@@ -13,7 +13,7 @@
#include <mlpack/methods/ann/convolution_rules/svd_convolution.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::ann;
@@ -23,7 +23,7 @@
#include <mlpack/methods/ann/cnn.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::ann;
+1 -1
View File
@@ -9,7 +9,7 @@
#include <mlpack/core/tree/cosine_tree/cosine_tree.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
BOOST_AUTO_TEST_SUITE(CosineTreeTest);
+1 -1
View File
@@ -8,7 +8,7 @@
#include <mlpack/methods/decision_stump/decision_stump.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::decision_stump;
+1 -1
View File
@@ -7,7 +7,7 @@
*/
#include <mlpack/core.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
// This trick does not work on Windows. We will have to comment out the tests
// that depend on it.
+1 -1
View File
@@ -7,7 +7,7 @@
#include <mlpack/core.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::distribution;
+1 -1
View File
@@ -6,7 +6,7 @@
#include <mlpack/core.hpp>
#include <mlpack/methods/emst/dtb.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
#include <mlpack/core/tree/cover_tree.hpp>
+1 -1
View File
@@ -9,7 +9,7 @@
#include <mlpack/methods/fastmks/fastmks_model.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
#include "serialization.hpp"
using namespace mlpack;
@@ -24,7 +24,7 @@
#include <mlpack/core/optimizers/rmsprop/rmsprop.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::ann;
+1 -1
View File
@@ -15,7 +15,7 @@
#include <mlpack/methods/gmm/eigenvalue_ratio_constraint.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::gmm;
+1 -1
View File
@@ -8,7 +8,7 @@
#include <mlpack/methods/gmm/gmm.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::hmm;
+1 -1
View File
@@ -12,7 +12,7 @@
#include <mlpack/methods/hoeffding_trees/binary_numeric_split.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
#include "serialization.hpp"
#include <stack>
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include <mlpack/core.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
BOOST_AUTO_TEST_SUITE(ind2subTest);
+1 -1
View File
@@ -14,7 +14,7 @@
#include <mlpack/methods/ann/init_rules/zero_init.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::ann;
+1 -1
View File
@@ -10,7 +10,7 @@
#include <mlpack/methods/kernel_pca/kernel_pca.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
BOOST_AUTO_TEST_SUITE(KernelPCATest);
+1 -1
View File
@@ -19,7 +19,7 @@
#include <mlpack/core/metrics/mahalanobis_distance.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::kernel;
+1 -1
View File
@@ -9,7 +9,7 @@
#include <mlpack/core.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::kernel;
+1 -1
View File
@@ -7,7 +7,7 @@
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
#include <mlpack/core/tree/cover_tree.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::neighbor;
+1 -2
View File
@@ -18,7 +18,7 @@
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::kmeans;
@@ -390,7 +390,6 @@ BOOST_AUTO_TEST_CASE(RefinedStartTest)
// Our dataset will be five Gaussians of largely varying numbers of points and
// we expect that the refined starting policy should return good guesses at
// what these Gaussians are.
math::RandomSeed(std::time(NULL));
arma::mat data(3, 3000);
data.randn();
+12 -6
View File
@@ -10,7 +10,7 @@
#include <mlpack/core/tree/cover_tree.hpp>
#include <mlpack/core/tree/example_tree.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::neighbor;
@@ -888,7 +888,9 @@ BOOST_AUTO_TEST_CASE(DualBallTreeTest)
}
}
// Make sure sparse nearest neighbors works with kd trees.
/**
* Make sure sparse nearest neighbors works with kd trees.
*/
BOOST_AUTO_TEST_CASE(SparseKNNKDTreeTest)
{
// The dimensionality of these datasets must be high so that the probability
@@ -975,7 +977,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
KNNModel models[12];
KNNModel models[14];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
@@ -988,6 +990,8 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false);
models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true);
models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false);
models[12] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true);
models[13] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false);
for (size_t j = 0; j < 2; ++j)
{
@@ -997,7 +1001,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
arma::mat baselineDistances;
knn.Search(queryData, 3, baselineNeighbors, baselineDistances);
for (size_t i = 0; i < 12; ++i)
for (size_t i = 0; i < 14; ++i)
{
// We only have std::move() constructors so make a copy of our data.
arma::mat referenceCopy(referenceData);
@@ -1041,7 +1045,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
// Build all the possible models.
KNNModel models[12];
KNNModel models[14];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
@@ -1054,6 +1058,8 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, false);
models[10] = KNNModel(KNNModel::TreeTypes::BALL_TREE, true);
models[11] = KNNModel(KNNModel::TreeTypes::BALL_TREE, false);
models[12] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true);
models[13] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false);
for (size_t j = 0; j < 2; ++j)
{
@@ -1063,7 +1069,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
arma::mat baselineDistances;
knn.Search(3, baselineNeighbors, baselineDistances);
for (size_t i = 0; i < 12; ++i)
for (size_t i = 0; i < 14; ++i)
{
// We only have a std::move() constructor... so copy the data.
arma::mat referenceCopy(referenceData);
+5 -3
View File
@@ -10,7 +10,7 @@
#include <mlpack/core/tree/cover_tree.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
#include <mlpack/methods/rann/ra_search.hpp>
#include <mlpack/methods/rann/ra_model.hpp>
@@ -625,7 +625,7 @@ BOOST_AUTO_TEST_CASE(RAModelTest)
data::Load("rann_test_q_3_100.csv", queryData, true);
// Build all the possible models.
KNNModel models[10];
KNNModel models[12];
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false);
@@ -636,13 +636,15 @@ BOOST_AUTO_TEST_CASE(RAModelTest)
models[7] = KNNModel(KNNModel::TreeTypes::R_STAR_TREE, true);
models[8] = KNNModel(KNNModel::TreeTypes::X_TREE, false);
models[9] = KNNModel(KNNModel::TreeTypes::X_TREE, true);
models[10] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, false);
models[11] = KNNModel(KNNModel::TreeTypes::HILBERT_R_TREE, true);
arma::Mat<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 < 10; ++i)
for (size_t i = 0; i < 12; ++i)
{
// We only have std::move() constructors so make a copy of our data.
arma::mat referenceCopy(referenceData);
+1 -1
View File
@@ -10,7 +10,7 @@
#include <mlpack/methods/lars/lars.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::regression;
+1 -1
View File
@@ -13,7 +13,7 @@
#include <mlpack/methods/ann/layer/multiclass_classification_layer.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::ann;
+1 -1
View File
@@ -10,7 +10,7 @@
#include <mlpack/core/optimizers/lbfgs/test_functions.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack::optimization;
using namespace mlpack::optimization::test;
+1 -1
View File
@@ -10,7 +10,7 @@
#include <mlpack/core/math/lin_alg.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace arma;
using namespace mlpack;
+1 -1
View File
@@ -7,7 +7,7 @@
#include <mlpack/methods/linear_regression/linear_regression.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::regression;
+1 -1
View File
@@ -9,7 +9,7 @@
#include <mlpack/core.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::data;
@@ -10,7 +10,7 @@
#include <mlpack/methods/local_coordinate_coding/lcc.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
#include "serialization.hpp"
using namespace arma;
+1 -1
View File
@@ -8,7 +8,7 @@
#include <mlpack/core.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
@@ -9,7 +9,7 @@
#include <mlpack/core/optimizers/sgd/sgd.hpp>
#include <boost/test/unit_test.hpp>
#include "old_boost_test_definitions.hpp"
#include "test_tools.hpp"
using namespace mlpack;
using namespace mlpack::regression;

Some files were not shown because too many files have changed in this diff Show More