Merge branch 'octree' of https://github.com/rcurtin/mlpack into octree
This commit is contained in:
@@ -50,6 +50,14 @@ set(SOURCES
|
||||
hollow_ball_bound_impl.hpp
|
||||
hrectbound.hpp
|
||||
hrectbound_impl.hpp
|
||||
octree.hpp
|
||||
octree/octree.hpp
|
||||
octree/octree_impl.hpp
|
||||
octree/single_tree_traverser.hpp
|
||||
octree/single_tree_traverser_impl.hpp
|
||||
octree/dual_tree_traverser.hpp
|
||||
octree/dual_tree_traverser_impl.hpp
|
||||
octree/traits.hpp
|
||||
rectangle_tree.hpp
|
||||
rectangle_tree/rectangle_tree.hpp
|
||||
rectangle_tree/rectangle_tree_impl.hpp
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
/**
|
||||
* This is a specialization of the TreeType class to the BinarySpaceTree tree
|
||||
* This is a specialization of the TreeTraits class to the BinarySpaceTree tree
|
||||
* type. It defines characteristics of the binary space tree, and is used to
|
||||
* help write tree-independent (but still optimized) tree-based algorithms. See
|
||||
* mlpack/core/tree/tree_traits.hpp for more information.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file octree.hpp
|
||||
* @author Ryan Curtin
|
||||
*
|
||||
* Include all the necessary files to use the Octree class.
|
||||
*/
|
||||
#ifndef MLPACK_CORE_TREE_OCTREE_HPP
|
||||
#define MLPACK_CORE_TREE_OCTREE_HPP
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include "bounds.hpp"
|
||||
#include "octree/octree.hpp"
|
||||
#include "octree/traits.hpp"
|
||||
#include "octree/single_tree_traverser.hpp"
|
||||
#include "octree/dual_tree_traverser.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @file dual_tree_traverser.hpp
|
||||
* @author Ryan Curtin
|
||||
*
|
||||
* Define the dual-tree traverser for the Octree.
|
||||
*/
|
||||
#ifndef MLPACK_CORE_TREE_OCTREE_DUAL_TREE_TRAVERSER_HPP
|
||||
#define MLPACK_CORE_TREE_OCTREE_DUAL_TREE_TRAVERSER_HPP
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include "octree.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
template<typename MetricType,
|
||||
typename StatisticType,
|
||||
typename MatType>
|
||||
template<typename RuleType>
|
||||
class Octree<MetricType, StatisticType, MatType>::DualTreeTraverser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Instantiate the given dual-tree traverser with the given rule set.
|
||||
*/
|
||||
DualTreeTraverser(RuleType& rule);
|
||||
|
||||
/**
|
||||
* Traverse the two trees. This does not reset the statistics of the
|
||||
* traversals (it just adds to them).
|
||||
*/
|
||||
void Traverse(Octree& queryNode, Octree& referenceNode);
|
||||
|
||||
//! Get the number of pruned nodes.
|
||||
size_t NumPrunes() const { return numPrunes; }
|
||||
//! Modify the number of pruned nodes (i.e. to reset it).
|
||||
size_t& NumPrunes() { return numPrunes; }
|
||||
|
||||
//! Get the number of visited node combinations.
|
||||
size_t NumVisited() const { return numVisited; }
|
||||
//! Modify the number of visited node combinations.
|
||||
size_t& NumVistied() { return numVisited; }
|
||||
|
||||
//! Get the number of times a node was scored.
|
||||
size_t NumScores() const { return numScores; }
|
||||
//! Modify the number of times a node was scored.
|
||||
size_t& NumScores() { return numScores; }
|
||||
|
||||
//! Get the number of times a base case was computed.
|
||||
size_t NumBaseCases() const { return numBaseCases; }
|
||||
//! Modify the number of times a base case was computed.
|
||||
size_t& NumBaseCases() { return numBaseCases; }
|
||||
|
||||
private:
|
||||
//! The rule type to use.
|
||||
RuleType& rule;
|
||||
|
||||
//! The number of prunes.
|
||||
size_t numPrunes;
|
||||
//! The number of visited node combinations.
|
||||
size_t numVisited;
|
||||
//! The number of times a node was scored.
|
||||
size_t numScores;
|
||||
//! The number of times a base case was calculated.
|
||||
size_t numBaseCases;
|
||||
|
||||
//! Traversal information, held in the class so that it isn't continually
|
||||
//! being reallocated.
|
||||
typename RuleType::TraversalInfoType traversalInfo;
|
||||
};
|
||||
|
||||
} // namespace tree
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "dual_tree_traverser_impl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @file dual_tree_traverser_impl.hpp
|
||||
* @author Ryan Curtin
|
||||
*
|
||||
* Implementation of the dual-tree traverser for the octree.
|
||||
*/
|
||||
#ifndef MLPACK_CORE_TREE_OCTREE_DUAL_TREE_TRAVERSER_IMPL_HPP
|
||||
#define MLPACK_CORE_TREE_OCTREE_DUAL_TREE_TRAVERSER_IMPL_HPP
|
||||
|
||||
// In case it hasn't been included yet.
|
||||
#include "dual_tree_traverser.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename RuleType>
|
||||
Octree<MetricType, StatisticType, MatType>::DualTreeTraverser<RuleType>::
|
||||
DualTreeTraverser(RuleType& rule) :
|
||||
rule(rule),
|
||||
numPrunes(0),
|
||||
numVisited(0),
|
||||
numScores(0),
|
||||
numBaseCases(0)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename RuleType>
|
||||
void Octree<MetricType, StatisticType, MatType>::DualTreeTraverser<RuleType>::
|
||||
Traverse(Octree& queryNode, Octree& referenceNode)
|
||||
{
|
||||
// Increment the visit counter.
|
||||
++numVisited;
|
||||
|
||||
// Store the current traversal info.
|
||||
traversalInfo = rule.TraversalInfo();
|
||||
|
||||
if (queryNode.IsLeaf() && referenceNode.IsLeaf())
|
||||
{
|
||||
const size_t begin = queryNode.Point(0);
|
||||
const size_t end = begin + queryNode.NumPoints();
|
||||
for (size_t q = begin; q < end; ++q)
|
||||
{
|
||||
// First, see if we can prune the reference node for this query point.
|
||||
rule.TraversalInfo() = traversalInfo;
|
||||
const double score = rule.Score(q, referenceNode);
|
||||
if (score == DBL_MAX)
|
||||
{
|
||||
++numPrunes;
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t rBegin = referenceNode.Point(0);
|
||||
const size_t rEnd = rBegin + referenceNode.NumPoints();
|
||||
for (size_t r = rBegin; r < rEnd; ++r)
|
||||
rule.BaseCase(q, r);
|
||||
|
||||
numBaseCases += referenceNode.NumPoints();
|
||||
}
|
||||
}
|
||||
else if (!queryNode.IsLeaf() && referenceNode.IsLeaf())
|
||||
{
|
||||
// We have to recurse down the query node. Order does not matter.
|
||||
for (size_t i = 0; i < queryNode.NumChildren(); ++i)
|
||||
{
|
||||
rule.TraversalInfo() = traversalInfo;
|
||||
const double score = rule.Score(queryNode.Child(i), referenceNode);
|
||||
if (score == DBL_MAX)
|
||||
{
|
||||
++numPrunes;
|
||||
continue;
|
||||
}
|
||||
|
||||
Traverse(queryNode.Child(i), referenceNode);
|
||||
}
|
||||
}
|
||||
else if (queryNode.IsLeaf() && !referenceNode.IsLeaf())
|
||||
{
|
||||
// We have to recurse down the reference node, so we need to do it in an
|
||||
// ordered manner.
|
||||
arma::vec scores(referenceNode.NumChildren());
|
||||
std::vector<typename RuleType::TraversalInfoType> tis;
|
||||
for (size_t i = 0; i < referenceNode.NumChildren(); ++i)
|
||||
{
|
||||
rule.TraversalInfo() = traversalInfo;
|
||||
scores[i] = rule.Score(queryNode, referenceNode.Child(i));
|
||||
tis.push_back(rule.TraversalInfo());
|
||||
}
|
||||
|
||||
// Sort the scores.
|
||||
arma::uvec scoreOrder = arma::sort_index(scores);
|
||||
for (size_t i = 0; i < scoreOrder.n_elem; ++i)
|
||||
{
|
||||
if (scores[scoreOrder[i]] == DBL_MAX)
|
||||
{
|
||||
// We don't need to check any more---all children past here are pruned.
|
||||
numPrunes += scoreOrder.n_elem - i;
|
||||
break;
|
||||
}
|
||||
|
||||
rule.TraversalInfo() = tis[scoreOrder[i]];
|
||||
Traverse(queryNode, referenceNode.Child(scoreOrder[i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have to recurse down both the query and reference nodes. Query order
|
||||
// does not matter, so we will do that in sequence. However we will
|
||||
// allocate the arrays for recursion at this level.
|
||||
arma::vec scores(referenceNode.NumChildren());
|
||||
std::vector<typename RuleType::TraversalInfoType>
|
||||
tis(referenceNode.NumChildren());
|
||||
for (size_t j = 0; j < queryNode.NumChildren(); ++j)
|
||||
{
|
||||
// Now we have to recurse down the reference node, which we will do in a
|
||||
// prioritized manner.
|
||||
for (size_t i = 0; i < referenceNode.NumChildren(); ++i)
|
||||
{
|
||||
rule.TraversalInfo() = traversalInfo;
|
||||
scores[i] = rule.Score(queryNode.Child(j), referenceNode.Child(i));
|
||||
tis[i] = rule.TraversalInfo();
|
||||
}
|
||||
|
||||
// Sort the scores.
|
||||
arma::uvec scoreOrder = arma::sort_index(scores);
|
||||
for (size_t i = 0; i < scoreOrder.n_elem; ++i)
|
||||
{
|
||||
if (scores[scoreOrder[i]] == DBL_MAX)
|
||||
{
|
||||
// We don't need to check any more---all children past here are pruned.
|
||||
numPrunes += scoreOrder.n_elem - i;
|
||||
break;
|
||||
}
|
||||
|
||||
rule.TraversalInfo() = tis[scoreOrder[i]];
|
||||
Traverse(queryNode.Child(j), referenceNode.Child(scoreOrder[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tree
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -9,11 +9,12 @@
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include "../hrectbound.hpp"
|
||||
#include "../statistic.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
template<typename MetricType,
|
||||
template<typename MetricType = metric::EuclideanDistance,
|
||||
typename StatisticType = EmptyStatistic,
|
||||
typename MatType = arma::mat>
|
||||
class Octree
|
||||
@@ -24,6 +25,14 @@ class Octree
|
||||
//! The type of element held in MatType.
|
||||
typedef typename MatType::elem_type ElemType;
|
||||
|
||||
//! A single-tree traverser; see single_tree_traverser.hpp.
|
||||
template<typename RuleType>
|
||||
class SingleTreeTraverser;
|
||||
|
||||
//! A dual-tree traverser; see dual_tree_traverser.hpp.
|
||||
template<typename RuleType>
|
||||
class DualTreeTraverser;
|
||||
|
||||
private:
|
||||
//! The children held by this node.
|
||||
std::vector<Octree*> children;
|
||||
@@ -36,11 +45,19 @@ class Octree
|
||||
size_t count;
|
||||
//! The minimum bounding rectangle of the points held in the node (and its
|
||||
//! children).
|
||||
HRectBound<MeetricType> bound;
|
||||
bound::HRectBound<MetricType> bound;
|
||||
//! The dataset.
|
||||
MatType* dataset;
|
||||
//! The parent (NULL if this node is the root).
|
||||
Octree* parent;
|
||||
//! The statistic.
|
||||
StatisticType stat;
|
||||
//! The distance from the center of this node to the center of the parent.
|
||||
ElemType parentDistance;
|
||||
//! The distance to the furthest descendant, cached to speed things up.
|
||||
ElemType furthestDescendantDistance;
|
||||
//! An instantiated metric.
|
||||
MetricType metric;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -97,7 +114,7 @@ class Octree
|
||||
* @param data Dataset to create tree from. This will be copied!
|
||||
* @param maxLeafSize Maximum number of points in a leaf node.
|
||||
*/
|
||||
Octree(const MatType& data, const size_t maxLeafSize = 20);
|
||||
Octree(MatType&& data, const size_t maxLeafSize = 20);
|
||||
|
||||
/**
|
||||
* Construct this as the root node of an octree on the given dataset. This
|
||||
@@ -183,6 +200,199 @@ class Octree
|
||||
const double width,
|
||||
const size_t maxLeafSize = 20);
|
||||
|
||||
/**
|
||||
* Copy the given tree. Be careful! This may use a lot of memory.
|
||||
*
|
||||
* @param other Tree to copy from.
|
||||
*/
|
||||
Octree(const Octree& other);
|
||||
|
||||
/**
|
||||
* Move the given tree. The tree passed as a parameter will be emptied and
|
||||
* will not be usable after this call.
|
||||
*
|
||||
* @param other Tree to move.
|
||||
*/
|
||||
Octree(Octree&& other);
|
||||
|
||||
/**
|
||||
* Initialize the tree from a boost::serialization archive.
|
||||
*
|
||||
* @param ar Archive to load tree from. Must be an iarchive, not an oarchive.
|
||||
*/
|
||||
template<typename Archive>
|
||||
Octree(
|
||||
Archive& ar,
|
||||
const typename boost::enable_if<typename Archive::is_loading>::type* = 0);
|
||||
|
||||
/**
|
||||
* Destroy the tree.
|
||||
*/
|
||||
~Octree();
|
||||
|
||||
//! Return the dataset used by this node.
|
||||
const MatType& Dataset() const { return *dataset; }
|
||||
|
||||
//! Get the pointer to the parent.
|
||||
Octree* Parent() const { return parent; }
|
||||
//! Modify the pointer to the parent (be careful!).
|
||||
Octree*& Parent() { return parent; }
|
||||
|
||||
//! Return the bound object for this node.
|
||||
const bound::HRectBound<MetricType>& Bound() const { return bound; }
|
||||
//! Modify the bound object for this node.
|
||||
bound::HRectBound<MetricType>& Bound() { return bound; }
|
||||
|
||||
//! Return the statistic object for this node.
|
||||
const StatisticType& Stat() const { return stat; }
|
||||
//! Modify the statistic object for this node.
|
||||
StatisticType& Stat() { return stat; }
|
||||
|
||||
//! Return the number of children in this node.
|
||||
size_t NumChildren() const;
|
||||
|
||||
//! Return the metric that this tree uses.
|
||||
MetricType Metric() const { return MetricType(); }
|
||||
|
||||
/**
|
||||
* Return the index of the nearest child node to the given query point. If
|
||||
* this is a leaf node, it will return NumChildren() (invalid index).
|
||||
*/
|
||||
template<typename VecType>
|
||||
size_t GetNearestChild(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type* = 0) const;
|
||||
|
||||
/**
|
||||
* Return the index of the furthest child node to the given query point. If
|
||||
* this is a leaf node, it will return NumChildren() (invalid index).
|
||||
*/
|
||||
template<typename VecType>
|
||||
size_t GetFurthestChild(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType> >::type* = 0) const;
|
||||
|
||||
/**
|
||||
* Return whether or not the node is a leaf.
|
||||
*/
|
||||
bool IsLeaf() const { return NumChildren() == 0; }
|
||||
|
||||
/**
|
||||
* Return the index of the nearest child node to the given query node. If it
|
||||
* can't decide, it will return NumChildren() (invalid index).
|
||||
*/
|
||||
size_t GetNearestChild(const Octree& queryNode) const;
|
||||
|
||||
/**
|
||||
* Return the index of the furthest child node to the given query node. If it
|
||||
* can't decide, it will return NumChildren() (invalid index).
|
||||
*/
|
||||
size_t GetFurthestChild(const Octree& queryNode) const;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
ElemType FurthestPointDistance() const;
|
||||
|
||||
/**
|
||||
* Return the furthest possible descendant distance. This returns the maximum
|
||||
* distance from the centroid to the edge of the bound and not the empirical
|
||||
* quantity which is the actual furthest descendant distance. So the actual
|
||||
* furthest descendant distance may be less than what this method returns (but
|
||||
* it will never be greater than this).
|
||||
*/
|
||||
ElemType FurthestDescendantDistance() const;
|
||||
|
||||
//! Return the minimum distance from the center of the node to any bound edge.
|
||||
ElemType MinimumBoundDistance() const;
|
||||
|
||||
//! Return the distance from the center of this node to the center of the
|
||||
//! parent node.
|
||||
ElemType ParentDistance() const { return parentDistance; }
|
||||
//! Modify the distance from the center of this node to the center of the
|
||||
//! parent node.
|
||||
ElemType& ParentDistance() { return parentDistance; }
|
||||
|
||||
/**
|
||||
* Return the specified child. If the index is out of bounds, unspecified
|
||||
* behavior will occur.
|
||||
*/
|
||||
const Octree& Child(const size_t child) const { return *children[child]; }
|
||||
|
||||
/**
|
||||
* Return the specified child. If the index is out of bounds, unspecified
|
||||
* behavior will occur.
|
||||
*/
|
||||
Octree& Child(const size_t child) { return *children[child]; }
|
||||
|
||||
/**
|
||||
* Return the pointer to the given child. This allows the child itself to be
|
||||
* modified.
|
||||
*/
|
||||
Octree*& ChildPtr(const size_t child) { return children[child]; }
|
||||
|
||||
//! Return the number of points in this node (0 if not a leaf).
|
||||
size_t NumPoints() const;
|
||||
|
||||
//! Return the number of descendants of this node.
|
||||
size_t NumDescendants() const;
|
||||
|
||||
/**
|
||||
* Return the index (with reference to the dataset) of a particular
|
||||
* descendant.
|
||||
*/
|
||||
size_t Descendant(const size_t index) const;
|
||||
|
||||
/**
|
||||
* Return the index (with reference to the dataset) of a particular point in
|
||||
* this node. If the given index is invalid (i.e. if it is greater than
|
||||
* NumPoints()), the indices returned will be invalid.
|
||||
*/
|
||||
size_t Point(const size_t index) const;
|
||||
|
||||
//! Return the minimum distance to another node.
|
||||
ElemType MinDistance(const Octree* other) const;
|
||||
//! Return the maximum distance to another node.
|
||||
ElemType MaxDistance(const Octree* other) const;
|
||||
//! Return the minimum and maximum distance to another node.
|
||||
math::RangeType<ElemType> RangeDistance(const Octree* other) const;
|
||||
|
||||
//! Return the minimum distance to the given point.
|
||||
template<typename VecType>
|
||||
ElemType MinDistance(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type* = 0) const;
|
||||
//! Return the maximum distance to the given point.
|
||||
template<typename VecType>
|
||||
ElemType MaxDistance(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type* = 0) const;
|
||||
//! Return the minimum and maximum distance to another node.
|
||||
template<typename VecType>
|
||||
math::RangeType<ElemType> RangeDistance(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type* = 0) const;
|
||||
|
||||
//! Store the center of the bounding region in the given vector.
|
||||
void Center(arma::vec& center) const { bound.Center(center); }
|
||||
|
||||
//! Serialize the tree.
|
||||
template<typename Archive>
|
||||
void Serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* A default constructor. This is meant to only be used with
|
||||
* boost::serialization, which is allowed with the friend declaration below.
|
||||
* This does not return a valid treee! The method must be protected, so that
|
||||
* the serialization shim can work with the default constructor.
|
||||
*/
|
||||
Octree();
|
||||
|
||||
//! Friend access is given for the default constructor.
|
||||
friend class boost::serialization::access;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Split the node, using the given center and the given maximum width of this
|
||||
@@ -190,8 +400,11 @@ class Octree
|
||||
*
|
||||
* @param center Center of the node.
|
||||
* @param width Width of the current node.
|
||||
* @param maxLeafSize Maximum number of points allowed in a leaf.
|
||||
*/
|
||||
void SplitNode(const arma::vec& center, const double width);
|
||||
void SplitNode(const arma::vec& center,
|
||||
const double width,
|
||||
const size_t maxLeafSize);
|
||||
|
||||
/**
|
||||
* Split the node, using the given center and the given maximum width of this
|
||||
@@ -200,8 +413,18 @@ class Octree
|
||||
* @param center Center of the node.
|
||||
* @param width Width of the current node.
|
||||
* @param oldFromNew Mappings from old to new.
|
||||
* @param maxLeafSize Maximum number of points allowed in a leaf.
|
||||
*/
|
||||
void SplitNode(const arma::vec& center,
|
||||
const double width,
|
||||
std::vector<size_t>& oldFromNew);
|
||||
std::vector<size_t>& oldFromNew,
|
||||
const size_t maxLeafSize);
|
||||
};
|
||||
|
||||
} // namespace tree
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "octree_impl.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,19 +9,40 @@
|
||||
|
||||
#include "octree.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
//! Construct the tree.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(const MatType& dataset,
|
||||
const double maxLeafSize) :
|
||||
const size_t maxLeafSize) :
|
||||
begin(0),
|
||||
count(dataset.n_cols),
|
||||
bound(dataset.n_rows),
|
||||
dataset(new MatType(dataset)),
|
||||
|
||||
parent(NULL),
|
||||
parentDistance(0.0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *dataset;
|
||||
arma::vec center = bound.Center();
|
||||
double maxWidth = bound.MaxWidth();
|
||||
if (count > 0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *this->dataset;
|
||||
arma::vec center;
|
||||
bound.Center(center);
|
||||
|
||||
SplitNode(center, maxWidth);
|
||||
double maxWidth = 0.0;
|
||||
for (size_t i = 0; i < bound.Dim(); ++i)
|
||||
if (bound[i].Hi() - bound[i].Lo() > maxWidth)
|
||||
maxWidth = bound[i].Hi() - bound[i].Lo();
|
||||
|
||||
SplitNode(center, maxWidth, maxLeafSize);
|
||||
|
||||
furthestDescendantDistance = 0.5 * bound.Diameter();
|
||||
}
|
||||
else
|
||||
{
|
||||
furthestDescendantDistance = 0.0;
|
||||
}
|
||||
|
||||
// Initialize the statistic.
|
||||
stat = StatisticType(*this);
|
||||
@@ -33,19 +54,37 @@ Octree<MetricType, StatisticType, MatType>::Octree(
|
||||
const MatType& dataset,
|
||||
std::vector<size_t>& oldFromNew,
|
||||
const size_t maxLeafSize) :
|
||||
begin(0),
|
||||
count(dataset.n_cols),
|
||||
bound(dataset.n_rows),
|
||||
dataset(new MatType(dataset)),
|
||||
|
||||
parent(NULL),
|
||||
parentDistance(0.0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *dataset;
|
||||
arma::vec center = bound.Center();
|
||||
double maxWidth = bound.MaxWidth();
|
||||
|
||||
oldFromNew.resize(data.n_cols);
|
||||
for (size_t i = 0; i < data.n_cols; ++i)
|
||||
oldFromNew.resize(this->dataset->n_cols);
|
||||
for (size_t i = 0; i < this->dataset->n_cols; ++i)
|
||||
oldFromNew[i] = i;
|
||||
|
||||
SplitNode(center, maxWidth, oldFromNew);
|
||||
if (count > 0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *this->dataset;
|
||||
arma::vec center;
|
||||
bound.Center(center);
|
||||
|
||||
double maxWidth = 0.0;
|
||||
for (size_t i = 0; i < bound.Dim(); ++i)
|
||||
if (bound[i].Hi() - bound[i].Lo() > maxWidth)
|
||||
maxWidth = bound[i].Hi() - bound[i].Lo();
|
||||
|
||||
SplitNode(center, maxWidth, oldFromNew, maxLeafSize);
|
||||
|
||||
furthestDescendantDistance = 0.5 * bound.Diameter();
|
||||
}
|
||||
else
|
||||
{
|
||||
furthestDescendantDistance = 0.0;
|
||||
}
|
||||
|
||||
// Initialize the statistic.
|
||||
stat = StatisticType(*this);
|
||||
@@ -56,32 +95,621 @@ template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(
|
||||
const MatType& dataset,
|
||||
std::vector<size_t>& oldFromNew,
|
||||
std::vector<size_t>& newFromOld,
|
||||
const size_t maxLeafSize) :
|
||||
begin(0),
|
||||
count(dataset.n_cols),
|
||||
bound(dataset.n_rows),
|
||||
dataset(new MatType(dataset)),
|
||||
|
||||
parent(NULL),
|
||||
parentDistance(0.0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *dataset;
|
||||
arma::vec center = bound.Center();
|
||||
double maxWidth = bound.MaxWidth();
|
||||
|
||||
oldFromNew.resize(data.n_cols);
|
||||
for (size_t i = 0; i < data.n_cols; ++i)
|
||||
oldFromNew.resize(this->dataset->n_cols);
|
||||
for (size_t i = 0; i < this->dataset->n_cols; ++i)
|
||||
oldFromNew[i] = i;
|
||||
|
||||
SplitNode(center, maxWidth, oldFromNew);
|
||||
if (count > 0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *this->dataset;
|
||||
arma::vec center;
|
||||
bound.Center(center);
|
||||
|
||||
double maxWidth = 0.0;
|
||||
for (size_t i = 0; i < bound.Dim(); ++i)
|
||||
if (bound[i].Hi() - bound[i].Lo() > maxWidth)
|
||||
maxWidth = bound[i].Hi() - bound[i].Lo();
|
||||
|
||||
SplitNode(center, maxWidth, oldFromNew, maxLeafSize);
|
||||
|
||||
furthestDescendantDistance = 0.5 * bound.Diameter();
|
||||
}
|
||||
else
|
||||
{
|
||||
furthestDescendantDistance = 0.0;
|
||||
}
|
||||
|
||||
// Initialize the statistic.
|
||||
stat = StatisticType(*this);
|
||||
|
||||
// Map the newFromOld indices correctly.
|
||||
newFromOld.resize(this->dataset->n_cols);
|
||||
for (size_t i = 0; i < this->dataset->n_cols; i++)
|
||||
newFromOld[oldFromNew[i]] = i;
|
||||
}
|
||||
|
||||
//! Construct the tree.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(MatType&& dataset,
|
||||
const size_t maxLeafSize) :
|
||||
begin(0),
|
||||
count(dataset.n_cols),
|
||||
bound(dataset.n_rows),
|
||||
dataset(new MatType(std::move(dataset))),
|
||||
parent(NULL),
|
||||
parentDistance(0.0)
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *this->dataset;
|
||||
arma::vec center;
|
||||
bound.Center(center);
|
||||
|
||||
double maxWidth = 0.0;
|
||||
for (size_t i = 0; i < bound.Dim(); ++i)
|
||||
if (bound[i].Hi() - bound[i].Lo() > maxWidth)
|
||||
maxWidth = bound[i].Hi() - bound[i].Lo();
|
||||
|
||||
SplitNode(center, maxWidth, maxLeafSize);
|
||||
|
||||
furthestDescendantDistance = 0.5 * bound.Diameter();
|
||||
}
|
||||
else
|
||||
{
|
||||
furthestDescendantDistance = 0.0;
|
||||
}
|
||||
|
||||
// Initialize the statistic.
|
||||
stat = StatisticType(*this);
|
||||
}
|
||||
|
||||
//! Construct the tree.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(
|
||||
MatType&& dataset,
|
||||
std::vector<size_t>& oldFromNew,
|
||||
const size_t maxLeafSize) :
|
||||
begin(0),
|
||||
count(dataset.n_cols),
|
||||
bound(dataset.n_rows),
|
||||
dataset(new MatType(std::move(dataset))),
|
||||
parent(NULL),
|
||||
parentDistance(0.0)
|
||||
{
|
||||
oldFromNew.resize(this->dataset->n_cols);
|
||||
for (size_t i = 0; i < this->dataset->n_cols; ++i)
|
||||
oldFromNew[i] = i;
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *this->dataset;
|
||||
arma::vec center;
|
||||
bound.Center(center);
|
||||
|
||||
double maxWidth = 0.0;
|
||||
for (size_t i = 0; i < bound.Dim(); ++i)
|
||||
if (bound[i].Hi() - bound[i].Lo() > maxWidth)
|
||||
maxWidth = bound[i].Hi() - bound[i].Lo();
|
||||
|
||||
SplitNode(center, maxWidth, oldFromNew, maxLeafSize);
|
||||
|
||||
furthestDescendantDistance = 0.5 * bound.Diameter();
|
||||
}
|
||||
else
|
||||
{
|
||||
furthestDescendantDistance = 0.0;
|
||||
}
|
||||
|
||||
// Initialize the statistic.
|
||||
stat = StatisticType(*this);
|
||||
}
|
||||
|
||||
//! Construct the tree.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(
|
||||
MatType&& dataset,
|
||||
std::vector<size_t>& oldFromNew,
|
||||
std::vector<size_t>& newFromOld,
|
||||
const size_t maxLeafSize) :
|
||||
begin(0),
|
||||
count(dataset.n_cols),
|
||||
bound(dataset.n_rows),
|
||||
dataset(new MatType(std::move(dataset))),
|
||||
parent(NULL),
|
||||
parentDistance(0.0)
|
||||
{
|
||||
oldFromNew.resize(this->dataset->n_cols);
|
||||
for (size_t i = 0; i < this->dataset->n_cols; ++i)
|
||||
oldFromNew[i] = i;
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= *this->dataset;
|
||||
arma::vec center;
|
||||
bound.Center(center);
|
||||
|
||||
double maxWidth = 0.0;
|
||||
for (size_t i = 0; i < bound.Dim(); ++i)
|
||||
if (bound[i].Hi() - bound[i].Lo() > maxWidth)
|
||||
maxWidth = bound[i].Hi() - bound[i].Lo();
|
||||
|
||||
SplitNode(center, maxWidth, oldFromNew, maxLeafSize);
|
||||
|
||||
furthestDescendantDistance = 0.5 * bound.Diameter();
|
||||
}
|
||||
else
|
||||
{
|
||||
furthestDescendantDistance = 0.0;
|
||||
}
|
||||
|
||||
// Initialize the statistic.
|
||||
stat = StatisticType(*this);
|
||||
|
||||
// Map the newFromOld indices correctly.
|
||||
newFromOld.resize(this->dataset->n_cols);
|
||||
for (size_t i = 0; i < this->dataset->n_cols; i++)
|
||||
newFromOld[oldFromNew[i]] = i;
|
||||
}
|
||||
|
||||
//! Construct a child node.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(
|
||||
Octree* parent,
|
||||
const size_t begin,
|
||||
const size_t count,
|
||||
const arma::vec& center,
|
||||
const double width,
|
||||
const size_t maxLeafSize) :
|
||||
begin(begin),
|
||||
count(count),
|
||||
bound(parent->dataset->n_rows),
|
||||
dataset(parent->dataset),
|
||||
parent(parent)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= dataset->cols(begin, begin + count - 1);
|
||||
|
||||
// Now split the node.
|
||||
SplitNode(center, width, maxLeafSize);
|
||||
|
||||
// Calculate the distance from the empirical center of this node to the
|
||||
// empirical center of the parent.
|
||||
arma::vec trueCenter, parentCenter;
|
||||
bound.Center(trueCenter);
|
||||
parent->Bound().Center(parentCenter);
|
||||
parentDistance = metric.Evaluate(trueCenter, parentCenter);
|
||||
|
||||
furthestDescendantDistance = 0.5 * bound.Diameter();
|
||||
|
||||
// Initialize the statistic.
|
||||
stat = StatisticType(*this);
|
||||
}
|
||||
|
||||
//! Construct a child node.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(
|
||||
Octree* parent,
|
||||
const size_t begin,
|
||||
const size_t count,
|
||||
std::vector<size_t>& oldFromNew,
|
||||
const arma::vec& center,
|
||||
const double width,
|
||||
const size_t maxLeafSize) :
|
||||
begin(begin),
|
||||
count(count),
|
||||
bound(parent->dataset->n_rows),
|
||||
dataset(parent->dataset),
|
||||
parent(parent)
|
||||
{
|
||||
// Calculate empirical center of data.
|
||||
bound |= dataset->cols(begin, begin + count - 1);
|
||||
|
||||
// Now split the node.
|
||||
SplitNode(center, width, oldFromNew, maxLeafSize);
|
||||
|
||||
// Calculate the distance from the empirical center of this node to the
|
||||
// empirical center of the parent.
|
||||
arma::vec trueCenter, parentCenter;
|
||||
bound.Center(trueCenter);
|
||||
parent->Bound().Center(parentCenter);
|
||||
parentDistance = metric.Evaluate(trueCenter, parentCenter);
|
||||
|
||||
furthestDescendantDistance = 0.5 * bound.Diameter();
|
||||
|
||||
// Initialize the statistic.
|
||||
stat = StatisticType(*this);
|
||||
}
|
||||
|
||||
//! Copy the given tree.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(const Octree& other) :
|
||||
begin(other.begin),
|
||||
count(other.count),
|
||||
bound(other.bound),
|
||||
dataset((other.parent == NULL) ? new MatType(*other.dataset) : NULL),
|
||||
parent(NULL),
|
||||
stat(other.stat),
|
||||
parentDistance(other.parentDistance),
|
||||
furthestDescendantDistance(other.furthestDescendantDistance),
|
||||
metric(other.metric)
|
||||
{
|
||||
// If we have any children, we need to create them, and then ensure that their
|
||||
// parent links are set right.
|
||||
for (size_t i = 0; i < other.NumChildren(); ++i)
|
||||
{
|
||||
children.push_back(new Octree(other.Child(i)));
|
||||
children[i]->parent = this;
|
||||
children[i]->dataset = this->dataset;
|
||||
}
|
||||
}
|
||||
|
||||
//! Move the given tree.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(Octree&& other) :
|
||||
children(std::move(other.children)),
|
||||
begin(other.begin),
|
||||
count(other.count),
|
||||
bound(std::move(other.bound)),
|
||||
dataset(other.dataset),
|
||||
parent(other.parent),
|
||||
stat(std::move(other.stat)),
|
||||
parentDistance(other.parentDistance),
|
||||
furthestDescendantDistance(other.furthestDescendantDistance),
|
||||
metric(std::move(other.metric))
|
||||
{
|
||||
// Update the parent pointers of the direct children.
|
||||
for (size_t i = 0; i < children.size(); ++i)
|
||||
children[i]->parent = this;
|
||||
|
||||
other.begin = 0;
|
||||
other.count = 0;
|
||||
other.dataset = new MatType();
|
||||
other.parentDistance = 0.0;
|
||||
other.furthestDescendantDistance = 0.0;
|
||||
other.parent = NULL;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree() :
|
||||
begin(0),
|
||||
count(0),
|
||||
bound(0),
|
||||
dataset(new MatType()),
|
||||
parent(NULL),
|
||||
parentDistance(0.0),
|
||||
furthestDescendantDistance(0.0)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename Archive>
|
||||
Octree<MetricType, StatisticType, MatType>::Octree(
|
||||
Archive& ar,
|
||||
const typename boost::enable_if<typename Archive::is_loading>::type*) :
|
||||
Octree() // Create an empty tree.
|
||||
{
|
||||
// De-serialize the tree into this object.
|
||||
ar >> data::CreateNVP(*this, "tree");
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
Octree<MetricType, StatisticType, MatType>::~Octree()
|
||||
{
|
||||
// Delete the dataset if we aren't the parent.
|
||||
if (!parent)
|
||||
delete dataset;
|
||||
|
||||
// Now delete each of the children.
|
||||
for (size_t i = 0; i < children.size(); ++i)
|
||||
delete children[i];
|
||||
children.clear();
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::NumChildren() const
|
||||
{
|
||||
return children.size();
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename VecType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::GetNearestChild(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type*) const
|
||||
{
|
||||
// It's possible that this could be improved by caching which children we have
|
||||
// and which we don't, but for now this is just a brute force search.
|
||||
ElemType bestDistance = DBL_MAX;
|
||||
size_t bestIndex = NumChildren();
|
||||
for (size_t i = 0; i < NumChildren(); ++i)
|
||||
{
|
||||
const double dist = children[i]->MinDistance(point);
|
||||
if (dist < bestDistance)
|
||||
{
|
||||
bestDistance = dist;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename VecType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::GetFurthestChild(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type*) const
|
||||
{
|
||||
// It's possible that this could be improved by caching which children we have
|
||||
// and which we don't, but for now this is just a brute force search.
|
||||
ElemType bestDistance = -1.0; // Initialize to invalid distance.
|
||||
size_t bestIndex = NumChildren();
|
||||
for (size_t i = 0; i < NumChildren(); ++i)
|
||||
{
|
||||
const double dist = children[i]->MaxDistance(point);
|
||||
if (dist > bestDistance)
|
||||
{
|
||||
bestDistance = dist;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::GetNearestChild(
|
||||
const Octree& queryNode) const
|
||||
{
|
||||
// It's possible that this could be improved by caching which children we have
|
||||
// and which we don't, but for now this is just a brute force search.
|
||||
ElemType bestDistance = DBL_MAX;
|
||||
size_t bestIndex = NumChildren();
|
||||
for (size_t i = 0; i < NumChildren(); ++i)
|
||||
{
|
||||
const double dist = children[i]->MaxDistance(queryNode);
|
||||
if (dist < bestDistance)
|
||||
{
|
||||
bestDistance = dist;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::GetFurthestChild(
|
||||
const Octree& queryNode) const
|
||||
{
|
||||
// It's possible that this could be improved by caching which children we have
|
||||
// and which we don't, but for now this is just a brute force search.
|
||||
ElemType bestDistance = -1.0; // Initialize to invalid distance.
|
||||
size_t bestIndex = NumChildren();
|
||||
for (size_t i = 0; i < NumChildren(); ++i)
|
||||
{
|
||||
const double dist = children[i]->MaxDistance(queryNode);
|
||||
if (dist > bestDistance)
|
||||
{
|
||||
bestDistance = dist;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
typename Octree<MetricType, StatisticType, MatType>::ElemType
|
||||
Octree<MetricType, StatisticType, MatType>::FurthestPointDistance()
|
||||
const
|
||||
{
|
||||
// If we are not a leaf, then this distance is 0. Otherwise, return the
|
||||
// furthest descendant distance.
|
||||
return (children.size() > 0) ? 0.0 : furthestDescendantDistance;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
typename Octree<MetricType, StatisticType, MatType>::ElemType
|
||||
Octree<MetricType, StatisticType, MatType>::FurthestDescendantDistance() const
|
||||
{
|
||||
return furthestDescendantDistance;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
typename Octree<MetricType, StatisticType, MatType>::ElemType
|
||||
Octree<MetricType, StatisticType, MatType>::MinimumBoundDistance() const
|
||||
{
|
||||
return bound.MinWidth() / 2.0;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::NumPoints() const
|
||||
{
|
||||
// We have no points unless we are a leaf;
|
||||
return (children.size() > 0) ? 0 : count;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::NumDescendants() const
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::Descendant(
|
||||
const size_t index) const
|
||||
{
|
||||
return begin + index;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
size_t Octree<MetricType, StatisticType, MatType>::Point(const size_t index)
|
||||
const
|
||||
{
|
||||
return begin + index;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
typename Octree<MetricType, StatisticType, MatType>::ElemType
|
||||
Octree<MetricType, StatisticType, MatType>::MinDistance(const Octree* other)
|
||||
const
|
||||
{
|
||||
return bound.MinDistance(other->Bound());
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
typename Octree<MetricType, StatisticType, MatType>::ElemType
|
||||
Octree<MetricType, StatisticType, MatType>::MaxDistance(const Octree* other)
|
||||
const
|
||||
{
|
||||
return bound.MaxDistance(other->Bound());
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
math::RangeType<typename Octree<MetricType, StatisticType, MatType>::ElemType>
|
||||
Octree<MetricType, StatisticType, MatType>::RangeDistance(const Octree* other)
|
||||
const
|
||||
{
|
||||
return bound.RangeDistance(other->Bound());
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename VecType>
|
||||
typename Octree<MetricType, StatisticType, MatType>::ElemType
|
||||
Octree<MetricType, StatisticType, MatType>::MinDistance(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type*) const
|
||||
{
|
||||
return bound.MinDistance(point);
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename VecType>
|
||||
typename Octree<MetricType, StatisticType, MatType>::ElemType
|
||||
Octree<MetricType, StatisticType, MatType>::MaxDistance(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type*) const
|
||||
{
|
||||
return bound.MaxDistance(point);
|
||||
}
|
||||
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename VecType>
|
||||
math::RangeType<typename Octree<MetricType, StatisticType, MatType>::ElemType>
|
||||
Octree<MetricType, StatisticType, MatType>::RangeDistance(
|
||||
const VecType& point,
|
||||
typename boost::enable_if<IsVector<VecType>>::type*) const
|
||||
{
|
||||
return bound.RangeDistance(point);
|
||||
}
|
||||
|
||||
//! Serialize the tree.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename Archive>
|
||||
void Octree<MetricType, StatisticType, MatType>::Serialize(
|
||||
Archive& ar,
|
||||
const unsigned int /* version */)
|
||||
{
|
||||
using data::CreateNVP;
|
||||
|
||||
//
|
||||
|
||||
// If we're loading and we have children, they need to be deleted.
|
||||
if (Archive::is_loading::value)
|
||||
{
|
||||
for (size_t i = 0; i < children.size(); ++i)
|
||||
delete children[i];
|
||||
children.clear();
|
||||
|
||||
if (!parent)
|
||||
delete dataset;
|
||||
}
|
||||
|
||||
ar & CreateNVP(begin, "begin");
|
||||
ar & CreateNVP(count, "count");
|
||||
ar & CreateNVP(bound, "bound");
|
||||
ar & CreateNVP(stat, "stat");
|
||||
ar & CreateNVP(parentDistance, "parentDistance");
|
||||
ar & CreateNVP(furthestDescendantDistance, "furthestDescendantDistance");
|
||||
ar & CreateNVP(metric, "metric");
|
||||
|
||||
// Due to quirks of boost::serialization, depending on how the user
|
||||
// serializes the tree, it's possible that the root of the tree will
|
||||
// accidentally be serialized twice. So if we are a first-level child, we
|
||||
// avoid serializing the parent. The true (non-duplicated) parent will fix
|
||||
// the parent link.
|
||||
bool hasFakeParent = false;
|
||||
if (Archive::is_saving::value && parent != NULL && parent->parent == NULL)
|
||||
{
|
||||
Octree* fakeParent = NULL;
|
||||
hasFakeParent = true;
|
||||
ar & CreateNVP(fakeParent, "parent");
|
||||
ar & CreateNVP(hasFakeParent, "hasFakeParent");
|
||||
}
|
||||
else
|
||||
{
|
||||
ar & CreateNVP(parent, "parent");
|
||||
ar & CreateNVP(hasFakeParent, "hasFakeParent");
|
||||
}
|
||||
|
||||
// Only serialize the dataset if we don't have a fake parent. Otherwise, the
|
||||
// real parent will come and set it later.
|
||||
if (!hasFakeParent)
|
||||
ar & CreateNVP(dataset, "dataset");
|
||||
|
||||
size_t numChildren = 0;
|
||||
if (Archive::is_saving::value)
|
||||
numChildren = children.size();
|
||||
ar & CreateNVP(numChildren, "numChildren");
|
||||
if (Archive::is_loading::value)
|
||||
children.resize(numChildren);
|
||||
|
||||
for (size_t i = 0; i < numChildren; ++i)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "child" << i;
|
||||
ar & CreateNVP(children[i], oss.str());
|
||||
}
|
||||
|
||||
// Fix the child pointers, if they were set to a fake parent.
|
||||
if (Archive::is_loading::value && parent == NULL)
|
||||
{
|
||||
for (size_t i = 0; i < children.size(); ++i)
|
||||
{
|
||||
children[i]->dataset = this->dataset;
|
||||
children[i]->parent = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Split the node.
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
void Octree<MetricType, StatisticType, MatType>::SplitNode(
|
||||
const arma::vec& center,
|
||||
const double width)
|
||||
const double width,
|
||||
const size_t maxLeafSize)
|
||||
{
|
||||
// No need to split if we have fewer than the maximum number of points in this
|
||||
// node.
|
||||
if (count <= maxLeafSize)
|
||||
return;
|
||||
|
||||
// We must split the dataset by sequentially creating each of the children.
|
||||
// We do this in two steps: first we make a pass to count the number of points
|
||||
// that will fall into each child; then in the second pass we rearrange the
|
||||
@@ -101,7 +729,7 @@ void Octree<MetricType, StatisticType, MatType>::SplitNode(
|
||||
// the points fall on. The last dimension represents the most significant
|
||||
// bit in the assignment; the bit is '1' if it falls to the right of the
|
||||
// center.
|
||||
if (dataset(d, begin + i) > center(d))
|
||||
if ((*dataset)(d, begin + i) > center(d))
|
||||
assignments(i) |= (1 << d);
|
||||
}
|
||||
|
||||
@@ -130,7 +758,7 @@ void Octree<MetricType, StatisticType, MatType>::SplitNode(
|
||||
for (size_t d = 0; d < center.n_elem; ++d)
|
||||
{
|
||||
// Is the dimension "right" (1) or "left" (0)?
|
||||
if ((i >> d) & 1 == 0)
|
||||
if (((i >> d) & 1) == 0)
|
||||
childCenter[d] = center[d] - childWidth;
|
||||
else
|
||||
childCenter[d] = center[d] + childWidth;
|
||||
@@ -148,8 +776,14 @@ template<typename MetricType, typename StatisticType, typename MatType>
|
||||
void Octree<MetricType, StatisticType, MatType>::SplitNode(
|
||||
const arma::vec& center,
|
||||
const double width,
|
||||
std::vector<size_t>& oldFromNew)
|
||||
std::vector<size_t>& oldFromNew,
|
||||
const size_t maxLeafSize)
|
||||
{
|
||||
// No need to split if we have fewer than the maximum number of points in this
|
||||
// node.
|
||||
if (count <= maxLeafSize)
|
||||
return;
|
||||
|
||||
// We must split the dataset by sequentially creating each of the children.
|
||||
// We do this in two steps: first we make a pass to count the number of points
|
||||
// that will fall into each child; then in the second pass we rearrange the
|
||||
@@ -169,7 +803,7 @@ void Octree<MetricType, StatisticType, MatType>::SplitNode(
|
||||
// the points fall on. The last dimension represents the most significant
|
||||
// bit in the assignment; the bit is '1' if it falls to the right of the
|
||||
// center.
|
||||
if (dataset(d, begin + i) > center(d))
|
||||
if ((*dataset)(d, begin + i) > center(d))
|
||||
assignments(i) |= (1 << d);
|
||||
}
|
||||
|
||||
@@ -183,8 +817,9 @@ void Octree<MetricType, StatisticType, MatType>::SplitNode(
|
||||
// really a problem. We use non-contiguous submatrix views to extract the
|
||||
// columns in the correct order.
|
||||
dataset->cols(begin, begin + count - 1) = dataset->cols(begin + ordering);
|
||||
std::vector<size_t> oldFromNewCopy(oldFromNew); // We need the old indices.
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
oldFromNew[ordering[i] + begin] = i + begin;
|
||||
oldFromNew[i + begin] = oldFromNewCopy[ordering[i] + begin];
|
||||
|
||||
// Now that the dataset is reordered, we can create the children.
|
||||
size_t childBegin = begin;
|
||||
@@ -200,7 +835,7 @@ void Octree<MetricType, StatisticType, MatType>::SplitNode(
|
||||
for (size_t d = 0; d < center.n_elem; ++d)
|
||||
{
|
||||
// Is the dimension "right" (1) or "left" (0)?
|
||||
if ((i >> d) & 1 == 0)
|
||||
if (((i >> d) & 1) == 0)
|
||||
childCenter[d] = center[d] - childWidth;
|
||||
else
|
||||
childCenter[d] = center[d] + childWidth;
|
||||
@@ -212,3 +847,8 @@ void Octree<MetricType, StatisticType, MatType>::SplitNode(
|
||||
childBegin += childCounts[i];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tree
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file single_tree_traverser.hpp
|
||||
* @author Ryan Curtin
|
||||
*
|
||||
* Definition of the single tree traverser for the octree.
|
||||
*/
|
||||
#ifndef MLPACK_CORE_TREE_OCTREE_SINGLE_TREE_TRAVERSER_HPP
|
||||
#define MLPACK_CORE_TREE_OCTREE_SINGLE_TREE_TRAVERSER_HPP
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include "octree.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename RuleType>
|
||||
class Octree<MetricType, StatisticType, MatType>::SingleTreeTraverser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Instantiate the traverser with the given rule set.
|
||||
*/
|
||||
SingleTreeTraverser(RuleType& rule);
|
||||
|
||||
/**
|
||||
* Traverse the reference tree with the given query point. This does not
|
||||
* reset the number of pruned nodes.
|
||||
*
|
||||
* @param queryIndex Index of query point.
|
||||
* @param referenceNode Node in reference tree.
|
||||
*/
|
||||
void Traverse(const size_t queryIndex, Octree& referenceNode);
|
||||
|
||||
//! Get the number of pruned nodes.
|
||||
size_t NumPrunes() const { return numPrunes; }
|
||||
//! Modify the number of pruned nodes.
|
||||
size_t& NumPrunes() { return numPrunes; }
|
||||
|
||||
private:
|
||||
//! The instantiated rule.
|
||||
RuleType& rule;
|
||||
//! The number of reference nodes that have been pruned.
|
||||
size_t numPrunes;
|
||||
};
|
||||
|
||||
} // namespace tree
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "single_tree_traverser_impl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file single_tree_traverser_impl.hpp
|
||||
* @author Ryan Curtin
|
||||
*
|
||||
* Implementation of the single tree traverser for octrees.
|
||||
*/
|
||||
#ifndef MLPACK_CORE_TREE_OCTREE_SINGLE_TREE_TRAVERSER_IMPL_HPP
|
||||
#define MLPACK_CORE_TREE_OCTREE_SINGLE_TREE_TRAVERSER_IMPL_HPP
|
||||
|
||||
// In case it hasn't been included yet.
|
||||
#include "single_tree_traverser.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename RuleType>
|
||||
Octree<MetricType, StatisticType, MatType>::SingleTreeTraverser<RuleType>::
|
||||
SingleTreeTraverser(RuleType& rule) :
|
||||
rule(rule)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
template<typename MetricType, typename StatisticType, typename MatType>
|
||||
template<typename RuleType>
|
||||
void Octree<MetricType, StatisticType, MatType>::SingleTreeTraverser<RuleType>::
|
||||
Traverse(const size_t queryIndex, Octree& referenceNode)
|
||||
{
|
||||
// If we are a leaf, run the base cases.
|
||||
if (referenceNode.NumChildren() == 0)
|
||||
{
|
||||
const size_t refBegin = referenceNode.Point(0);
|
||||
const size_t refEnd = refBegin + referenceNode.NumPoints();
|
||||
for (size_t r = refBegin; r < refEnd; ++r)
|
||||
rule.BaseCase(queryIndex, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do a prioritized recursion, by scoring all candidates and then sorting
|
||||
// them.
|
||||
arma::vec scores(referenceNode.NumChildren());
|
||||
for (size_t i = 0; i < scores.n_elem; ++i)
|
||||
scores[i] = rule.Score(queryIndex, referenceNode.Child(i));
|
||||
|
||||
// Sort the scores.
|
||||
arma::uvec sortedIndices = arma::sort_index(scores);
|
||||
|
||||
for (size_t i = 0; i < sortedIndices.n_elem; ++i)
|
||||
{
|
||||
// If the node is pruned, all subsequent nodes in sorted order will also
|
||||
// be pruned.
|
||||
if (scores[sortedIndices[i]] == DBL_MAX)
|
||||
{
|
||||
numPrunes += (sortedIndices.n_elem - i);
|
||||
break;
|
||||
}
|
||||
|
||||
Traverse(queryIndex, referenceNode.Child(sortedIndices[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tree
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @file traits.hpp
|
||||
* @author Ryan Curtin
|
||||
*
|
||||
* Specialization of the TreeTraits class for the Octree class.
|
||||
*/
|
||||
#ifndef MLPACK_CORE_TREE_OCTREE_TRAITS_HPP
|
||||
#define MLPACK_CORE_TREE_OCTREE_TRAITS_HPP
|
||||
|
||||
#include <mlpack/core/tree/tree_traits.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace tree {
|
||||
|
||||
/**
|
||||
* This is a specialization of the TreeTraits class to the Octree tree type. It
|
||||
* defines characteristics of the octree, and is used to help write
|
||||
* tree-independent (but still optimized) tree-based algorithms. See
|
||||
* mlpack/core/tree/tree_traits.hpp for more information.
|
||||
*/
|
||||
template<typename MetricType,
|
||||
typename StatisticType,
|
||||
typename MatType>
|
||||
class TreeTraits<Octree<MetricType, StatisticType, MatType>>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* No octree nodes will overlap.
|
||||
*/
|
||||
static const bool HasOverlappingChildren = false;
|
||||
|
||||
/**
|
||||
* Points are not shared across nodes in the octree.
|
||||
*/
|
||||
static const bool HasDuplicatedPoints = false;
|
||||
|
||||
/**
|
||||
* There is no guarantee that the first point in a node is its centroid.
|
||||
*/
|
||||
static const bool FirstPointIsCentroid = false;
|
||||
|
||||
/**
|
||||
* Points are not contained at multiple levels of the octree.
|
||||
*/
|
||||
static const bool HasSelfChildren = false;
|
||||
|
||||
/**
|
||||
* Points are rearranged during building of the tree.
|
||||
*/
|
||||
static const bool RearrangesDataset = true;
|
||||
|
||||
/**
|
||||
* This is not necessarily a binary tree.
|
||||
*/
|
||||
static const bool BinaryTree = false;
|
||||
|
||||
/**
|
||||
* NumDescendants() represents the number of unique descendant points.
|
||||
*/
|
||||
static const bool UniqueNumDescendants = true;
|
||||
};
|
||||
|
||||
} // namespace tree
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -68,10 +68,10 @@ PARAM_INT_IN("k", "Number of furthest neighbors to find.", "k", 0);
|
||||
// building.
|
||||
PARAM_STRING_IN("tree_type", "Type of tree to use: 'kd', 'vp', 'rp', 'max-rp', "
|
||||
"'ub', 'cover', 'r', 'r-star', 'x', 'ball', 'hilbert-r', 'r-plus', "
|
||||
"'r-plus-plus'.", "t", "kd");
|
||||
"'r-plus-plus', 'octree'.", "t", "kd");
|
||||
PARAM_INT_IN("leaf_size", "Leaf size for tree building (used for kd-trees, "
|
||||
"vp trees, random projection trees, UB trees, R trees, R* trees, X trees, "
|
||||
"Hilbert R trees, R+ trees and R++ trees).", "l", 20);
|
||||
"Hilbert R trees, R+ trees, R++ trees, and octrees).", "l", 20);
|
||||
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
|
||||
"random orthogonal basis.", "R");
|
||||
PARAM_INT_IN("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
|
||||
@@ -262,10 +262,13 @@ int main(int argc, char *argv[])
|
||||
tree = KFNModel::MAX_RP_TREE;
|
||||
else if (treeType == "ub")
|
||||
tree = KFNModel::UB_TREE;
|
||||
else if (treeType == "octree")
|
||||
tree = KFNModel::OCTREE;
|
||||
else
|
||||
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
|
||||
<< "'kd', 'vp', 'rp', 'max-rp', 'ub', 'cover', 'r', 'r-star', 'x', "
|
||||
<< "'ball', 'hilbert-r', 'r-plus' and 'r-plus-plus'." << endl;
|
||||
<< "'ball', 'hilbert-r', 'r-plus', 'r-plus-plus', and 'octree'."
|
||||
<< endl;
|
||||
|
||||
kfn.TreeType() = tree;
|
||||
kfn.RandomBasis() = randomBasis;
|
||||
|
||||
@@ -73,7 +73,8 @@ PARAM_STRING_IN("tree_type", "Type of tree to use: 'kd', 'vp', 'rp', 'max-rp', "
|
||||
"'r-plus-plus', 'spill'.", "t", "kd");
|
||||
PARAM_INT_IN("leaf_size", "Leaf size for tree building (used for kd-trees, vp "
|
||||
"trees, random projection trees, UB trees, R trees, R* trees, X trees, "
|
||||
"Hilbert R trees, R+ trees, R++ trees and spill trees).", "l", 20);
|
||||
"Hilbert R trees, R+ trees, R++ trees, spill trees, and octrees).", "l",
|
||||
20);
|
||||
PARAM_DOUBLE_IN("tau", "Overlapping size (only valid for spill trees).", "u",
|
||||
0);
|
||||
PARAM_DOUBLE_IN("rho", "Balance threshold (only valid for spill trees).", "b",
|
||||
@@ -276,11 +277,13 @@ int main(int argc, char *argv[])
|
||||
tree = KNNModel::MAX_RP_TREE;
|
||||
else if (treeType == "ub")
|
||||
tree = KNNModel::UB_TREE;
|
||||
else if (treeType == "octree")
|
||||
tree = KNNModel::OCTREE;
|
||||
else
|
||||
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
|
||||
<< "'kd', 'vp', 'rp', 'max-rp', 'ub', 'cover', 'r', 'r-star', 'x', "
|
||||
<< "'ball', 'hilbert-r', 'r-plus', 'r-plus-plus' and 'spill'."
|
||||
<< endl;
|
||||
<< "'ball', 'hilbert-r', 'r-plus', 'r-plus-plus', 'spill', and "
|
||||
<< "'octree'." << endl;
|
||||
|
||||
knn.TreeType() = tree;
|
||||
knn.RandomBasis() = randomBasis;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <mlpack/core/tree/cover_tree.hpp>
|
||||
#include <mlpack/core/tree/rectangle_tree.hpp>
|
||||
#include <mlpack/core/tree/spill_tree.hpp>
|
||||
#include <mlpack/core/tree/octree.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
#include "neighbor_search.hpp"
|
||||
|
||||
@@ -133,6 +134,9 @@ class BiSearchVisitor : public boost::static_visitor<void>
|
||||
//! Bichromatic neighbor search specialized for SPTrees.
|
||||
void operator()(SpillKNN* ns) const;
|
||||
|
||||
//! Bichromatic neighbor search specialized for octrees.
|
||||
void operator()(NSTypeT<tree::Octree>* ns) const;
|
||||
|
||||
//! Construct the BiSearchVisitor.
|
||||
BiSearchVisitor(const arma::mat& querySet,
|
||||
const size_t k,
|
||||
@@ -188,6 +192,9 @@ class TrainVisitor : public boost::static_visitor<void>
|
||||
//! Train specialized for SPTrees.
|
||||
void operator()(SpillKNN* ns) const;
|
||||
|
||||
//! Train specialized for octrees.
|
||||
void operator()(NSTypeT<tree::Octree>* ns) const;
|
||||
|
||||
//! Construct the TrainVisitor object with the given reference set, leafSize
|
||||
//! for BinarySpaceTrees, and tau and rho for spill trees.
|
||||
TrainVisitor(arma::mat&& referenceSet,
|
||||
@@ -287,7 +294,8 @@ class NSModel
|
||||
RP_TREE,
|
||||
MAX_RP_TREE,
|
||||
SPILL_TREE,
|
||||
UB_TREE
|
||||
UB_TREE,
|
||||
OCTREE
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -325,7 +333,8 @@ class NSModel
|
||||
NSType<SortPolicy, tree::RPTree>*,
|
||||
NSType<SortPolicy, tree::MaxRPTree>*,
|
||||
SpillKNN*,
|
||||
NSType<SortPolicy, tree::UBTree>*> nSearch;
|
||||
NSType<SortPolicy, tree::UBTree>*,
|
||||
NSType<SortPolicy, tree::Octree>*> nSearch;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -96,6 +96,15 @@ void BiSearchVisitor<SortPolicy>::operator()(SpillKNN* ns) const
|
||||
throw std::runtime_error("no neighbor search model initialized");
|
||||
}
|
||||
|
||||
//! Bichromatic neighbor search specialized for octrees.
|
||||
template<typename SortPolicy>
|
||||
void BiSearchVisitor<SortPolicy>::operator()(NSTypeT<tree::Octree>* ns) const
|
||||
{
|
||||
if (ns)
|
||||
return SearchLeaf(ns);
|
||||
throw std::runtime_error("no neighbor search model initialized");
|
||||
}
|
||||
|
||||
//! Bichromatic neighbor search on the given NSType considering the leafSize.
|
||||
template<typename SortPolicy>
|
||||
template<typename NSType>
|
||||
@@ -150,7 +159,7 @@ void TrainVisitor<SortPolicy>::operator()(NSTypeT<TreeType>* ns) const
|
||||
|
||||
//! Train on the given NSType specialized for KDTrees.
|
||||
template<typename SortPolicy>
|
||||
void TrainVisitor<SortPolicy>::operator ()(NSTypeT<tree::KDTree>* ns) const
|
||||
void TrainVisitor<SortPolicy>::operator()(NSTypeT<tree::KDTree>* ns) const
|
||||
{
|
||||
if (ns)
|
||||
return TrainLeaf(ns);
|
||||
@@ -159,7 +168,7 @@ void TrainVisitor<SortPolicy>::operator ()(NSTypeT<tree::KDTree>* ns) const
|
||||
|
||||
//! Train on the given NSType specialized for BallTrees.
|
||||
template<typename SortPolicy>
|
||||
void TrainVisitor<SortPolicy>::operator ()(NSTypeT<tree::BallTree>* ns) const
|
||||
void TrainVisitor<SortPolicy>::operator()(NSTypeT<tree::BallTree>* ns) const
|
||||
{
|
||||
if (ns)
|
||||
return TrainLeaf(ns);
|
||||
@@ -168,7 +177,7 @@ void TrainVisitor<SortPolicy>::operator ()(NSTypeT<tree::BallTree>* ns) const
|
||||
|
||||
//! Train specialized for SPTrees.
|
||||
template<typename SortPolicy>
|
||||
void TrainVisitor<SortPolicy>::operator ()(SpillKNN* ns) const
|
||||
void TrainVisitor<SortPolicy>::operator()(SpillKNN* ns) const
|
||||
{
|
||||
if (ns)
|
||||
{
|
||||
@@ -184,6 +193,15 @@ void TrainVisitor<SortPolicy>::operator ()(SpillKNN* ns) const
|
||||
throw std::runtime_error("no neighbor search model initialized");
|
||||
}
|
||||
|
||||
//! Train specialized for Octrees.
|
||||
template<typename SortPolicy>
|
||||
void TrainVisitor<SortPolicy>::operator()(NSTypeT<tree::Octree>* ns) const
|
||||
{
|
||||
if (ns)
|
||||
return TrainLeaf(ns);
|
||||
throw std::runtime_error("no neighbor search model initialized");
|
||||
}
|
||||
|
||||
//! Train on the given NSType considering the leafSize.
|
||||
template<typename SortPolicy>
|
||||
template<typename NSType>
|
||||
@@ -485,6 +503,9 @@ void NSModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
|
||||
case UB_TREE:
|
||||
nSearch = new NSType<SortPolicy, tree::UBTree>(searchMode, epsilon);
|
||||
break;
|
||||
case OCTREE:
|
||||
nSearch = new NSType<SortPolicy, tree::Octree>(searchMode, epsilon);
|
||||
break;
|
||||
}
|
||||
|
||||
TrainVisitor<SortPolicy> tn(std::move(referenceSet), leafSize, tau, rho);
|
||||
|
||||
@@ -72,10 +72,10 @@ PARAM_DOUBLE_IN("min", "Lower bound in range.", "L", 0.0);
|
||||
// building.
|
||||
PARAM_STRING_IN("tree_type", "Type of tree to use: 'kd', 'vp', 'rp', 'max-rp', "
|
||||
"'ub', 'cover', 'r', 'r-star', 'x', 'ball', 'hilbert-r', 'r-plus', "
|
||||
"'r-plus-plus'.", "t", "kd");
|
||||
"'r-plus-plus', 'octree'.", "t", "kd");
|
||||
PARAM_INT_IN("leaf_size", "Leaf size for tree building (used for kd-trees, "
|
||||
"vp trees, random projection trees, UB trees, R trees, R* trees, X trees, "
|
||||
"Hilbert R trees, R+ trees and R++ trees).", "l", 20);
|
||||
"Hilbert R trees, R+ trees, R++ trees, and octrees).", "l", 20);
|
||||
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
|
||||
"random orthogonal basis.", "R");
|
||||
PARAM_INT_IN("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
|
||||
@@ -191,10 +191,12 @@ int main(int argc, char *argv[])
|
||||
tree = RSModel::MAX_RP_TREE;
|
||||
else if (treeType == "ub")
|
||||
tree = RSModel::UB_TREE;
|
||||
else if (treeType == "octree")
|
||||
tree = RSModel::OCTREE;
|
||||
else
|
||||
Log::Fatal << "Unknown tree type '" << treeType << "; valid choices are "
|
||||
<< "'kd', 'vp', 'rp', 'max-rp', 'ub', 'cover', 'r', 'r-star', 'x', "
|
||||
<< "'ball', 'hilbert-r', 'r-plus' and 'r-plus-plus'." << endl;
|
||||
<< "'ball', 'hilbert-r', 'r-plus', 'r-plus-plus', and 'octree'." << endl;
|
||||
|
||||
rs.TreeType() = tree;
|
||||
rs.RandomBasis() = randomBasis;
|
||||
|
||||
@@ -29,7 +29,8 @@ RSModel::RSModel(TreeTypes treeType, bool randomBasis) :
|
||||
vpTreeRS(NULL),
|
||||
rpTreeRS(NULL),
|
||||
maxRPTreeRS(NULL),
|
||||
ubTreeRS(NULL)
|
||||
ubTreeRS(NULL),
|
||||
octreeRS(NULL)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
@@ -164,6 +165,28 @@ void RSModel::BuildModel(arma::mat&& referenceSet,
|
||||
ubTreeRS = new RSType<tree::UBTree>(move(referenceSet),
|
||||
naive, singleMode);
|
||||
break;
|
||||
|
||||
case OCTREE:
|
||||
// If necessary, build the octree.
|
||||
if (naive)
|
||||
{
|
||||
octreeRS = new RSType<tree::Octree>(move(referenceSet), naive,
|
||||
singleMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<size_t> oldFromNewReferences;
|
||||
RSType<tree::Octree>::Tree* octree =
|
||||
new RSType<tree::Octree>::Tree(move(referenceSet),
|
||||
oldFromNewReferences, leafSize);
|
||||
octreeRS = new RSType<tree::Octree>(octree, singleMode);
|
||||
|
||||
// Give the model ownership of the tree and the mappings.
|
||||
octreeRS->treeOwner = true;
|
||||
octreeRS->oldFromNewReferences = move(oldFromNewReferences);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!naive)
|
||||
@@ -301,6 +324,38 @@ void RSModel::Search(arma::mat&& querySet,
|
||||
case UB_TREE:
|
||||
ubTreeRS->Search(querySet, range, neighbors, distances);
|
||||
break;
|
||||
|
||||
case OCTREE:
|
||||
if (!octreeRS->Naive() && !octreeRS->SingleMode())
|
||||
{
|
||||
// Build a query tree and search.
|
||||
Timer::Start("tree_building");
|
||||
Log::Info << "Building query tree..." << endl;
|
||||
vector<size_t> oldFromNewQueries;
|
||||
RSType<tree::Octree>::Tree queryTree(move(querySet), oldFromNewQueries,
|
||||
leafSize);
|
||||
Log::Info << "Tree built." << endl;
|
||||
Timer::Stop("tree_building");
|
||||
|
||||
vector<vector<size_t>> neighborsOut;
|
||||
vector<vector<double>> distancesOut;
|
||||
octreeRS->Search(&queryTree, range, neighborsOut, distancesOut);
|
||||
|
||||
// Remap the query points.
|
||||
neighbors.resize(queryTree.Dataset().n_cols);
|
||||
distances.resize(queryTree.Dataset().n_cols);
|
||||
for (size_t i = 0; i < queryTree.Dataset().n_cols; ++i)
|
||||
{
|
||||
neighbors[oldFromNewQueries[i]] = neighborsOut[i];
|
||||
distances[oldFromNewQueries[i]] = distancesOut[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Search without building a second tree.
|
||||
octreeRS->Search(querySet, range, neighbors, distances);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,6 +426,10 @@ void RSModel::Search(const math::Range& range,
|
||||
case UB_TREE:
|
||||
ubTreeRS->Search(range, neighbors, distances);
|
||||
break;
|
||||
|
||||
case OCTREE:
|
||||
octreeRS->Search(range, neighbors, distances);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,6 +464,8 @@ std::string RSModel::TreeName() const
|
||||
return "random projection tree (max split)";
|
||||
case UB_TREE:
|
||||
return "UB tree";
|
||||
case OCTREE:
|
||||
return "octree";
|
||||
default:
|
||||
return "unknown tree";
|
||||
}
|
||||
@@ -413,32 +474,20 @@ std::string RSModel::TreeName() const
|
||||
// Clean memory.
|
||||
void RSModel::CleanMemory()
|
||||
{
|
||||
if (kdTreeRS)
|
||||
delete kdTreeRS;
|
||||
if (coverTreeRS)
|
||||
delete coverTreeRS;
|
||||
if (rTreeRS)
|
||||
delete rTreeRS;
|
||||
if (rStarTreeRS)
|
||||
delete rStarTreeRS;
|
||||
if (ballTreeRS)
|
||||
delete ballTreeRS;
|
||||
if (xTreeRS)
|
||||
delete xTreeRS;
|
||||
if (hilbertRTreeRS)
|
||||
delete hilbertRTreeRS;
|
||||
if (rPlusTreeRS)
|
||||
delete rPlusTreeRS;
|
||||
if (rPlusPlusTreeRS)
|
||||
delete rPlusPlusTreeRS;
|
||||
if (vpTreeRS)
|
||||
delete vpTreeRS;
|
||||
if (rpTreeRS)
|
||||
delete rpTreeRS;
|
||||
if (maxRPTreeRS)
|
||||
delete maxRPTreeRS;
|
||||
if (ubTreeRS)
|
||||
delete ubTreeRS;
|
||||
delete kdTreeRS;
|
||||
delete coverTreeRS;
|
||||
delete rTreeRS;
|
||||
delete rStarTreeRS;
|
||||
delete ballTreeRS;
|
||||
delete xTreeRS;
|
||||
delete hilbertRTreeRS;
|
||||
delete rPlusTreeRS;
|
||||
delete rPlusPlusTreeRS;
|
||||
delete vpTreeRS;
|
||||
delete rpTreeRS;
|
||||
delete maxRPTreeRS;
|
||||
delete ubTreeRS;
|
||||
delete octreeRS;
|
||||
|
||||
kdTreeRS = NULL;
|
||||
coverTreeRS = NULL;
|
||||
@@ -453,4 +502,5 @@ void RSModel::CleanMemory()
|
||||
rpTreeRS = NULL;
|
||||
maxRPTreeRS = NULL;
|
||||
ubTreeRS = NULL;
|
||||
octreeRS = NULL;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <mlpack/core/tree/binary_space_tree.hpp>
|
||||
#include <mlpack/core/tree/cover_tree.hpp>
|
||||
#include <mlpack/core/tree/rectangle_tree.hpp>
|
||||
#include <mlpack/core/tree/octree.hpp>
|
||||
|
||||
#include "range_search.hpp"
|
||||
|
||||
@@ -36,7 +37,8 @@ class RSModel
|
||||
VP_TREE,
|
||||
RP_TREE,
|
||||
MAX_RP_TREE,
|
||||
UB_TREE
|
||||
UB_TREE,
|
||||
OCTREE
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -84,6 +86,8 @@ class RSModel
|
||||
//! Universal B tree based range search object
|
||||
//! (NULL if not in use).
|
||||
RSType<tree::UBTree>* ubTreeRS;
|
||||
//! Octree-based range search object (NULL if not in use).
|
||||
RSType<tree::Octree>* octreeRS;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -81,6 +81,10 @@ void RSModel::Serialize(Archive& ar, const unsigned int /* version */)
|
||||
case UB_TREE:
|
||||
ar & CreateNVP(ubTreeRS, "range_search_model");
|
||||
break;
|
||||
|
||||
case OCTREE:
|
||||
ar & CreateNVP(octreeRS, "range_search_model");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +116,8 @@ inline const arma::mat& RSModel::Dataset() const
|
||||
return maxRPTreeRS->ReferenceSet();
|
||||
else if (ubTreeRS)
|
||||
return ubTreeRS->ReferenceSet();
|
||||
else if (octreeRS)
|
||||
return octreeRS->ReferenceSet();
|
||||
|
||||
throw std::runtime_error("no range search model initialized");
|
||||
}
|
||||
@@ -144,6 +150,8 @@ inline bool RSModel::SingleMode() const
|
||||
return maxRPTreeRS->SingleMode();
|
||||
else if (ubTreeRS)
|
||||
return ubTreeRS->SingleMode();
|
||||
else if (octreeRS)
|
||||
return octreeRS->SingleMode();
|
||||
|
||||
throw std::runtime_error("no range search model initialized");
|
||||
}
|
||||
@@ -176,6 +184,8 @@ inline bool& RSModel::SingleMode()
|
||||
return maxRPTreeRS->SingleMode();
|
||||
else if (ubTreeRS)
|
||||
return ubTreeRS->SingleMode();
|
||||
else if (octreeRS)
|
||||
return octreeRS->SingleMode();
|
||||
|
||||
throw std::runtime_error("no range search model initialized");
|
||||
}
|
||||
@@ -208,6 +218,8 @@ inline bool RSModel::Naive() const
|
||||
return maxRPTreeRS->Naive();
|
||||
else if (ubTreeRS)
|
||||
return ubTreeRS->Naive();
|
||||
else if (octreeRS)
|
||||
return octreeRS->Naive();
|
||||
|
||||
throw std::runtime_error("no range search model initialized");
|
||||
}
|
||||
@@ -240,6 +252,8 @@ inline bool& RSModel::Naive()
|
||||
return maxRPTreeRS->Naive();
|
||||
else if (ubTreeRS)
|
||||
return ubTreeRS->Naive();
|
||||
else if (octreeRS)
|
||||
return octreeRS->Naive();
|
||||
|
||||
throw std::runtime_error("no range search model initialized");
|
||||
}
|
||||
|
||||
@@ -65,10 +65,11 @@ PARAM_INT_IN("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_IN("tree_type", "Type of tree to use: 'kd', 'ub', 'cover', 'r', "
|
||||
"'x', 'r-star', 'hilbert-r', 'r-plus', 'r-plus-plus'.", "t", "kd");
|
||||
"'x', 'r-star', 'hilbert-r', 'r-plus', 'r-plus-plus', 'octree'.", "t",
|
||||
"kd");
|
||||
PARAM_INT_IN("leaf_size", "Leaf size for tree building (used for kd-trees, "
|
||||
"UB trees, R trees, R* trees, X trees, Hilbert R trees, R+ trees and "
|
||||
"R++ trees).", "l", 20);
|
||||
"UB trees, R trees, R* trees, X trees, Hilbert R trees, R+ trees, "
|
||||
"R++ trees, and octrees).", "l", 20);
|
||||
PARAM_FLAG("random_basis", "Before tree-building, project the data onto a "
|
||||
"random orthogonal basis.", "R");
|
||||
PARAM_INT_IN("seed", "Random seed (if 0, std::time(NULL) is used).", "s", 0);
|
||||
@@ -182,10 +183,12 @@ int main(int argc, char *argv[])
|
||||
tree = RANNModel::R_PLUS_PLUS_TREE;
|
||||
else if (treeType == "ub")
|
||||
tree = RANNModel::UB_TREE;
|
||||
else if (treeType == "octree")
|
||||
tree = RANNModel::OCTREE;
|
||||
else
|
||||
Log::Fatal << "Unknown tree type '" << treeType << "'; valid choices are "
|
||||
<< "'kd', 'ub', 'cover', 'r', 'r-star', 'x', 'hilbert-r', "
|
||||
<< "'r-plus' and 'r-plus-plus'." << endl;
|
||||
<< "'r-plus', 'r-plus-plus', 'octree'." << endl;
|
||||
|
||||
rann.TreeType() = tree;
|
||||
rann.RandomBasis() = randomBasis;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <mlpack/core/tree/binary_space_tree.hpp>
|
||||
#include <mlpack/core/tree/cover_tree.hpp>
|
||||
#include <mlpack/core/tree/rectangle_tree.hpp>
|
||||
#include <mlpack/core/tree/octree.hpp>
|
||||
|
||||
#include "ra_search.hpp"
|
||||
|
||||
@@ -44,7 +45,8 @@ class RAModel
|
||||
HILBERT_R_TREE,
|
||||
R_PLUS_TREE,
|
||||
R_PLUS_PLUS_TREE,
|
||||
UB_TREE
|
||||
UB_TREE,
|
||||
OCTREE
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -85,6 +87,8 @@ class RAModel
|
||||
RAType<tree::RPlusPlusTree>* rPlusPlusTreeRA;
|
||||
//! Non-NULL if the UB tree is used.
|
||||
RAType<tree::UBTree>* ubTreeRA;
|
||||
//! Non-NULL if the octree is used.
|
||||
RAType<tree::Octree>* octreeRA;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,8 @@ RAModel<SortPolicy>::RAModel(const TreeTypes treeType, const bool randomBasis) :
|
||||
hilbertRTreeRA(NULL),
|
||||
rPlusTreeRA(NULL),
|
||||
rPlusPlusTreeRA(NULL),
|
||||
ubTreeRA(NULL)
|
||||
ubTreeRA(NULL),
|
||||
octreeRA(NULL)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
@@ -34,24 +35,16 @@ RAModel<SortPolicy>::RAModel(const TreeTypes treeType, const bool randomBasis) :
|
||||
template<typename SortPolicy>
|
||||
RAModel<SortPolicy>::~RAModel()
|
||||
{
|
||||
if (kdTreeRA)
|
||||
delete kdTreeRA;
|
||||
if (coverTreeRA)
|
||||
delete coverTreeRA;
|
||||
if (rTreeRA)
|
||||
delete rTreeRA;
|
||||
if (rStarTreeRA)
|
||||
delete rStarTreeRA;
|
||||
if (xTreeRA)
|
||||
delete xTreeRA;
|
||||
if (hilbertRTreeRA)
|
||||
delete hilbertRTreeRA;
|
||||
if (rPlusTreeRA)
|
||||
delete rPlusTreeRA;
|
||||
if (rPlusPlusTreeRA)
|
||||
delete rPlusPlusTreeRA;
|
||||
if (ubTreeRA)
|
||||
delete ubTreeRA;
|
||||
delete kdTreeRA;
|
||||
delete coverTreeRA;
|
||||
delete rTreeRA;
|
||||
delete rStarTreeRA;
|
||||
delete xTreeRA;
|
||||
delete hilbertRTreeRA;
|
||||
delete rPlusTreeRA;
|
||||
delete rPlusPlusTreeRA;
|
||||
delete ubTreeRA;
|
||||
delete octreeRA;
|
||||
}
|
||||
|
||||
template<typename SortPolicy>
|
||||
@@ -66,24 +59,16 @@ void RAModel<SortPolicy>::Serialize(Archive& ar,
|
||||
// This should never happen, but just in case, be clean with memory.
|
||||
if (Archive::is_loading::value)
|
||||
{
|
||||
if (kdTreeRA)
|
||||
delete kdTreeRA;
|
||||
if (coverTreeRA)
|
||||
delete coverTreeRA;
|
||||
if (rTreeRA)
|
||||
delete rTreeRA;
|
||||
if (rStarTreeRA)
|
||||
delete rStarTreeRA;
|
||||
if (xTreeRA)
|
||||
delete xTreeRA;
|
||||
if (hilbertRTreeRA)
|
||||
delete hilbertRTreeRA;
|
||||
if (rPlusTreeRA)
|
||||
delete rPlusTreeRA;
|
||||
if (rPlusPlusTreeRA)
|
||||
delete rPlusPlusTreeRA;
|
||||
if (ubTreeRA)
|
||||
delete ubTreeRA;
|
||||
delete kdTreeRA;
|
||||
delete coverTreeRA;
|
||||
delete rTreeRA;
|
||||
delete rStarTreeRA;
|
||||
delete xTreeRA;
|
||||
delete hilbertRTreeRA;
|
||||
delete rPlusTreeRA;
|
||||
delete rPlusPlusTreeRA;
|
||||
delete ubTreeRA;
|
||||
delete octreeRA;
|
||||
|
||||
// Set all the pointers to NULL.
|
||||
kdTreeRA = NULL;
|
||||
@@ -127,6 +112,9 @@ void RAModel<SortPolicy>::Serialize(Archive& ar,
|
||||
case UB_TREE:
|
||||
ar & data::CreateNVP(ubTreeRA, "ra_model");
|
||||
break;
|
||||
case OCTREE:
|
||||
ar & data::CreateNVP(octreeRA, "ra_model");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +139,8 @@ const arma::mat& RAModel<SortPolicy>::Dataset() const
|
||||
return rPlusPlusTreeRA->ReferenceSet();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->ReferenceSet();
|
||||
else if (octreeRA)
|
||||
return octreeRA->ReferenceSet();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -177,6 +167,8 @@ bool RAModel<SortPolicy>::Naive() const
|
||||
return rPlusPlusTreeRA->Naive();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->Naive();
|
||||
else if (octreeRA)
|
||||
return octreeRA->Naive();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -203,6 +195,8 @@ bool& RAModel<SortPolicy>::Naive()
|
||||
return rPlusPlusTreeRA->Naive();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->Naive();
|
||||
else if (octreeRA)
|
||||
return octreeRA->Naive();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -229,6 +223,8 @@ bool RAModel<SortPolicy>::SingleMode() const
|
||||
return rPlusPlusTreeRA->SingleMode();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->SingleMode();
|
||||
else if (octreeRA)
|
||||
return octreeRA->SingleMode();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -255,6 +251,8 @@ bool& RAModel<SortPolicy>::SingleMode()
|
||||
return rPlusPlusTreeRA->SingleMode();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->SingleMode();
|
||||
else if (octreeRA)
|
||||
return octreeRA->SingleMode();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -281,6 +279,8 @@ double RAModel<SortPolicy>::Tau() const
|
||||
return rPlusPlusTreeRA->Tau();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->Tau();
|
||||
else if (octreeRA)
|
||||
return octreeRA->Tau();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -307,6 +307,8 @@ double& RAModel<SortPolicy>::Tau()
|
||||
return rPlusPlusTreeRA->Tau();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->Tau();
|
||||
else if (octreeRA)
|
||||
return octreeRA->Tau();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -333,6 +335,8 @@ double RAModel<SortPolicy>::Alpha() const
|
||||
return rPlusPlusTreeRA->Alpha();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->Alpha();
|
||||
else if (octreeRA)
|
||||
return octreeRA->Alpha();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -359,6 +363,8 @@ double& RAModel<SortPolicy>::Alpha()
|
||||
return rPlusPlusTreeRA->Alpha();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->Alpha();
|
||||
else if (octreeRA)
|
||||
return octreeRA->Alpha();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -385,6 +391,8 @@ bool RAModel<SortPolicy>::SampleAtLeaves() const
|
||||
return rPlusPlusTreeRA->SampleAtLeaves();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->SampleAtLeaves();
|
||||
else if (octreeRA)
|
||||
return octreeRA->SampleAtLeaves();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -411,6 +419,8 @@ bool& RAModel<SortPolicy>::SampleAtLeaves()
|
||||
return rPlusPlusTreeRA->SampleAtLeaves();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->SampleAtLeaves();
|
||||
else if (octreeRA)
|
||||
return octreeRA->SampleAtLeaves();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -437,6 +447,8 @@ bool RAModel<SortPolicy>::FirstLeafExact() const
|
||||
return rPlusPlusTreeRA->FirstLeafExact();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->FirstLeafExact();
|
||||
else if (octreeRA)
|
||||
return octreeRA->FirstLeafExact();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -463,6 +475,8 @@ bool& RAModel<SortPolicy>::FirstLeafExact()
|
||||
return rPlusPlusTreeRA->FirstLeafExact();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->FirstLeafExact();
|
||||
else if (octreeRA)
|
||||
return octreeRA->FirstLeafExact();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -489,6 +503,8 @@ size_t RAModel<SortPolicy>::SingleSampleLimit() const
|
||||
return rPlusPlusTreeRA->SingleSampleLimit();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->SingleSampleLimit();
|
||||
else if (octreeRA)
|
||||
return octreeRA->SingleSampleLimit();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -515,6 +531,8 @@ size_t& RAModel<SortPolicy>::SingleSampleLimit()
|
||||
return rPlusPlusTreeRA->SingleSampleLimit();
|
||||
else if (ubTreeRA)
|
||||
return ubTreeRA->SingleSampleLimit();
|
||||
else if (octreeRA)
|
||||
return octreeRA->SingleSampleLimit();
|
||||
|
||||
throw std::runtime_error("no rank-approximate nearest neighbor search model "
|
||||
"initialized");
|
||||
@@ -570,24 +588,16 @@ void RAModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
|
||||
}
|
||||
|
||||
// Clean memory, if necessary.
|
||||
if (kdTreeRA)
|
||||
delete kdTreeRA;
|
||||
if (coverTreeRA)
|
||||
delete coverTreeRA;
|
||||
if (rTreeRA)
|
||||
delete rTreeRA;
|
||||
if (rStarTreeRA)
|
||||
delete rStarTreeRA;
|
||||
if (xTreeRA)
|
||||
delete xTreeRA;
|
||||
if (hilbertRTreeRA)
|
||||
delete hilbertRTreeRA;
|
||||
if (rPlusTreeRA)
|
||||
delete rPlusTreeRA;
|
||||
if (rPlusPlusTreeRA)
|
||||
delete rPlusPlusTreeRA;
|
||||
if (ubTreeRA)
|
||||
delete ubTreeRA;
|
||||
delete kdTreeRA;
|
||||
delete coverTreeRA;
|
||||
delete rTreeRA;
|
||||
delete rStarTreeRA;
|
||||
delete xTreeRA;
|
||||
delete hilbertRTreeRA;
|
||||
delete rPlusTreeRA;
|
||||
delete rPlusPlusTreeRA;
|
||||
delete ubTreeRA;
|
||||
delete octreeRA;
|
||||
|
||||
if (randomBasis)
|
||||
referenceSet = q * referenceSet;
|
||||
@@ -652,6 +662,26 @@ void RAModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
|
||||
ubTreeRA = new RAType<tree::UBTree>(std::move(referenceSet),
|
||||
naive, singleMode);
|
||||
break;
|
||||
case OCTREE:
|
||||
// Build tree, if necessary.
|
||||
if (naive)
|
||||
{
|
||||
octreeRA = new RAType<tree::Octree>(std::move(referenceSet), naive,
|
||||
singleMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<size_t> oldFromNewReferences;
|
||||
typename RAType<tree::Octree>::Tree* octree =
|
||||
new typename RAType<tree::Octree>::Tree(std::move(referenceSet),
|
||||
oldFromNewReferences, leafSize);
|
||||
octreeRA = new RAType<tree::Octree>(octree, singleMode);
|
||||
|
||||
// Give the model ownership of the tree.
|
||||
octreeRA->treeOwner = true;
|
||||
octreeRA->oldFromNewReferences = oldFromNewReferences;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!naive)
|
||||
@@ -745,6 +775,37 @@ void RAModel<SortPolicy>::Search(arma::mat&& querySet,
|
||||
// No mapping necessary.
|
||||
ubTreeRA->Search(querySet, k, neighbors, distances);
|
||||
break;
|
||||
case OCTREE:
|
||||
if (!octreeRA->Naive() && !octreeRA->SingleMode())
|
||||
{
|
||||
// Build a second tree and search.
|
||||
Timer::Start("tree_building");
|
||||
Log::Info << "Building query tree..." << std::endl;
|
||||
std::vector<size_t> oldFromNewQueries;
|
||||
typename RAType<tree::Octree>::Tree queryTree(std::move(querySet),
|
||||
oldFromNewQueries, leafSize);
|
||||
Log::Info << "Tree built." << std::endl;
|
||||
Timer::Stop("tree_building");
|
||||
|
||||
arma::Mat<size_t> neighborsOut;
|
||||
arma::mat distancesOut;
|
||||
octreeRA->Search(&queryTree, k, neighborsOut, distancesOut);
|
||||
|
||||
// Unmap the query points.
|
||||
distances.set_size(distancesOut.n_rows, distancesOut.n_cols);
|
||||
neighbors.set_size(neighborsOut.n_rows, neighborsOut.n_cols);
|
||||
for (size_t i = 0; i < neighborsOut.n_cols; ++i)
|
||||
{
|
||||
neighbors.col(oldFromNewQueries[i]) = neighborsOut.col(i);
|
||||
distances.col(oldFromNewQueries[i]) = distancesOut.col(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Search without building a second tree.
|
||||
octreeRA->Search(querySet, k, neighbors, distances);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,6 +852,9 @@ void RAModel<SortPolicy>::Search(const size_t k,
|
||||
case UB_TREE:
|
||||
ubTreeRA->Search(k, neighbors, distances);
|
||||
break;
|
||||
case OCTREE:
|
||||
octreeRA->Search(k, neighbors, distances);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,6 +881,8 @@ std::string RAModel<SortPolicy>::TreeName() const
|
||||
return "R++ tree";
|
||||
case UB_TREE:
|
||||
return "UB tree";
|
||||
case OCTREE:
|
||||
return "octree";
|
||||
default:
|
||||
return "unknown tree";
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ add_executable(mlpack_test
|
||||
network_util_test.cpp
|
||||
nmf_test.cpp
|
||||
nystroem_method_test.cpp
|
||||
octree_test.cpp
|
||||
pca_test.cpp
|
||||
perceptron_test.cpp
|
||||
quic_svd_test.cpp
|
||||
|
||||
@@ -1066,7 +1066,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
|
||||
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
|
||||
|
||||
// Build all the possible models.
|
||||
KNNModel models[26];
|
||||
KNNModel models[28];
|
||||
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
|
||||
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
|
||||
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
|
||||
@@ -1093,6 +1093,8 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
|
||||
models[23] = KNNModel(KNNModel::TreeTypes::MAX_RP_TREE, false);
|
||||
models[24] = KNNModel(KNNModel::TreeTypes::UB_TREE, true);
|
||||
models[25] = KNNModel(KNNModel::TreeTypes::UB_TREE, false);
|
||||
models[26] = KNNModel(KNNModel::TreeTypes::OCTREE, true);
|
||||
models[27] = KNNModel(KNNModel::TreeTypes::OCTREE, false);
|
||||
|
||||
for (size_t j = 0; j < 2; ++j)
|
||||
{
|
||||
@@ -1102,7 +1104,7 @@ BOOST_AUTO_TEST_CASE(KNNModelTest)
|
||||
arma::mat baselineDistances;
|
||||
knn.Search(queryData, 3, baselineNeighbors, baselineDistances);
|
||||
|
||||
for (size_t i = 0; i < 26; ++i)
|
||||
for (size_t i = 0; i < 28; ++i)
|
||||
{
|
||||
// We only have std::move() constructors so make a copy of our data.
|
||||
arma::mat referenceCopy(referenceData);
|
||||
@@ -1147,7 +1149,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
|
||||
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
|
||||
|
||||
// Build all the possible models.
|
||||
KNNModel models[26];
|
||||
KNNModel models[28];
|
||||
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
|
||||
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
|
||||
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, true);
|
||||
@@ -1174,6 +1176,8 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
|
||||
models[23] = KNNModel(KNNModel::TreeTypes::MAX_RP_TREE, false);
|
||||
models[24] = KNNModel(KNNModel::TreeTypes::UB_TREE, true);
|
||||
models[25] = KNNModel(KNNModel::TreeTypes::UB_TREE, false);
|
||||
models[26] = KNNModel(KNNModel::TreeTypes::OCTREE, true);
|
||||
models[27] = KNNModel(KNNModel::TreeTypes::OCTREE, false);
|
||||
|
||||
for (size_t j = 0; j < 2; ++j)
|
||||
{
|
||||
@@ -1183,7 +1187,7 @@ BOOST_AUTO_TEST_CASE(KNNModelMonochromaticTest)
|
||||
arma::mat baselineDistances;
|
||||
knn.Search(3, baselineNeighbors, baselineDistances);
|
||||
|
||||
for (size_t i = 0; i < 26; ++i)
|
||||
for (size_t i = 0; i < 28; ++i)
|
||||
{
|
||||
// We only have a std::move() constructor... so copy the data.
|
||||
arma::mat referenceCopy(referenceData);
|
||||
|
||||
@@ -625,7 +625,7 @@ BOOST_AUTO_TEST_CASE(RAModelTest)
|
||||
data::Load("rann_test_q_3_100.csv", queryData, true);
|
||||
|
||||
// Build all the possible models.
|
||||
KNNModel models[18];
|
||||
KNNModel models[20];
|
||||
models[0] = KNNModel(KNNModel::TreeTypes::KD_TREE, false);
|
||||
models[1] = KNNModel(KNNModel::TreeTypes::KD_TREE, true);
|
||||
models[2] = KNNModel(KNNModel::TreeTypes::COVER_TREE, false);
|
||||
@@ -644,13 +644,15 @@ BOOST_AUTO_TEST_CASE(RAModelTest)
|
||||
models[15] = KNNModel(KNNModel::TreeTypes::R_PLUS_PLUS_TREE, true);
|
||||
models[16] = KNNModel(KNNModel::TreeTypes::UB_TREE, false);
|
||||
models[17] = KNNModel(KNNModel::TreeTypes::UB_TREE, true);
|
||||
models[18] = KNNModel(KNNModel::TreeTypes::OCTREE, false);
|
||||
models[19] = KNNModel(KNNModel::TreeTypes::OCTREE, 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 < 18; ++i)
|
||||
for (size_t i = 0; i < 20; ++i)
|
||||
{
|
||||
// We only have std::move() constructors so make a copy of our data.
|
||||
arma::mat referenceCopy(referenceData);
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
* @file octree_test.cpp
|
||||
* @author Ryan Curtin
|
||||
*
|
||||
* Test various properties of the Octree.
|
||||
*/
|
||||
#include <mlpack/core.hpp>
|
||||
#include <mlpack/core/tree/octree.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_tools.hpp"
|
||||
#include "serialization.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
using namespace mlpack::math;
|
||||
using namespace mlpack::tree;
|
||||
using namespace mlpack::metric;
|
||||
using namespace mlpack::bound;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(OctreeTest);
|
||||
|
||||
/**
|
||||
* Build a quad-tree (2-d octree) on 4 points, and guarantee four points are
|
||||
* created.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SimpleQuadtreeTest)
|
||||
{
|
||||
// Four corners of the unit square.
|
||||
arma::mat dataset("0 0 1 1; 0 1 0 1");
|
||||
|
||||
Octree<> t(dataset, 1);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(t.NumChildren(), 4);
|
||||
BOOST_REQUIRE_EQUAL(t.Dataset().n_cols, 4);
|
||||
BOOST_REQUIRE_EQUAL(t.Dataset().n_rows, 2);
|
||||
BOOST_REQUIRE_EQUAL(t.NumDescendants(), 4);
|
||||
BOOST_REQUIRE_EQUAL(t.NumPoints(), 0);
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(t.Child(i).NumDescendants(), 1);
|
||||
BOOST_REQUIRE_EQUAL(t.Child(i).NumPoints(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an octree on 3 points and make sure that only three children are
|
||||
* created.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(OctreeMissingChildTest)
|
||||
{
|
||||
// Only three corners of the unit square.
|
||||
arma::mat dataset("0 0 1; 0 1 1");
|
||||
|
||||
Octree<> t(dataset, 1);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(t.NumChildren(), 3);
|
||||
BOOST_REQUIRE_EQUAL(t.Dataset().n_cols, 3);
|
||||
BOOST_REQUIRE_EQUAL(t.Dataset().n_rows, 2);
|
||||
BOOST_REQUIRE_EQUAL(t.NumDescendants(), 3);
|
||||
BOOST_REQUIRE_EQUAL(t.NumPoints(), 0);
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(t.Child(i).NumDescendants(), 1);
|
||||
BOOST_REQUIRE_EQUAL(t.Child(i).NumPoints(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that building an empty octree does not fail.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(EmptyOctreeTest)
|
||||
{
|
||||
arma::mat dataset;
|
||||
Octree<> t(dataset);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(t.NumChildren(), 0);
|
||||
BOOST_REQUIRE_EQUAL(t.Dataset().n_cols, 0);
|
||||
BOOST_REQUIRE_EQUAL(t.Dataset().n_rows, 0);
|
||||
BOOST_REQUIRE_EQUAL(t.NumDescendants(), 0);
|
||||
BOOST_REQUIRE_EQUAL(t.NumPoints(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that maxLeafSize is respected.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(MaxLeafSizeTest)
|
||||
{
|
||||
arma::mat dataset(5, 15, arma::fill::randu);
|
||||
Octree<> t1(dataset, 20);
|
||||
Octree<> t2(std::move(dataset), 20);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(t1.NumChildren(), 0);
|
||||
BOOST_REQUIRE_EQUAL(t1.NumDescendants(), 15);
|
||||
BOOST_REQUIRE_EQUAL(t1.NumPoints(), 15);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(t2.NumChildren(), 0);
|
||||
BOOST_REQUIRE_EQUAL(t2.NumDescendants(), 15);
|
||||
BOOST_REQUIRE_EQUAL(t2.NumPoints(), 15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the mappings given are correct.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(MappingsTest)
|
||||
{
|
||||
// Test with both constructors.
|
||||
arma::mat dataset(3, 5, arma::fill::randu);
|
||||
arma::mat datacopy(dataset);
|
||||
std::vector<size_t> oldFromNewCopy, oldFromNewMove;
|
||||
|
||||
Octree<> t1(dataset, oldFromNewCopy, 1);
|
||||
Octree<> t2(std::move(dataset), oldFromNewMove, 1);
|
||||
|
||||
for (size_t i = 0; i < oldFromNewCopy.size(); ++i)
|
||||
{
|
||||
BOOST_REQUIRE_SMALL(arma::norm(datacopy.col(oldFromNewCopy[i]) -
|
||||
t1.Dataset().col(i)), 1e-3);
|
||||
BOOST_REQUIRE_SMALL(arma::norm(datacopy.col(oldFromNewMove[i]) -
|
||||
t2.Dataset().col(i)), 1e-3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the reverse mappings are correct too.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(ReverseMappingsTest)
|
||||
{
|
||||
// Test with both constructors.
|
||||
arma::mat dataset(3, 300, arma::fill::randu);
|
||||
arma::mat datacopy(dataset);
|
||||
std::vector<size_t> oldFromNewCopy, oldFromNewMove, newFromOldCopy,
|
||||
newFromOldMove;
|
||||
|
||||
Octree<> t1(dataset, oldFromNewCopy, newFromOldCopy);
|
||||
Octree<> t2(std::move(dataset), oldFromNewMove, newFromOldMove);
|
||||
|
||||
for (size_t i = 0; i < oldFromNewCopy.size(); ++i)
|
||||
{
|
||||
BOOST_REQUIRE_SMALL(arma::norm(datacopy.col(oldFromNewCopy[i]) -
|
||||
t1.Dataset().col(i)), 1e-3);
|
||||
BOOST_REQUIRE_SMALL(arma::norm(datacopy.col(oldFromNewMove[i]) -
|
||||
t2.Dataset().col(i)), 1e-3);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(newFromOldCopy[oldFromNewCopy[i]], i);
|
||||
BOOST_REQUIRE_EQUAL(newFromOldMove[oldFromNewMove[i]], i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure no children at the same level are overlapping.
|
||||
*/
|
||||
template<typename TreeType>
|
||||
void CheckOverlap(TreeType& node)
|
||||
{
|
||||
// Check each combination of children.
|
||||
for (size_t i = 0; i < node.NumChildren(); ++i)
|
||||
for (size_t j = i + 1; j < node.NumChildren(); ++j)
|
||||
BOOST_REQUIRE_EQUAL(node.Child(i).Bound().Overlap(node.Child(j).Bound()),
|
||||
0.0); // We need exact equality here.
|
||||
|
||||
for (size_t i = 0; i < node.NumChildren(); ++i)
|
||||
CheckOverlap(node.Child(i));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(OverlapTest)
|
||||
{
|
||||
// Test with both constructors.
|
||||
arma::mat dataset(3, 300, arma::fill::randu);
|
||||
|
||||
Octree<> t1(dataset);
|
||||
Octree<> t2(std::move(dataset));
|
||||
|
||||
CheckOverlap(t1);
|
||||
CheckOverlap(t2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure no points are further than the furthest point distance, and that no
|
||||
* descendants are further than the furthest descendant distance.
|
||||
*/
|
||||
template<typename TreeType>
|
||||
void CheckFurthestDistances(TreeType& node)
|
||||
{
|
||||
arma::vec center;
|
||||
node.Center(center);
|
||||
|
||||
// Compare points held in the node.
|
||||
for (size_t i = 0; i < node.NumPoints(); ++i)
|
||||
{
|
||||
// Handle floating-point inaccuracies.
|
||||
BOOST_REQUIRE_LE(metric::EuclideanDistance::Evaluate(node.Dataset().col(node.Point(i)),
|
||||
center), node.FurthestPointDistance() * (1 + 1e-5));
|
||||
}
|
||||
|
||||
// Compare descendants held in the node.
|
||||
for (size_t i = 0; i < node.NumDescendants(); ++i)
|
||||
{
|
||||
// Handle floating-point inaccuracies.
|
||||
BOOST_REQUIRE_LE(metric::EuclideanDistance::Evaluate(node.Dataset().col(node.Descendant(i)),
|
||||
center), node.FurthestDescendantDistance() * (1 + 1e-5));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node.NumChildren(); ++i)
|
||||
CheckFurthestDistances(node.Child(i));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(FurthestDistanceTest)
|
||||
{
|
||||
// Test with both constructors.
|
||||
arma::mat dataset(3, 500, arma::fill::randu);
|
||||
|
||||
Octree<> t1(dataset);
|
||||
Octree<> t2(std::move(dataset));
|
||||
|
||||
CheckFurthestDistances(t1);
|
||||
CheckFurthestDistances(t2);
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum number of children a node can have is limited by the
|
||||
* dimensionality. So we test to make sure there are no cases where we have too
|
||||
* many children.
|
||||
*/
|
||||
template<typename TreeType>
|
||||
void CheckNumChildren(TreeType& node)
|
||||
{
|
||||
BOOST_REQUIRE_LE(node.NumChildren(), std::pow(2, node.Dataset().n_rows));
|
||||
for (size_t i = 0; i < node.NumChildren(); ++i)
|
||||
CheckNumChildren(node.Child(i));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MaxNumChildrenTest)
|
||||
{
|
||||
for (size_t d = 1; d < 10; ++d)
|
||||
{
|
||||
arma::mat dataset(d, 1000 * d, arma::fill::randu);
|
||||
Octree<> t(std::move(dataset));
|
||||
|
||||
CheckNumChildren(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the copy constructor.
|
||||
*/
|
||||
template<typename TreeType>
|
||||
void CheckSameNode(TreeType& node1, TreeType& node2)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(node1.NumChildren(), node2.NumChildren());
|
||||
BOOST_REQUIRE_NE(&node1.Dataset(), &node2.Dataset());
|
||||
|
||||
// Make sure the children actually got copied.
|
||||
for (size_t i = 0; i < node1.NumChildren(); ++i)
|
||||
BOOST_REQUIRE_NE(&node1.Child(i), &node2.Child(i));
|
||||
|
||||
// Check that all the points are the same.
|
||||
BOOST_REQUIRE_EQUAL(node1.NumPoints(), node2.NumPoints());
|
||||
BOOST_REQUIRE_EQUAL(node1.NumDescendants(), node2.NumDescendants());
|
||||
for (size_t i = 0; i < node1.NumPoints(); ++i)
|
||||
BOOST_REQUIRE_EQUAL(node1.Point(i), node2.Point(i));
|
||||
for (size_t i = 0; i < node1.NumDescendants(); ++i)
|
||||
BOOST_REQUIRE_EQUAL(node1.Descendant(i), node2.Descendant(i));
|
||||
|
||||
// Check that the bound is the same.
|
||||
BOOST_REQUIRE_EQUAL(node1.Bound().Dim(), node2.Bound().Dim());
|
||||
for (size_t d = 0; d < node1.Bound().Dim(); ++d)
|
||||
{
|
||||
BOOST_REQUIRE_CLOSE(node1.Bound()[d].Lo(), node2.Bound()[d].Lo(), 1e-5);
|
||||
BOOST_REQUIRE_CLOSE(node1.Bound()[d].Hi(), node2.Bound()[d].Hi(), 1e-5);
|
||||
}
|
||||
|
||||
// Check that the furthest point and descendant distance are the same.
|
||||
BOOST_REQUIRE_CLOSE(node1.FurthestPointDistance(),
|
||||
node2.FurthestPointDistance(), 1e-5);
|
||||
BOOST_REQUIRE_CLOSE(node1.FurthestDescendantDistance(),
|
||||
node2.FurthestDescendantDistance(), 1e-5);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CopyConstructorTest)
|
||||
{
|
||||
// Use a small random dataset.
|
||||
arma::mat dataset(3, 100, arma::fill::randu);
|
||||
|
||||
Octree<> t(dataset);
|
||||
Octree<> t2(t);
|
||||
|
||||
CheckSameNode(t, t2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the move constructor.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(MoveConstructorTest)
|
||||
{
|
||||
// Use a small random dataset.
|
||||
arma::mat dataset(3, 100, arma::fill::randu);
|
||||
|
||||
Octree<> t(std::move(dataset));
|
||||
Octree<> tcopy(t);
|
||||
|
||||
// Move the tree.
|
||||
Octree<> t2(std::move(t));
|
||||
|
||||
// Make sure the original tree has no data.
|
||||
BOOST_REQUIRE_EQUAL(t.Dataset().n_rows, 0);
|
||||
BOOST_REQUIRE_EQUAL(t.Dataset().n_cols, 0);
|
||||
BOOST_REQUIRE_EQUAL(t.NumChildren(), 0);
|
||||
BOOST_REQUIRE_EQUAL(t.NumPoints(), 0);
|
||||
BOOST_REQUIRE_EQUAL(t.NumDescendants(), 0);
|
||||
BOOST_REQUIRE_SMALL(t.FurthestPointDistance(), 1e-5);
|
||||
BOOST_REQUIRE_SMALL(t.FurthestDescendantDistance(), 1e-5);
|
||||
BOOST_REQUIRE_EQUAL(t.Bound().Dim(), 0);
|
||||
|
||||
// Check that the new tree is the same as our copy.
|
||||
CheckSameNode(tcopy, t2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test serialization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SerializationTest)
|
||||
{
|
||||
// Use a small random dataset.
|
||||
arma::mat dataset(3, 500, arma::fill::randu);
|
||||
Octree<> t(std::move(dataset));
|
||||
|
||||
Octree<>* xmlTree;
|
||||
Octree<>* binaryTree;
|
||||
Octree<>* textTree;
|
||||
|
||||
SerializePointerObjectAll(&t, xmlTree, binaryTree, textTree);
|
||||
|
||||
CheckSameNode(t, *xmlTree);
|
||||
CheckSameNode(t, *binaryTree);
|
||||
CheckSameNode(t, *textTree);
|
||||
|
||||
delete xmlTree;
|
||||
delete binaryTree;
|
||||
delete textTree;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -1249,7 +1249,7 @@ BOOST_AUTO_TEST_CASE(RSModelTest)
|
||||
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
|
||||
|
||||
// Build all the possible models.
|
||||
RSModel models[26];
|
||||
RSModel models[28];
|
||||
models[0] = RSModel(RSModel::TreeTypes::KD_TREE, true);
|
||||
models[1] = RSModel(RSModel::TreeTypes::KD_TREE, false);
|
||||
models[2] = RSModel(RSModel::TreeTypes::COVER_TREE, true);
|
||||
@@ -1276,6 +1276,8 @@ BOOST_AUTO_TEST_CASE(RSModelTest)
|
||||
models[23] = RSModel(RSModel::TreeTypes::MAX_RP_TREE, false);
|
||||
models[24] = RSModel(RSModel::TreeTypes::UB_TREE, true);
|
||||
models[25] = RSModel(RSModel::TreeTypes::UB_TREE, false);
|
||||
models[26] = RSModel(RSModel::TreeTypes::OCTREE, true);
|
||||
models[27] = RSModel(RSModel::TreeTypes::OCTREE, false);
|
||||
|
||||
for (size_t j = 0; j < 2; ++j)
|
||||
{
|
||||
@@ -1289,7 +1291,7 @@ BOOST_AUTO_TEST_CASE(RSModelTest)
|
||||
vector<vector<pair<double, size_t>>> baselineSorted;
|
||||
SortResults(baselineNeighbors, baselineDistances, baselineSorted);
|
||||
|
||||
for (size_t i = 0; i < 26; ++i)
|
||||
for (size_t i = 0; i < 28; ++i)
|
||||
{
|
||||
// We only have std::move() constructors, so make a copy of our data.
|
||||
arma::mat referenceCopy(referenceData);
|
||||
@@ -1333,7 +1335,7 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest)
|
||||
arma::mat referenceData = arma::randu<arma::mat>(10, 200);
|
||||
|
||||
// Build all the possible models.
|
||||
RSModel models[26];
|
||||
RSModel models[28];
|
||||
models[0] = RSModel(RSModel::TreeTypes::KD_TREE, true);
|
||||
models[1] = RSModel(RSModel::TreeTypes::KD_TREE, false);
|
||||
models[2] = RSModel(RSModel::TreeTypes::COVER_TREE, true);
|
||||
@@ -1360,6 +1362,8 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest)
|
||||
models[23] = RSModel(RSModel::TreeTypes::MAX_RP_TREE, false);
|
||||
models[24] = RSModel(RSModel::TreeTypes::MAX_RP_TREE, true);
|
||||
models[25] = RSModel(RSModel::TreeTypes::MAX_RP_TREE, false);
|
||||
models[26] = RSModel(RSModel::TreeTypes::OCTREE, true);
|
||||
models[27] = RSModel(RSModel::TreeTypes::OCTREE, false);
|
||||
|
||||
for (size_t j = 0; j < 2; ++j)
|
||||
{
|
||||
@@ -1372,7 +1376,7 @@ BOOST_AUTO_TEST_CASE(RSModelMonochromaticTest)
|
||||
vector<vector<pair<double, size_t>>> baselineSorted;
|
||||
SortResults(baselineNeighbors, baselineDistances, baselineSorted);
|
||||
|
||||
for (size_t i = 0; i < 26; ++i)
|
||||
for (size_t i = 0; i < 28; ++i)
|
||||
{
|
||||
// We only have std::move() cosntructors, so make a copy of our data.
|
||||
arma::mat referenceCopy(referenceData);
|
||||
|
||||
Reference in New Issue
Block a user