Files
mlpack/src/mlpack/methods/emst/dtb_impl.hpp
T
Martin Lambertsen 6d5231408b Remove includes in _impl header if main header contains include
Note that we consider the wider include to `core` as being sufficient.
2024-10-15 07:25:16 +02:00

304 lines
8.4 KiB
C++

/**
* @file methods/emst/dtb_impl.hpp
* @author Bill March (march@gatech.edu)
*
* Implementation of DTB.
*
* mlpack is free software; you may redistribute it and/or modify it under the
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#ifndef MLPACK_METHODS_EMST_DTB_IMPL_HPP
#define MLPACK_METHODS_EMST_DTB_IMPL_HPP
#include "dtb_rules.hpp"
namespace mlpack {
/**
* Takes in a reference to the data set. Copies the data, builds the tree,
* and initializes all of the member variables.
*/
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
DualTreeBoruvka<DistanceType, MatType, TreeType>::DualTreeBoruvka(
const MatType& dataset,
const bool naive,
const DistanceType distance) :
tree(naive ? NULL : BuildTree<Tree>(dataset, oldFromNew)),
data(naive ? dataset : tree->Dataset()),
ownTree(!naive),
naive(naive),
connections(dataset.n_cols),
totalDist(0.0),
distance(distance)
{
edges.reserve(data.n_cols - 1); // Set size.
neighborsInComponent.set_size(data.n_cols);
neighborsOutComponent.set_size(data.n_cols);
neighborsDistances.set_size(data.n_cols);
neighborsDistances.fill(DBL_MAX);
}
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
DualTreeBoruvka<DistanceType, MatType, TreeType>::DualTreeBoruvka(
Tree* tree,
const DistanceType distance) :
tree(tree),
data(tree->Dataset()),
ownTree(false),
naive(false),
connections(data.n_cols),
totalDist(0.0),
distance(distance)
{
edges.reserve(data.n_cols - 1); // Fill with EdgePairs.
neighborsInComponent.set_size(data.n_cols);
neighborsOutComponent.set_size(data.n_cols);
neighborsDistances.set_size(data.n_cols);
neighborsDistances.fill(DBL_MAX);
}
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
DualTreeBoruvka<DistanceType, MatType, TreeType>::~DualTreeBoruvka()
{
if (ownTree)
delete tree;
}
/**
* Iteratively find the nearest neighbor of each component until the MST is
* complete.
*/
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
void DualTreeBoruvka<DistanceType, MatType, TreeType>::ComputeMST(
arma::mat& results)
{
totalDist = 0; // Reset distance.
typedef DTBRules<DistanceType, Tree> RuleType;
RuleType rules(data, connections, neighborsDistances, neighborsInComponent,
neighborsOutComponent, distance);
while (edges.size() < (data.n_cols - 1))
{
if (naive)
{
// Full O(N^2) traversal.
for (size_t i = 0; i < data.n_cols; ++i)
for (size_t j = 0; j < data.n_cols; ++j)
rules.BaseCase(i, j);
}
else
{
typename Tree::template DualTreeTraverser<RuleType> traverser(rules);
traverser.Traverse(*tree, *tree);
}
AddAllEdges();
Cleanup();
Log::Info << edges.size() << " edges found so far." << std::endl;
if (!naive)
{
Log::Info << rules.BaseCases() << " cumulative base cases." << std::endl;
Log::Info << rules.Scores() << " cumulative node combinations scored."
<< std::endl;
}
}
EmitResults(results);
Log::Info << "Total spanning tree length: " << totalDist << std::endl;
}
/**
* Adds a single edge to the edge list
*/
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
void DualTreeBoruvka<DistanceType, MatType, TreeType>::AddEdge(
const size_t e1,
const size_t e2,
const double distance)
{
Log::Assert((distance >= 0.0),
"DualTreeBoruvka::AddEdge(): distance cannot be negative.");
if (e1 < e2)
edges.push_back(EdgePair(e1, e2, distance));
else
edges.push_back(EdgePair(e2, e1, distance));
}
/**
* Adds all the edges found in one iteration to the list of neighbors.
*/
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
void DualTreeBoruvka<DistanceType, MatType, TreeType>::AddAllEdges()
{
for (size_t i = 0; i < data.n_cols; ++i)
{
size_t component = connections.Find(i);
size_t inEdge = neighborsInComponent[component];
size_t outEdge = neighborsOutComponent[component];
if (connections.Find(inEdge) != connections.Find(outEdge))
{
// totalDist = totalDist + dist;
// changed to make this agree with the cover tree code
totalDist += neighborsDistances[component];
AddEdge(inEdge, outEdge, neighborsDistances[component]);
connections.Union(inEdge, outEdge);
}
}
}
/**
* Unpermute the edge list (if necessary) and output it to results.
*/
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
void DualTreeBoruvka<DistanceType, MatType, TreeType>::EmitResults(
arma::mat& results)
{
// Sort the edges.
std::sort(edges.begin(), edges.end(), SortFun);
Log::Assert(edges.size() == data.n_cols - 1);
results.set_size(3, edges.size());
// Need to unpermute the point labels.
if (!naive && ownTree && TreeTraits<Tree>::RearrangesDataset)
{
for (size_t i = 0; i < (data.n_cols - 1); ++i)
{
// Make sure the edge list stores the smaller index first to
// make checking correctness easier
size_t ind1 = oldFromNew[edges[i].Lesser()];
size_t ind2 = oldFromNew[edges[i].Greater()];
if (ind1 < ind2)
{
edges[i].Lesser() = ind1;
edges[i].Greater() = ind2;
}
else
{
edges[i].Lesser() = ind2;
edges[i].Greater() = ind1;
}
results(0, i) = edges[i].Lesser();
results(1, i) = edges[i].Greater();
results(2, i) = edges[i].Distance();
}
}
else
{
for (size_t i = 0; i < edges.size(); ++i)
{
results(0, i) = edges[i].Lesser();
results(1, i) = edges[i].Greater();
results(2, i) = edges[i].Distance();
}
}
}
/**
* This function resets the values in the nodes of the tree nearest neighbor
* distance and checks for fully connected nodes.
*/
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
void DualTreeBoruvka<DistanceType, MatType, TreeType>::CleanupHelper(Tree* tree)
{
// Reset the statistic information.
tree->Stat().MaxNeighborDistance() = DBL_MAX;
tree->Stat().MinNeighborDistance() = DBL_MAX;
tree->Stat().Bound() = DBL_MAX;
// Recurse into all children.
for (size_t i = 0; i < tree->NumChildren(); ++i)
CleanupHelper(&tree->Child(i));
// Get the component of the first child or point. Then we will check to see
// if all other components of children and points are the same.
const int component = (tree->NumChildren() != 0) ?
tree->Child(0).Stat().ComponentMembership() :
connections.Find(tree->Point(0));
// Check components of children.
for (size_t i = 0; i < tree->NumChildren(); ++i)
if (tree->Child(i).Stat().ComponentMembership() != component)
return;
// Check components of points.
for (size_t i = 0; i < tree->NumPoints(); ++i)
if (connections.Find(tree->Point(i)) != size_t(component))
return;
// If we made it this far, all components are the same.
tree->Stat().ComponentMembership() = component;
}
/**
* The values stored in the tree must be reset on each iteration.
*/
template<
typename DistanceType,
typename MatType,
template<typename TreeDistanceType,
typename TreeStatType,
typename TreeMatType> class TreeType>
void DualTreeBoruvka<DistanceType, MatType, TreeType>::Cleanup()
{
for (size_t i = 0; i < data.n_cols; ++i)
neighborsDistances[i] = DBL_MAX;
if (!naive)
CleanupHelper(tree);
}
} // namespace mlpack
#endif