Merges multiprobe LSH
This commit is contained in:
+15
-1
@@ -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).
|
||||
|
||||
@@ -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
|
||||
----------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
/**
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <queue>
|
||||
|
||||
// Defining _USE_MATH_DEFINES should set M_PI.
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user