diff --git a/src/mlpack/methods/rann/ra_model.hpp b/src/mlpack/methods/rann/ra_model.hpp index 8463ec2261..c775305458 100644 --- a/src/mlpack/methods/rann/ra_model.hpp +++ b/src/mlpack/methods/rann/ra_model.hpp @@ -18,12 +18,274 @@ #include #include #include - +#include #include "ra_search.hpp" namespace mlpack { namespace neighbor { +/** + * Alias template for RASearch + */ +template class TreeType> +using RAType = RASearch; + +template +struct RAModelName +{ + static const std::string Name() { return "rank_approx_search_model"; }; +}; + +/** + * MonoSearchVisitor executes a monochromatic neighbor search on the given + * RAType. We don't make any difference for different instantiation of RAType. + */ +class MonoSearchVisitor : public boost::static_visitor +{ + private: + //! Number of neighbors to search for. + const size_t k; + //! Result matrix for neighbors. + arma::Mat& neighbors; + //! Result matrix for distances. + arma::mat& distances; + + public: + //! Perform monochromatic nearest neighbor search. + template + void operator()(RAType* ra) const; + + //! Construct the MonoSearchVisitor object with the given parameters. + MonoSearchVisitor(const size_t k, + arma::Mat& neighbors, + arma::mat& distances) : + k(k), + neighbors(neighbors), + distances(distances) + {}; +}; + +/** + * BiSearchVisitor executes a bichromatic neighbor search on the given RAType. + * We use template specialization to differentiate those tree types types that + * accept leafSize as a parameter. In these cases, before doing neighbor search + * a query tree with proper leafSize is built from the querySet. + */ +template +class BiSearchVisitor : public boost::static_visitor +{ + private: + //! The query set for the bichromatic search. + const arma::mat& querySet; + //! The number of neighbors to search for. + const size_t k; + //! The results matrix for neighbors. + arma::Mat& neighbors; + //! The result matrix for distances. + arma::mat& distances; + //! The number of points in a leaf (for BinarySpaceTrees). + const size_t leafSize; + + //! Bichromatic neighbor search on the given RAType considering leafSize. + template + void SearchLeaf(RAType* ra) const; + + public: + //! Alias template necessary for visual c++ compiler. + template class TreeType> + using RATypeT = RAType; + + //! Default Bichromatic neighbor search on the given RAType instance. + template class TreeType> + void operator()(RATypeT* ra) const; + + //! Bichromatic search on the given RAType specialized for KDTrees. + void operator()(RATypeT* ra) const; + + //! Bichromatic search on the given RAType specialized for octrees. + void operator()(RATypeT* ra) const; + + //! Construct the BiSearchVisitor. + BiSearchVisitor(const arma::mat& querySet, + const size_t k, + arma::Mat& neighbors, + arma::mat& distances, + const size_t leafSize); +}; + +/** + * TrainVisitor sets the reference set to a new reference set on the given + * RAType. We use template specialization to differentiate those trees that + * accept leafSize as a parameter. In these cases, a reference tree with proper + * leafSize is built from the referenceSet. + */ +template +class TrainVisitor : public boost::static_visitor +{ + private: + //! The reference set to use for training. + arma::mat&& referenceSet; + //! The leaf size, used only by BinarySpaceTree. + size_t leafSize; + + //! Train on the given RAType considering the leafSize. + template + void TrainLeaf(RAType* ra) const; + + public: + //! Alias template necessary for visual c++ compiler. + template class TreeType> + using RATypeT = RAType; + + //! Default Train on the given RAType instance. + template class TreeType> + void operator()(RATypeT* ra) const; + + //! Train on the given RAType specialized for KDTrees. + void operator()(RATypeT* ra) const; + + //! Train on the given RAType specialized for Octrees. + void operator()(RATypeT* ra) const; + + //! Construct the TrainVisitor object with the given reference set, leafSize + //! for BinarySpaceTrees. + TrainVisitor(arma::mat&& referenceSet, + const size_t leafSize); +}; + +/** + * Exposes the SingleSampleLimit() method of the given RAType. + */ +class SingleSampleLimitVisitor : public boost::static_visitor +{ + public: + template + size_t& operator()(RAType* ra) const; +}; + +/** + * Exposes the FirstLeafExact() method of the given RAType. + */ +class FirstLeafExactVisitor : public boost::static_visitor +{ + public: + template + bool& operator()(RAType* ra) const; +}; + +/** + * Exposes the SampleAtLeaves() method of the given RAType. + */ +class SampleAtLeavesVisitor : public boost::static_visitor +{ + public: + //! Return SampleAtLeaves (whether or not sampling is done at leaves). + template + bool& operator()(RAType *) const; +}; + +/** + * Exposes the Alpha() method of the given RAType. + */ +class AlphaVisitor : public boost::static_visitor +{ + public: + //! Return Alpha parameter. + template + double& operator()(RAType* ra) const; +}; + +/** + * Exposes the Tau() method of the given RAType. + */ +class TauVisitor : public boost::static_visitor +{ + public: + //! Get a reference to the Tau parameter. + template + double& operator()(RAType* ra) const; +}; + +/** + * Exposes the SingleMode() method of the given RAType. + */ +class SingleModeVisitor : public boost::static_visitor +{ + public: + //! Get a reference to the SingleMode parameter of the given RASearch object. + template + bool& operator()(RAType* ra) const; +}; + +/** + * Exposes the referenceSet of the given RAType. + */ +class ReferenceSetVisitor : public boost::static_visitor +{ + public: + //! Return the reference set. + template + const arma::mat& operator()(RAType* ra) const; +}; + +/** + * Exposes the serialize method of the give RAType instance. + */ +template +class SerializeVisitor : public boost::static_visitor +{ + private: + //! Archive to serialize to. + Archive& ar; + //! Name of the model to serialize. + const std::string& name; + + public: + //! Serialize the given model. + template + void operator()(RAType*& ra) const; + + //! Construct the SerializeVisitor with the given archive and name. + SerializeVisitor(Archive& ar, const std::string& name); +}; + +/** + * DeleteVisitor deletes the give RAType Instance. + */ +class DeleteVisitor : public boost::static_visitor +{ + public: + //! Delete the RAType Object. + template void operator()(RAType* ra) const; +}; + +/** + * NaiveVisitor exposes the Naive() method of the given RAType. + */ +class NaiveVisitor : public boost::static_visitor +{ + public: + /** + * Get a reference to the naive parameter of the given RASearch object. + */ + template + bool& operator()(RAType* ra) const; +}; + /** * The RAModel class provides an abstraction for the RASearch class, abstracting * away the TreeType parameter and allowing it to be specified at runtime in @@ -64,36 +326,17 @@ class RAModel bool randomBasis; //! The basis to project into. arma::mat q; - - //! Typedef the RASearch class we'll use. - template class TreeType> - using RAType = RASearch; - - //! Non-NULL if the kd-tree is used. - RAType* kdTreeRA; - //! Non-NULL if the cover tree is used. - RAType* coverTreeRA; - //! Non-NULL if the R tree is used. - RAType* rTreeRA; - //! Non-NULL if the R* tree is used. - RAType* rStarTreeRA; - //! Non-NULL if the X tree is used. - RAType* xTreeRA; - //! Non-NULL if the Hilbert R tree is used. - RAType* hilbertRTreeRA; - //! Non-NULL if the R+ tree is used. - RAType* rPlusTreeRA; - //! Non-NULL if the R++ tree is used. - RAType* rPlusPlusTreeRA; - //! Non-NULL if the UB tree is used. - RAType* ubTreeRA; - //! Non-NULL if the octree is used. - RAType* octreeRA; + + boost::variant*, + RAType*, + RAType*, + RAType*, + RAType*, + RAType*, + RAType*, + RAType*, + RAType*, + RAType*> raSearch; public: /** diff --git a/src/mlpack/methods/rann/ra_model_impl.hpp b/src/mlpack/methods/rann/ra_model_impl.hpp index 117c1918ff..c2b3a62df6 100644 --- a/src/mlpack/methods/rann/ra_model_impl.hpp +++ b/src/mlpack/methods/rann/ra_model_impl.hpp @@ -19,21 +19,258 @@ namespace mlpack { namespace neighbor { +//! Monochromatic search for the given RAType instance. +template +void MonoSearchVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->Search(k, neighbors, distances); + throw std::runtime_error("no rank-approximate model initialized"); +} + +//! Save the parameters for the rank-approximate search. +template +BiSearchVisitor::BiSearchVisitor(const arma::mat& querySet, + const size_t k, + arma::Mat& neighbors, + arma::mat& distances, + const size_t leafSize) : + querySet(querySet), + k(k), + neighbors(neighbors), + distances(distances), + leafSize(leafSize) +{}; + +//! Default Bichromatic search on the given RAType instance. +template +template class TreeType> +void BiSearchVisitor::operator()(RATypeT* ra) const +{ + if(ra) + return ra->Search(querySet, k, neighbors, distances); + throw std::runtime_error("no rank-approximate model initialized"); +} + +//! Bichromatic search on the given RAType specialized for KDTrees. +template +void BiSearchVisitor::operator()(RATypeT* ra) const +{ + if(ra) + return SearchLeaf(ra); + throw std::runtime_error("no rank-approximate search model initialized"); +} + +//! Bichromatic search on the given RAType specialized for Octrees. +template +void BiSearchVisitor::operator()(RATypeT* ra) const +{ + if(ra) + return SearchLeaf(ra); + throw std::runtime_error("no rank-approximate search model initialized"); +} + +//! Bichromatic search on the given RAType considering the leafSize. +template +template +void BiSearchVisitor::SearchLeaf(RAType* ra) const +{ + if(!ra->Naive() && !ra->SingleMode()) + { + // Build a second tree and search + Timer::Start("tree_building"); + Log::Info << "Building query tree...."<< std::endl; + std::vector oldFromNewQueries; + typename RAType::Tree queryTree(std::move(querySet), oldFromNewQueries, + leafSize); + Log::Info << "Tree Built." << std::endl; + Timer::Stop("tree_building"); + + arma::Mat neighborsOut; + arma::mat distancesOut; + ra->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. + ra->Search(querySet, k, neighbors, distances); + } +} + +//! Save parameters for the Train. +template +TrainVisitor::TrainVisitor(arma::mat&& referenceSet, + const size_t leafSize) : + referenceSet(std::move(referenceSet)), + leafSize(leafSize) +{}; + +//! Default Train on the given RAType instance. +template +template class TreeType> +void TrainVisitor::operator()(RATypeT* ra) const +{ + if(ra) + return ra->Train(std::move(referenceSet)); + throw std::runtime_error("no rank-approximate search model initialized"); +} + +//! Train on the given RAType specialized for KDTrees. +template +void TrainVisitor::operator()(RATypeT* ra) const +{ + if(ra) + return TrainLeaf(ra); + throw std::runtime_error("no rank-approximate search model initialized"); +} + +//! Train on the given RAType specialized for Octrees. +template +void TrainVisitor::operator()(RATypeT* ra) const +{ + if(ra) + return TrainLeaf(ra); + throw std::runtime_error("no rank-approximate search model is initialized"); +} + +//! Train on the given RAType considering the leafSize. +template +template +void TrainVisitor::TrainLeaf(RAType* ra) const +{ + // Build tree, if necessary + if(ra->Naive()) + { + ra->Train(std::move(referenceSet)); + } + else + { + std::vector oldFromNewReferences; + typename RAType::Tree* tree = + new typename RAType::Tree(std::move(referenceSet), oldFromNewReferences, + leafSize); + ra->Train(tree); + + // Give the model ownership of the tree and the mappings. + ra->treeOwner = true; + ra->oldFromNewReferences = std::move(oldFromNewReferences); + } +} + +//! Exposes the SingleSampleLimit() method of the given RAType. +template +size_t& SingleSampleLimitVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->SingleSampleLimit(); + throw std::runtime_error("no rank-approximate search model is initialized"); +} + +//! Exposes the FirstLeafExact() method of the given RAType. +template +bool& FirstLeafExactVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->FirstLeafExact(); + throw std::runtime_error("no rank-approximate search model is initialized"); +} + +//! Exposes the SampleAtLeaves() method of the given RAType. +template +bool& SampleAtLeavesVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->SampleAtLeaves(); + throw std::runtime_error("no rank-approximate search model is initialized"); +} + +//! Exposes the Alpha() method of the given RAType instance. +template +double& AlphaVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->Alpha(); + throw std::runtime_error("no rank-approximate model is initialized"); +} + +//! Exposes the Tau() method of the given RAType instance. +template +double& TauVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->Tau(); + throw std::runtime_error("no rank-approximate model is initialized"); +} + +//! Exposes the SingleMode() method of the given RAType. +template +bool& SingleModeVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->SingleMode(); + throw std::runtime_error("no rank-approximate model is intialized"); +} + +//! Exposes the referenceSet of the given RAType. +template +const arma::mat& ReferenceSetVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->ReferenceSet(); + throw std::runtime_error("no rank-approximate model is intialized"); +} + +//! Save parameters for serializing +template +SerializeVisitor::SerializeVisitor(Archive& ar, + const std::string& name) : + ar(ar), + name(name) +{} + +//! Serializes the given RAType instance. +template +template +void SerializeVisitor::operator()(RAType*& ra) const +{ + ar & data::CreateNVP(ra, name); +} + +//! Exposes the Naive() method of the given RAType instance. +template +bool& NaiveVisitor::operator()(RAType* ra) const +{ + if(ra) + return ra->Naive(); + throw std::runtime_error("no rank-approximate search model is intialized"); +} + +//! For cleaning memory +template +void DeleteVisitor::operator()(RSType* rs) const +{ + if (rs) + delete rs; +} + template RAModel::RAModel(const TreeTypes treeType, const bool randomBasis) : treeType(treeType), leafSize(20), - randomBasis(randomBasis), - kdTreeRA(NULL), - coverTreeRA(NULL), - rTreeRA(NULL), - rStarTreeRA(NULL), - xTreeRA(NULL), - hilbertRTreeRA(NULL), - rPlusTreeRA(NULL), - rPlusPlusTreeRA(NULL), - ubTreeRA(NULL), - octreeRA(NULL) + randomBasis(randomBasis) { // Nothing to do. } @@ -44,37 +281,9 @@ RAModel::RAModel(const RAModel& other) : treeType(other.treeType), leafSize(other.leafSize), randomBasis(other.randomBasis), - kdTreeRA(NULL), - coverTreeRA(NULL), - rTreeRA(NULL), - rStarTreeRA(NULL), - xTreeRA(NULL), - hilbertRTreeRA(NULL), - rPlusTreeRA(NULL), - rPlusPlusTreeRA(NULL), - ubTreeRA(NULL), - octreeRA(NULL) + raSearch(other.raSearch) { - if (other.kdTreeRA) - kdTreeRA = new RAType(*other.kdTreeRA); - if (other.coverTreeRA) - coverTreeRA = new RAType(*other.coverTreeRA); - if (other.rTreeRA) - rTreeRA = new RAType(*other.rTreeRA); - if (other.rStarTreeRA) - rStarTreeRA = new RAType(*other.rStarTreeRA); - if (other.xTreeRA) - xTreeRA = new RAType(*other.xTreeRA); - if (other.hilbertRTreeRA) - hilbertRTreeRA = new RAType(*other.hilbertRTreeRA); - if (other.rPlusTreeRA) - rPlusTreeRA = new RAType(*other.rPlusTreeRA); - if (other.rPlusPlusTreeRA) - rPlusPlusTreeRA = new RAType(*other.rPlusPlusTreeRA); - if (other.ubTreeRA) - ubTreeRA = new RAType(*other.ubTreeRA); - if (other.octreeRA) - octreeRA = new RAType(*other.octreeRA); + } // Move constructor. @@ -83,31 +292,13 @@ RAModel::RAModel(RAModel&& other) : treeType(other.treeType), leafSize(other.leafSize), randomBasis(other.randomBasis), - kdTreeRA(other.kdTreeRA), - coverTreeRA(other.coverTreeRA), - rTreeRA(other.rTreeRA), - rStarTreeRA(other.rStarTreeRA), - xTreeRA(other.xTreeRA), - hilbertRTreeRA(other.hilbertRTreeRA), - rPlusTreeRA(other.rPlusTreeRA), - rPlusPlusTreeRA(other.rPlusPlusTreeRA), - ubTreeRA(other.ubTreeRA), - octreeRA(other.octreeRA) + raSearch(other.raSearch) { // Clear other model. other.treeType = TreeTypes::KD_TREE; other.leafSize = 20; other.randomBasis = false; - other.kdTreeRA = NULL; - other.coverTreeRA = NULL; - other.rTreeRA = NULL; - other.rStarTreeRA = NULL; - other.xTreeRA = NULL; - other.hilbertRTreeRA = NULL; - other.rPlusTreeRA = NULL; - other.rPlusPlusTreeRA = NULL; - other.ubTreeRA = NULL; - other.octreeRA = NULL; + other.raSearch = decltype(other.raSearch)(); } // Copy operator. @@ -115,41 +306,12 @@ template RAModel& RAModel::operator=(const RAModel& other) { // Clear current model. - delete kdTreeRA; - delete coverTreeRA; - delete rTreeRA; - delete rStarTreeRA; - delete xTreeRA; - delete hilbertRTreeRA; - delete rPlusTreeRA; - delete rPlusPlusTreeRA; - delete ubTreeRA; - delete octreeRA; + boost::apply_visitor(DeleteVisitor(), raSearch); treeType = other.treeType; leafSize = other.leafSize; randomBasis = other.randomBasis; - - if (other.kdTreeRA) - kdTreeRA = new RAType(*other.kdTreeRA); - if (other.coverTreeRA) - coverTreeRA = new RAType(*other.coverTreeRA); - if (other.rTreeRA) - rTreeRA = new RAType(*other.rTreeRA); - if (other.rStarTreeRA) - rStarTreeRA = new RAType(*other.rStarTreeRA); - if (other.xTreeRA) - xTreeRA = new RAType(*other.xTreeRA); - if (other.hilbertRTreeRA) - hilbertRTreeRA = new RAType(*other.hilbertRTreeRA); - if (other.rPlusTreeRA) - rPlusTreeRA = new RAType(*other.rPlusTreeRA); - if (other.rPlusPlusTreeRA) - rPlusPlusTreeRA = new RAType(*other.rPlusPlusTreeRA); - if (other.ubTreeRA) - ubTreeRA = new RAType(*other.ubTreeRA); - if (other.octreeRA) - octreeRA = new RAType(*other.octreeRA); + raSearch = other.raSearch; return *this; } @@ -157,62 +319,27 @@ RAModel& RAModel::operator=(const RAModel& other) template RAModel& RAModel::operator=(RAModel&& other) { - delete kdTreeRA; - delete coverTreeRA; - delete rTreeRA; - delete rStarTreeRA; - delete xTreeRA; - delete hilbertRTreeRA; - delete rPlusTreeRA; - delete rPlusPlusTreeRA; - delete ubTreeRA; - delete octreeRA; + boost::apply_visitor(DeleteVisitor(), raSearch); treeType = other.treeType; leafSize = other.leafSize; randomBasis = other.randomBasis; - kdTreeRA = other.kdTreeRA; - coverTreeRA = other.coverTreeRA; - rTreeRA = other.rTreeRA; - rStarTreeRA = other.rStarTreeRA; - xTreeRA = other.xTreeRA; - hilbertRTreeRA = other.hilbertRTreeRA; - rPlusTreeRA = other.rPlusTreeRA; - rPlusPlusTreeRA = other.rPlusPlusTreeRA; - ubTreeRA = other.ubTreeRA; - octreeRA = other.octreeRA; + raSearch = other.raSearch; // Reset other model. other.treeType = TreeTypes::KD_TREE; other.leafSize = 20; other.randomBasis = false; - other.kdTreeRA = NULL; - other.coverTreeRA = NULL; - other.rTreeRA = NULL; - other.rStarTreeRA = NULL; - other.xTreeRA = NULL; - other.hilbertRTreeRA = NULL; - other.rPlusTreeRA = NULL; - other.rPlusPlusTreeRA = NULL; - other.ubTreeRA = NULL; - other.octreeRA = NULL; + other.raSearch = decltype(other.raSearch)(); return *this; } +// Clean memory, if necessary template RAModel::~RAModel() { - delete kdTreeRA; - delete coverTreeRA; - delete rTreeRA; - delete rStarTreeRA; - delete xTreeRA; - delete hilbertRTreeRA; - delete rPlusTreeRA; - delete rPlusPlusTreeRA; - delete ubTreeRA; - delete octreeRA; + boost::apply_visitor(DeleteVisitor(), raSearch); } template @@ -227,483 +354,103 @@ void RAModel::Serialize(Archive& ar, // This should never happen, but just in case, be clean with memory. if (Archive::is_loading::value) { - 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; - coverTreeRA = NULL; - rTreeRA = NULL; - rStarTreeRA = NULL; - xTreeRA = NULL; - hilbertRTreeRA = NULL; - rPlusPlusTreeRA = NULL; - rPlusTreeRA = NULL; - ubTreeRA = NULL; + boost::apply_visitor(DeleteVisitor(), raSearch); } // We only need to serialize one of the kRANN objects. - switch (treeType) - { - case KD_TREE: - ar & data::CreateNVP(kdTreeRA, "ra_model"); - break; - case COVER_TREE: - ar & data::CreateNVP(coverTreeRA, "ra_model"); - break; - case R_TREE: - ar & data::CreateNVP(rTreeRA, "ra_model"); - break; - case R_STAR_TREE: - ar & data::CreateNVP(rStarTreeRA, "ra_model"); - break; - case X_TREE: - ar & data::CreateNVP(xTreeRA, "ra_model"); - break; - case HILBERT_R_TREE: - ar & data::CreateNVP(hilbertRTreeRA, "ra_model"); - break; - case R_PLUS_TREE: - ar & data::CreateNVP(rPlusTreeRA, "ra_model"); - break; - case R_PLUS_PLUS_TREE: - ar & data::CreateNVP(rPlusPlusTreeRA, "ra_model"); - break; - case UB_TREE: - ar & data::CreateNVP(ubTreeRA, "ra_model"); - break; - case OCTREE: - ar & data::CreateNVP(octreeRA, "ra_model"); - break; - } + const std::string& name = RAModelName::Name(); + SerializeVisitor s(ar, name); + boost::apply_visitor(s, raSearch); } template const arma::mat& RAModel::Dataset() const { - if (kdTreeRA) - return kdTreeRA->ReferenceSet(); - else if (coverTreeRA) - return coverTreeRA->ReferenceSet(); - else if (rTreeRA) - return rTreeRA->ReferenceSet(); - else if (rStarTreeRA) - return rStarTreeRA->ReferenceSet(); - else if (xTreeRA) - return xTreeRA->ReferenceSet(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->ReferenceSet(); - else if (rPlusTreeRA) - return rPlusTreeRA->ReferenceSet(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(ReferenceSetVisitor(), raSearch); } template bool RAModel::Naive() const { - if (kdTreeRA) - return kdTreeRA->Naive(); - else if (coverTreeRA) - return coverTreeRA->Naive(); - else if (rTreeRA) - return rTreeRA->Naive(); - else if (rStarTreeRA) - return rStarTreeRA->Naive(); - else if (xTreeRA) - return xTreeRA->Naive(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->Naive(); - else if (rPlusTreeRA) - return rPlusTreeRA->Naive(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(NaiveVisitor(), raSearch); } template bool& RAModel::Naive() { - if (kdTreeRA) - return kdTreeRA->Naive(); - else if (coverTreeRA) - return coverTreeRA->Naive(); - else if (rTreeRA) - return rTreeRA->Naive(); - else if (rStarTreeRA) - return rStarTreeRA->Naive(); - else if (xTreeRA) - return xTreeRA->Naive(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->Naive(); - else if (rPlusTreeRA) - return rPlusTreeRA->Naive(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(NaiveVisitor(), raSearch); } template bool RAModel::SingleMode() const { - if (kdTreeRA) - return kdTreeRA->SingleMode(); - else if (coverTreeRA) - return coverTreeRA->SingleMode(); - else if (rTreeRA) - return rTreeRA->SingleMode(); - else if (rStarTreeRA) - return rStarTreeRA->SingleMode(); - else if (xTreeRA) - return xTreeRA->SingleMode(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->SingleMode(); - else if (rPlusTreeRA) - return rPlusTreeRA->SingleMode(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(SingleModeVisitor(), raSearch); } template bool& RAModel::SingleMode() { - if (kdTreeRA) - return kdTreeRA->SingleMode(); - else if (coverTreeRA) - return coverTreeRA->SingleMode(); - else if (rTreeRA) - return rTreeRA->SingleMode(); - else if (rStarTreeRA) - return rStarTreeRA->SingleMode(); - else if (xTreeRA) - return xTreeRA->SingleMode(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->SingleMode(); - else if (rPlusTreeRA) - return rPlusTreeRA->SingleMode(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(SingleModeVisitor(), raSearch); } template double RAModel::Tau() const { - if (kdTreeRA) - return kdTreeRA->Tau(); - else if (coverTreeRA) - return coverTreeRA->Tau(); - else if (rTreeRA) - return rTreeRA->Tau(); - else if (rStarTreeRA) - return rStarTreeRA->Tau(); - else if (xTreeRA) - return xTreeRA->Tau(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->Tau(); - else if (rPlusTreeRA) - return rPlusTreeRA->Tau(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(TauVisitor(), raSearch); } template double& RAModel::Tau() { - if (kdTreeRA) - return kdTreeRA->Tau(); - else if (coverTreeRA) - return coverTreeRA->Tau(); - else if (rTreeRA) - return rTreeRA->Tau(); - else if (rStarTreeRA) - return rStarTreeRA->Tau(); - else if (xTreeRA) - return xTreeRA->Tau(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->Tau(); - else if (rPlusTreeRA) - return rPlusTreeRA->Tau(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(TauVisitor(), raSearch); } template double RAModel::Alpha() const { - if (kdTreeRA) - return kdTreeRA->Alpha(); - else if (coverTreeRA) - return coverTreeRA->Alpha(); - else if (rTreeRA) - return rTreeRA->Alpha(); - else if (rStarTreeRA) - return rStarTreeRA->Alpha(); - else if (xTreeRA) - return xTreeRA->Alpha(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->Alpha(); - else if (rPlusTreeRA) - return rPlusTreeRA->Alpha(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(AlphaVisitor(), raSearch); } template double& RAModel::Alpha() { - if (kdTreeRA) - return kdTreeRA->Alpha(); - else if (coverTreeRA) - return coverTreeRA->Alpha(); - else if (rTreeRA) - return rTreeRA->Alpha(); - else if (rStarTreeRA) - return rStarTreeRA->Alpha(); - else if (xTreeRA) - return xTreeRA->Alpha(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->Alpha(); - else if (rPlusTreeRA) - return rPlusTreeRA->Alpha(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(AlphaVisitor(), raSearch); } template bool RAModel::SampleAtLeaves() const { - if (kdTreeRA) - return kdTreeRA->SampleAtLeaves(); - else if (coverTreeRA) - return coverTreeRA->SampleAtLeaves(); - else if (rTreeRA) - return rTreeRA->SampleAtLeaves(); - else if (rStarTreeRA) - return rStarTreeRA->SampleAtLeaves(); - else if (xTreeRA) - return xTreeRA->SampleAtLeaves(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->SampleAtLeaves(); - else if (rPlusTreeRA) - return rPlusTreeRA->SampleAtLeaves(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(SampleAtLeavesVisitor(), raSearch); } template bool& RAModel::SampleAtLeaves() { - if (kdTreeRA) - return kdTreeRA->SampleAtLeaves(); - else if (coverTreeRA) - return coverTreeRA->SampleAtLeaves(); - else if (rTreeRA) - return rTreeRA->SampleAtLeaves(); - else if (rStarTreeRA) - return rStarTreeRA->SampleAtLeaves(); - else if (xTreeRA) - return xTreeRA->SampleAtLeaves(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->SampleAtLeaves(); - else if (rPlusTreeRA) - return rPlusTreeRA->SampleAtLeaves(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(SampleAtLeavesVisitor(), raSearch); } template bool RAModel::FirstLeafExact() const { - if (kdTreeRA) - return kdTreeRA->FirstLeafExact(); - else if (coverTreeRA) - return coverTreeRA->FirstLeafExact(); - else if (rTreeRA) - return rTreeRA->FirstLeafExact(); - else if (rStarTreeRA) - return rStarTreeRA->FirstLeafExact(); - else if (xTreeRA) - return xTreeRA->FirstLeafExact(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->FirstLeafExact(); - else if (rPlusTreeRA) - return rPlusTreeRA->FirstLeafExact(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(FirstLeafExactVisitor(), raSearch); } template bool& RAModel::FirstLeafExact() { - if (kdTreeRA) - return kdTreeRA->FirstLeafExact(); - else if (coverTreeRA) - return coverTreeRA->FirstLeafExact(); - else if (rTreeRA) - return rTreeRA->FirstLeafExact(); - else if (rStarTreeRA) - return rStarTreeRA->FirstLeafExact(); - else if (xTreeRA) - return xTreeRA->FirstLeafExact(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->FirstLeafExact(); - else if (rPlusTreeRA) - return rPlusTreeRA->FirstLeafExact(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(FirstLeafExactVisitor(), raSearch); } template size_t RAModel::SingleSampleLimit() const { - if (kdTreeRA) - return kdTreeRA->SingleSampleLimit(); - else if (coverTreeRA) - return coverTreeRA->SingleSampleLimit(); - else if (rTreeRA) - return rTreeRA->SingleSampleLimit(); - else if (rStarTreeRA) - return rStarTreeRA->SingleSampleLimit(); - else if (xTreeRA) - return xTreeRA->SingleSampleLimit(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->SingleSampleLimit(); - else if (rPlusTreeRA) - return rPlusTreeRA->SingleSampleLimit(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(SingleSampleLimitVisitor(), raSearch); } template size_t& RAModel::SingleSampleLimit() { - if (kdTreeRA) - return kdTreeRA->SingleSampleLimit(); - else if (coverTreeRA) - return coverTreeRA->SingleSampleLimit(); - else if (rTreeRA) - return rTreeRA->SingleSampleLimit(); - else if (rStarTreeRA) - return rStarTreeRA->SingleSampleLimit(); - else if (xTreeRA) - return xTreeRA->SingleSampleLimit(); - else if (hilbertRTreeRA) - return hilbertRTreeRA->SingleSampleLimit(); - else if (rPlusTreeRA) - return rPlusTreeRA->SingleSampleLimit(); - else if (rPlusPlusTreeRA) - 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"); + return boost::apply_visitor(SingleSampleLimitVisitor(), raSearch); } template @@ -756,16 +503,7 @@ void RAModel::BuildModel(arma::mat&& referenceSet, } // Clean memory, if necessary. - delete kdTreeRA; - delete coverTreeRA; - delete rTreeRA; - delete rStarTreeRA; - delete xTreeRA; - delete hilbertRTreeRA; - delete rPlusTreeRA; - delete rPlusPlusTreeRA; - delete ubTreeRA; - delete octreeRA; + boost::apply_visitor(DeleteVisitor(), raSearch); this->leafSize = leafSize; @@ -781,79 +519,42 @@ void RAModel::BuildModel(arma::mat&& referenceSet, switch (treeType) { case KD_TREE: - // Build tree, if necessary. - if (naive) - { - kdTreeRA = new RAType(std::move(referenceSet), naive, - singleMode); - } - else - { - std::vector oldFromNewReferences; - typename RAType::Tree* kdTree = - new typename RAType::Tree(std::move(referenceSet), - oldFromNewReferences, leafSize); - kdTreeRA = new RAType(kdTree, singleMode); - - // Give the model ownership of the tree. - kdTreeRA->treeOwner = true; - kdTreeRA->oldFromNewReferences = oldFromNewReferences; - } + raSearch = new RAType(naive, singleMode); break; case COVER_TREE: - coverTreeRA = new RAType(std::move(referenceSet), - naive, singleMode); + raSearch = new RAType(naive, + singleMode); break; case R_TREE: - rTreeRA = new RAType(std::move(referenceSet), naive, - singleMode); + raSearch = new RAType(naive, singleMode); break; case R_STAR_TREE: - rStarTreeRA = new RAType(std::move(referenceSet), naive, - singleMode); + raSearch = new RAType(naive, singleMode); break; case X_TREE: - xTreeRA = new RAType(std::move(referenceSet), naive, - singleMode); + raSearch = new RAType(naive, singleMode); break; case HILBERT_R_TREE: - hilbertRTreeRA = new RAType(std::move(referenceSet), - naive, singleMode); + raSearch = new RAType(naive, singleMode); break; case R_PLUS_TREE: - rPlusTreeRA = new RAType(std::move(referenceSet), - naive, singleMode); + raSearch = new RAType(naive, singleMode); break; case R_PLUS_PLUS_TREE: - rPlusPlusTreeRA = new RAType(std::move(referenceSet), - naive, singleMode); + raSearch = new RAType(naive, + singleMode); break; case UB_TREE: - ubTreeRA = new RAType(std::move(referenceSet), - naive, singleMode); + raSearch = new RAType(naive, singleMode); break; case OCTREE: - // Build tree, if necessary. - if (naive) - { - octreeRA = new RAType(std::move(referenceSet), naive, - singleMode); - } - else - { - std::vector oldFromNewReferences; - typename RAType::Tree* octree = - new typename RAType::Tree(std::move(referenceSet), - oldFromNewReferences, leafSize); - octreeRA = new RAType(octree, singleMode); - - // Give the model ownership of the tree. - octreeRA->treeOwner = true; - octreeRA->oldFromNewReferences = oldFromNewReferences; - } + raSearch = new RAType(naive, singleMode); break; } + TrainVisitor tn(std::move(referenceSet), leafSize); + boost::apply_visitor(tn, raSearch); + if (!naive) { Timer::Stop("tree_building"); @@ -880,103 +581,8 @@ void RAModel::Search(arma::mat&& querySet, Log::Info << "brute-force (naive) rank-approximate search..."; Log::Info << std::endl; - switch (treeType) - { - case KD_TREE: - if (!kdTreeRA->Naive() && !kdTreeRA->SingleMode()) - { - // Build a second tree and search. - Timer::Start("tree_building"); - Log::Info << "Building query tree..." << std::endl; - std::vector oldFromNewQueries; - typename RAType::Tree queryTree(std::move(querySet), - oldFromNewQueries, leafSize); - Log::Info << "Tree built." << std::endl; - Timer::Stop("tree_building"); - - arma::Mat neighborsOut; - arma::mat distancesOut; - kdTreeRA->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. - kdTreeRA->Search(querySet, k, neighbors, distances); - } - break; - case COVER_TREE: - // No mapping necessary. - coverTreeRA->Search(querySet, k, neighbors, distances); - break; - case R_TREE: - // No mapping necessary. - rTreeRA->Search(querySet, k, neighbors, distances); - break; - case R_STAR_TREE: - // No mapping necessary. - rStarTreeRA->Search(querySet, k, neighbors, distances); - break; - case X_TREE: - // No mapping necessary. - xTreeRA->Search(querySet, k, neighbors, distances); - break; - case HILBERT_R_TREE: - // No mapping necessary. - hilbertRTreeRA->Search(querySet, k, neighbors, distances); - break; - case R_PLUS_TREE: - // No mapping necessary. - rPlusTreeRA->Search(querySet, k, neighbors, distances); - break; - case R_PLUS_PLUS_TREE: - // No mapping necessary. - rPlusPlusTreeRA->Search(querySet, k, neighbors, distances); - break; - case UB_TREE: - // 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 oldFromNewQueries; - typename RAType::Tree queryTree(std::move(querySet), - oldFromNewQueries, leafSize); - Log::Info << "Tree built." << std::endl; - Timer::Stop("tree_building"); - - arma::Mat 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; - } + BiSearchVisitor search(querySet, k, neighbors, distances, leafSize); + boost::apply_visitor(search, raSearch); } template @@ -993,39 +599,8 @@ void RAModel::Search(const size_t k, Log::Info << "brute-force (naive) rank-approximate search..."; Log::Info << std::endl; - switch (treeType) - { - case KD_TREE: - kdTreeRA->Search(k, neighbors, distances); - break; - case COVER_TREE: - coverTreeRA->Search(k, neighbors, distances); - break; - case R_TREE: - rTreeRA->Search(k, neighbors, distances); - break; - case R_STAR_TREE: - rStarTreeRA->Search(k, neighbors, distances); - break; - case X_TREE: - xTreeRA->Search(k, neighbors, distances); - break; - case HILBERT_R_TREE: - hilbertRTreeRA->Search(k, neighbors, distances); - break; - case R_PLUS_TREE: - rPlusTreeRA->Search(k, neighbors, distances); - break; - case R_PLUS_PLUS_TREE: - rPlusPlusTreeRA->Search(k, neighbors, distances); - break; - case UB_TREE: - ubTreeRA->Search(k, neighbors, distances); - break; - case OCTREE: - octreeRA->Search(k, neighbors, distances); - break; - } + MonoSearchVisitor search(k, neighbors, distances); + boost::apply_visitor(search, raSearch); } template diff --git a/src/mlpack/methods/rann/ra_search.hpp b/src/mlpack/methods/rann/ra_search.hpp index c88e189db1..1be6bcbd41 100644 --- a/src/mlpack/methods/rann/ra_search.hpp +++ b/src/mlpack/methods/rann/ra_search.hpp @@ -38,7 +38,7 @@ namespace neighbor { // Forward declaration. template -class RAModel; +class TrainVisitor; /** * The RASearch class: This class provides a generic manner to perform @@ -296,6 +296,11 @@ class RASearch */ void Train(MatType&& referenceSet); + /** + * Set the reference tree to a new reference tree. + */ + void Train(Tree* referenceTree); + /** * Compute the rank approximate nearest neighbors of each query point in the * query set and store the output in the given matrices. The matrices will be @@ -451,8 +456,9 @@ class RASearch //! Instantiation of kernel. MetricType metric; - //! RAModel can modify internal members as necessary. - friend class RAModel; + //! For access to mappings when building models. + template + friend class TrainVisitor; }; // class RASearch } // namespace neighbor diff --git a/src/mlpack/methods/rann/ra_search_impl.hpp b/src/mlpack/methods/rann/ra_search_impl.hpp index 23984132fd..16c5f5a67d 100644 --- a/src/mlpack/methods/rann/ra_search_impl.hpp +++ b/src/mlpack/methods/rann/ra_search_impl.hpp @@ -280,6 +280,31 @@ void RASearch::Train( } } +//! Set the reference tree to a new reference tree. +template class TreeType> +void RASearch::Train( + Tree* referenceTree) +{ + if (naive) + throw std::invalid_argument("cannot train on given reference tree when " + "naive search (without trees) is desired"); + + if (treeOwner && referenceTree) + delete this->referenceTree; + if (setOwner && referenceSet) + delete this->referenceSet; + + this->referenceTree = referenceTree; + this->referenceSet = &referenceTree->Dataset(); + treeOwner = false; + setOwner = false; +} + /** * Computes the best neighbors and stores them in resultingNeighbors and * distances.