diff --git a/include/tapkee/defines.hpp b/include/tapkee/defines.hpp index fe062ae..a815f4d 100644 --- a/include/tapkee/defines.hpp +++ b/include/tapkee/defines.hpp @@ -7,7 +7,6 @@ /* Tapkee includes */ #include #include -#include /* End of Tapkee includes */ #include diff --git a/include/tapkee/defines/methods.hpp b/include/tapkee/defines/methods.hpp index 5bc2521..90382cc 100644 --- a/include/tapkee/defines/methods.hpp +++ b/include/tapkee/defines/methods.hpp @@ -4,97 +4,12 @@ */ #pragma once +/* Tapkee includes */ #include -#include +/* End of Tapkee includes */ namespace tapkee { -//! Dimension reduction methods -enum DimensionReductionMethod -{ - /** Kernel Locally Linear Embedding as described - * in @cite Decoste2001 */ - KernelLocallyLinearEmbedding, - /** Neighborhood Preserving Embedding as described - * in @cite He2005 */ - NeighborhoodPreservingEmbedding, - /** Local Tangent Space Alignment as described - * in @cite Zhang2002 */ - KernelLocalTangentSpaceAlignment, - /** Linear Local Tangent Space Alignment as described - * in @cite Zhang2007 */ - LinearLocalTangentSpaceAlignment, - /** Hessian Locally Linear Embedding as described in - * @cite Donoho2003 */ - HessianLocallyLinearEmbedding, - /** Laplacian Eigenmaps as described in - * @cite Belkin2002 */ - LaplacianEigenmaps, - /** Locality Preserving Projections as described in - * @cite He2003 */ - LocalityPreservingProjections, - /** Diffusion map as described in - * @cite Coifman2006 */ - DiffusionMap, - /** Isomap as described in - * @cite Tenenbaum2000 */ - Isomap, - /** Landmark Isomap as described in - * @cite deSilva2002 */ - LandmarkIsomap, - /** Multidimensional scaling as described in - * @cite Cox2000 */ - MultidimensionalScaling, - /** Landmark multidimensional scaling as described in - * @cite deSilva2004 */ - LandmarkMultidimensionalScaling, - /** Stochastic Proximity Embedding as described in - * @cite Agrafiotis2003 */ - StochasticProximityEmbedding, - /** Kernel PCA as described in - * @cite Scholkopf1997 */ - KernelPCA, - /** Principal Component Analysis */ - PCA, - /** Random Projection as described in - * @cite Kaski1998*/ - RandomProjection, - /** Factor Analysis */ - FactorAnalysis, - /** t-SNE and Barnes-Hut-SNE as described in - * @cite vanDerMaaten2008 and @cite vanDerMaaten2013 */ - tDistributedStochasticNeighborEmbedding, - /** Manifold Sculpting as described in - * @cite Gashler2007 */ - ManifoldSculpting, - /** Passing through (doing nothing just passes the - * data through) */ - PassThru -}; - -#ifndef DOXYGEN_SHOULD_SKIP_THIS -// Methods identification -METHOD_THAT_NEEDS_ONLY_KERNEL_IS(KernelLocallyLinearEmbedding); -METHOD_THAT_NEEDS_KERNEL_AND_FEATURES_IS(NeighborhoodPreservingEmbedding); -METHOD_THAT_NEEDS_ONLY_KERNEL_IS(KernelLocalTangentSpaceAlignment); -METHOD_THAT_NEEDS_KERNEL_AND_FEATURES_IS(LinearLocalTangentSpaceAlignment); -METHOD_THAT_NEEDS_ONLY_KERNEL_IS(HessianLocallyLinearEmbedding); -METHOD_THAT_NEEDS_ONLY_DISTANCE_IS(LaplacianEigenmaps); -METHOD_THAT_NEEDS_DISTANCE_AND_FEATURES_IS(LocalityPreservingProjections); -METHOD_THAT_NEEDS_ONLY_DISTANCE_IS(DiffusionMap); -METHOD_THAT_NEEDS_ONLY_DISTANCE_IS(Isomap); -METHOD_THAT_NEEDS_ONLY_DISTANCE_IS(LandmarkIsomap); -METHOD_THAT_NEEDS_ONLY_DISTANCE_IS(MultidimensionalScaling); -METHOD_THAT_NEEDS_ONLY_DISTANCE_IS(LandmarkMultidimensionalScaling); -METHOD_THAT_NEEDS_DISTANCE_AND_FEATURES_IS(StochasticProximityEmbedding); -METHOD_THAT_NEEDS_ONLY_KERNEL_IS(KernelPCA); -METHOD_THAT_NEEDS_ONLY_FEATURES_IS(PCA); -METHOD_THAT_NEEDS_ONLY_FEATURES_IS(RandomProjection); -METHOD_THAT_NEEDS_NOTHING_IS(PassThru); -METHOD_THAT_NEEDS_ONLY_FEATURES_IS(FactorAnalysis); -METHOD_THAT_NEEDS_ONLY_FEATURES_IS(tDistributedStochasticNeighborEmbedding); -METHOD_THAT_NEEDS_DISTANCE_AND_FEATURES_IS(ManifoldSculpting); -#endif // DOXYGEN_SHOULD_SKIP_THS template struct Method { @@ -119,6 +34,112 @@ template struct Method const char* name_; }; +struct DimensionReductionTraits +{ + const bool needs_kernel; + const bool needs_distance; + const bool needs_features; +}; + +struct DimensionReductionMethod : public Method +{ + DimensionReductionMethod(const char* n, const DimensionReductionTraits& traits) + : Method(n) + , needs_kernel(traits.needs_kernel) + , needs_distance(traits.needs_distance) + , needs_features(traits.needs_features) + { + } + using Method::operator==; + bool needs_kernel; + bool needs_distance; + bool needs_features; +}; + +static const DimensionReductionTraits RequiresKernel{true, false, false}; +static const DimensionReductionTraits RequiresKernelAndFeatures{true, false, true}; +static const DimensionReductionTraits RequiresDistance{false, true, false}; +static const DimensionReductionTraits RequiresDistanceAndFeatures{false, true, true}; +static const DimensionReductionTraits RequiresFeatures{false, false, true}; + +/** Kernel Locally Linear Embedding as described + * in @cite Decoste2001 */ +static const DimensionReductionMethod KernelLocallyLinearEmbedding("Kernel Locally Linear Embedding (KLLE)", RequiresKernel); + +/** Neighborhood Preserving Embedding as described + * in @cite He2005 */ +static const DimensionReductionMethod NeighborhoodPreservingEmbedding("Neighborhood Preserving Embedding (NPE)", RequiresKernelAndFeatures); + +/** Local Tangent Space Alignment as described + * in @cite Zhang2002 */ +static const DimensionReductionMethod KernelLocalTangentSpaceAlignment("Kernel Local Tangent Space Alignment (KLTSA)", RequiresKernel); + +/** Linear Local Tangent Space Alignment as described + * in @cite Zhang2007 */ +static const DimensionReductionMethod LinearLocalTangentSpaceAlignment("Linear Local Tangent Space Alignment (LLTSA)", RequiresKernelAndFeatures); + +/** Hessian Locally Linear Embedding as described in + * @cite Donoho2003 */ +static const DimensionReductionMethod HessianLocallyLinearEmbedding("Hessian Locally Linear Embedding (HLLE)", RequiresKernel); + +/** Laplacian Eigenmaps as described in + * @cite Belkin2002 */ +static const DimensionReductionMethod LaplacianEigenmaps("Laplacian Eigenmaps", RequiresDistance); + +/** Locality Preserving Projections as described in + * @cite He2003 */ +static const DimensionReductionMethod LocalityPreservingProjections("Locality Preserving Projections (LPP)", RequiresDistanceAndFeatures); + +/** Diffusion map as described in + * @cite Coifman2006 */ +static const DimensionReductionMethod DiffusionMap("Diffusion Map", RequiresDistance); + +/** Isomap as described in + * @cite Tenenbaum2000 */ +static const DimensionReductionMethod Isomap("Isomap", RequiresDistance); + +/** Landmark Isomap as described in + * @cite deSilva2002 */ +static const DimensionReductionMethod LandmarkIsomap("Landmark Isomap", RequiresDistance); + +/** Multidimensional scaling as described in + * @cite Cox2000 */ +static const DimensionReductionMethod MultidimensionalScaling("Multidimensional Scaling (MDS)", RequiresDistance); + +/** Landmark multidimensional scaling as described in + * @cite deSilva2004 */ +static const DimensionReductionMethod LandmarkMultidimensionalScaling("Landmark Multidimensional Scaling (LMDS)", RequiresDistance); + +/** Stochastic Proximity Embedding as described in + * @cite Agrafiotis2003 */ +static const DimensionReductionMethod StochasticProximityEmbedding("Stochastic Proximity Embedding (SPE)", RequiresDistanceAndFeatures); + +/** Kernel PCA as described in + * @cite Scholkopf1997 */ +static const DimensionReductionMethod KernelPCA("Kernel Principal Component Analysis (KPCA)", RequiresKernel); + +/** Principal Component Analysis */ +static const DimensionReductionMethod PCA("Principal Component Analysis (PCA)", RequiresFeatures); + +/** Random Projection as described in + * @cite Kaski1998*/ +static const DimensionReductionMethod RandomProjection("Random Projection", RequiresFeatures); + +/** Factor Analysis */ +static const DimensionReductionMethod FactorAnalysis("Factor Analysis", RequiresFeatures); + +/** t-SNE and Barnes-Hut-SNE as described in + * @cite vanDerMaaten2008 and @cite vanDerMaaten2013 */ +static const DimensionReductionMethod tDistributedStochasticNeighborEmbedding("t-distributed Stochastic Neighbor Embedding (t-SNE)", RequiresFeatures); + +/** Manifold Sculpting as described in + * @cite Gashler2007 */ +static const DimensionReductionMethod ManifoldSculpting("Manifold Sculpting", RequiresFeatures); + +/** Passing through (doing nothing just passes the + * data through) */ +static const DimensionReductionMethod PassThru("Pass-through", RequiresFeatures); + struct NeighborsMethod : public Method { NeighborsMethod(const char* n) : Method(n) @@ -199,7 +220,7 @@ struct EigendecompositionStrategy : public Method { return skip_; } - IndexType skip_; + const IndexType skip_; }; static const EigendecompositionStrategy LargestEigenvalues("Largest eigenvalues", 0); diff --git a/include/tapkee/methods.hpp b/include/tapkee/methods.hpp index a99bc5e..4d2cd93 100644 --- a/include/tapkee/methods.hpp +++ b/include/tapkee/methods.hpp @@ -22,58 +22,54 @@ class DynamicImplementation : public ImplementationBase::ImplementationBase; - TapkeeOutput embedUsing(DimensionReductionMethod method) + TapkeeOutput embedUsing(const DimensionReductionMethod& method) { + timed_context tctx__(fmt::format("[+] embedding with {}", method.name())); + if (this->context.is_cancelled()) throw cancelled_exception(); + if (method.needs_kernel && is_dummy::value) + { + throw unsupported_method_error("Kernel callback is missed"); + } + if (method.needs_distance && is_dummy::value) + { + throw unsupported_method_error("Distance callback is missed"); + } + if (method.needs_features && is_dummy::value) + { + throw unsupported_method_error("Features callback is missed"); + } + + const auto& self = static_cast>(*this); + #define tapkee_method_handle(X) \ - case X: { \ - timed_context tctx__("[+] embedding with " #X); \ - if (MethodTraits::needs_kernel && is_dummy::value) \ - { \ - throw unsupported_method_error("Kernel callback is missed"); \ - } \ - if (MethodTraits::needs_distance && is_dummy::value) \ - { \ - throw unsupported_method_error("Distance callback is missed"); \ - } \ - if (MethodTraits::needs_features && is_dummy::value) \ - { \ - throw unsupported_method_error("Features callback is missed"); \ - } \ - auto self = static_cast>(*this); \ + if (method == X) { \ auto implementation = \ X ## Implementation(self); \ return implementation.embed(); \ - } \ - break; - - switch (method) - { - tapkee_method_handle(KernelLocallyLinearEmbedding); - tapkee_method_handle(KernelLocalTangentSpaceAlignment); - tapkee_method_handle(DiffusionMap); - tapkee_method_handle(MultidimensionalScaling); - tapkee_method_handle(LandmarkMultidimensionalScaling); - tapkee_method_handle(Isomap); - tapkee_method_handle(LandmarkIsomap); - tapkee_method_handle(NeighborhoodPreservingEmbedding); - tapkee_method_handle(LinearLocalTangentSpaceAlignment); - tapkee_method_handle(HessianLocallyLinearEmbedding); - tapkee_method_handle(LaplacianEigenmaps); - tapkee_method_handle(LocalityPreservingProjections); - tapkee_method_handle(PCA); - tapkee_method_handle(KernelPCA); - tapkee_method_handle(RandomProjection); - tapkee_method_handle(StochasticProximityEmbedding); - tapkee_method_handle(PassThru); - tapkee_method_handle(FactorAnalysis); - tapkee_method_handle(tDistributedStochasticNeighborEmbedding); - tapkee_method_handle(ManifoldSculpting); - default: - break; - } + } + tapkee_method_handle(KernelLocallyLinearEmbedding); + tapkee_method_handle(KernelLocalTangentSpaceAlignment); + tapkee_method_handle(DiffusionMap); + tapkee_method_handle(MultidimensionalScaling); + tapkee_method_handle(LandmarkMultidimensionalScaling); + tapkee_method_handle(Isomap); + tapkee_method_handle(LandmarkIsomap); + tapkee_method_handle(NeighborhoodPreservingEmbedding); + tapkee_method_handle(LinearLocalTangentSpaceAlignment); + tapkee_method_handle(HessianLocallyLinearEmbedding); + tapkee_method_handle(LaplacianEigenmaps); + tapkee_method_handle(LocalityPreservingProjections); + tapkee_method_handle(PCA); + tapkee_method_handle(KernelPCA); + tapkee_method_handle(RandomProjection); + tapkee_method_handle(StochasticProximityEmbedding); + tapkee_method_handle(PassThru); + tapkee_method_handle(FactorAnalysis); + tapkee_method_handle(tDistributedStochasticNeighborEmbedding); + tapkee_method_handle(ManifoldSculpting); #undef tapkee_method_handle return TapkeeOutput(); } diff --git a/include/tapkee/methods/base.hpp b/include/tapkee/methods/base.hpp index fb80f0d..1a06a60 100644 --- a/include/tapkee/methods/base.hpp +++ b/include/tapkee/methods/base.hpp @@ -87,14 +87,14 @@ class ImplementationBase }; -#define __TAPKEE_IMPLEMENTATION(Method) \ - template \ - class Method ## Implementation : public ImplementationBase \ - { \ - public: \ - Method ## Implementation(const ImplementationBase& other) : \ - ImplementationBase(other) \ - { \ +#define __TAPKEE_IMPLEMENTATION(Method) \ + template \ + class Method ## Implementation : public ImplementationBase \ + { \ + public: \ + Method ## Implementation(const ImplementationBase& other) : \ + ImplementationBase(other) \ + { \ } #define __TAPKEE_END_IMPLEMENTATION() }; diff --git a/include/tapkee/traits/callbacks_traits.hpp b/include/tapkee/traits/callbacks_traits.hpp index 14f415c..34d4fa9 100644 --- a/include/tapkee/traits/callbacks_traits.hpp +++ b/include/tapkee/traits/callbacks_traits.hpp @@ -7,12 +7,6 @@ namespace tapkee { -template struct BatchCallbackTraits -{ - static const bool supports_batch; -}; -#define TAPKEE_CALLBACK_SUPPORTS_BATCH(X) template <> const bool BatchCallbackTraits::supports_batch = true; - template class is_dummy { typedef char yes; diff --git a/include/tapkee/traits/methods_traits.hpp b/include/tapkee/traits/methods_traits.hpp deleted file mode 100644 index 73989b1..0000000 --- a/include/tapkee/traits/methods_traits.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/* This software is distributed under BSD 3-clause license (see LICENSE file). - * - * Copyright (c) 2012-2013 Sergey Lisitsyn, Fernando Iglesias - */ -#pragma once - -namespace tapkee -{ - -//! Traits used to obtain information about dimension reduction methods compile-time -//! -template struct MethodTraits; - -#define METHOD_TRAIT(X, kernel_needed, distance_needed, features_needed) \ - template struct MethodTraits::type> \ - { \ - static const bool needs_kernel = kernel_needed; \ - static const bool needs_distance = distance_needed; \ - static const bool needs_features = features_needed; \ - } - -#define METHOD_THAT_NEEDS_ONLY_KERNEL_IS(X) METHOD_TRAIT(X, true, false, false) -#define METHOD_THAT_NEEDS_ONLY_DISTANCE_IS(X) METHOD_TRAIT(X, false, true, false) -#define METHOD_THAT_NEEDS_KERNEL_AND_FEATURES_IS(X) METHOD_TRAIT(X, true, false, true) -#define METHOD_THAT_NEEDS_DISTANCE_AND_FEATURES_IS(X) METHOD_TRAIT(X, false, true, true) -#define METHOD_THAT_NEEDS_ONLY_FEATURES_IS(X) METHOD_TRAIT(X, false, false, true) -#define METHOD_THAT_NEEDS_NOTHING_IS(X) METHOD_TRAIT(X, false, false, false) -} // namespace tapkee diff --git a/include/tapkee/utils/naming.hpp b/include/tapkee/utils/naming.hpp index f54ba49..3078fa5 100644 --- a/include/tapkee/utils/naming.hpp +++ b/include/tapkee/utils/naming.hpp @@ -12,52 +12,9 @@ namespace tapkee { /** Returns the name of the provided method */ -inline std::string get_method_name(DimensionReductionMethod m) +inline std::string get_method_name(const DimensionReductionMethod& m) { - switch (m) - { - case KernelLocallyLinearEmbedding: - return "Kernel Locally Linear Embedding"; - case KernelLocalTangentSpaceAlignment: - return "Local Tangent Space Alignment"; - case DiffusionMap: - return "Diffusion Map"; - case MultidimensionalScaling: - return "Classic Multidimensional Scaling"; - case LandmarkMultidimensionalScaling: - return "Landmark Multidimensional Scaling"; - case Isomap: - return "Isomap"; - case LandmarkIsomap: - return "Landmark Isomap"; - case NeighborhoodPreservingEmbedding: - return "Neighborhood Preserving Embedding"; - case LinearLocalTangentSpaceAlignment: - return "Linear Local Tangent Space Alignment"; - case HessianLocallyLinearEmbedding: - return "Hessian Locally Linear Embedding"; - case LaplacianEigenmaps: - return "Laplacian Eigenmaps"; - case LocalityPreservingProjections: - return "Locality Preserving Embedding"; - case PCA: - return "Principal Component Analysis"; - case KernelPCA: - return "Kernel Principal Component Analysis"; - case StochasticProximityEmbedding: - return "Stochastic Proximity Embedding"; - case PassThru: - return "passing through"; - case RandomProjection: - return "Random Projection"; - case FactorAnalysis: - return "Factor Analysis"; - case tDistributedStochasticNeighborEmbedding: - return "t-distributed Stochastic Neighbor Embedding"; - case ManifoldSculpting: - return "manifold sculpting"; - } - return "hello"; + return m.name(); } /** Returns the name of the provided neighbors method */ diff --git a/src/cli/main.cpp b/src/cli/main.cpp index e13aef6..b22919d 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -293,7 +293,7 @@ int run(int argc, const char **argv) tapkee::LoggingSingleton::instance().message_info("Benchmarking enabled"); } - tapkee::DimensionReductionMethod tapkee_method; + tapkee::DimensionReductionMethod tapkee_method = tapkee::PassThru; { string method = opt[METHOD_KEYWORD].as(); try @@ -439,13 +439,13 @@ int run(int argc, const char **argv) tapkee::DenseMatrix distance_matrix; tapkee::DenseMatrix kernel_matrix; { - if (method_needs_distance(tapkee_method)) + if (tapkee_method.needs_distance) { tapkee::tapkee_internal::timed_context context("[+] Distance matrix computation"); distance_matrix = matrix_from_callback(static_cast(input_data.cols()), tapkee::eigen_distance_callback(input_data)); } - if (method_needs_kernel(tapkee_method)) + if (tapkee_method.needs_kernel) { tapkee::tapkee_internal::timed_context context("[+] Kernel matrix computation"); kernel_matrix = matrix_from_callback(static_cast(input_data.cols()), diff --git a/src/cli/util.hpp b/src/cli/util.hpp index 7e66611..43e70d1 100644 --- a/src/cli/util.hpp +++ b/src/cli/util.hpp @@ -95,72 +95,6 @@ void write_vector(tapkee::DenseVector* matrix, ofstream& of) } } -bool method_needs_kernel(tapkee::DimensionReductionMethod method) -{ - switch (method) - { -#define IF_NEEDS_KERNEL(X) \ - case X: \ - return tapkee::MethodTraits::needs_kernel; \ - break; - IF_NEEDS_KERNEL(tapkee::KernelLocalTangentSpaceAlignment); - IF_NEEDS_KERNEL(tapkee::KernelLocallyLinearEmbedding); - IF_NEEDS_KERNEL(tapkee::HessianLocallyLinearEmbedding); - IF_NEEDS_KERNEL(tapkee::MultidimensionalScaling); - IF_NEEDS_KERNEL(tapkee::LandmarkMultidimensionalScaling); - IF_NEEDS_KERNEL(tapkee::Isomap); - IF_NEEDS_KERNEL(tapkee::LandmarkIsomap); - IF_NEEDS_KERNEL(tapkee::DiffusionMap); - IF_NEEDS_KERNEL(tapkee::KernelPCA); - IF_NEEDS_KERNEL(tapkee::PCA); - IF_NEEDS_KERNEL(tapkee::RandomProjection); - IF_NEEDS_KERNEL(tapkee::LaplacianEigenmaps); - IF_NEEDS_KERNEL(tapkee::LocalityPreservingProjections); - IF_NEEDS_KERNEL(tapkee::NeighborhoodPreservingEmbedding); - IF_NEEDS_KERNEL(tapkee::LinearLocalTangentSpaceAlignment); - IF_NEEDS_KERNEL(tapkee::StochasticProximityEmbedding); - IF_NEEDS_KERNEL(tapkee::PassThru); - IF_NEEDS_KERNEL(tapkee::FactorAnalysis); - IF_NEEDS_KERNEL(tapkee::tDistributedStochasticNeighborEmbedding); - IF_NEEDS_KERNEL(tapkee::ManifoldSculpting); -#undef IF_NEEDS_KERNEL - } - return false; -} - -bool method_needs_distance(tapkee::DimensionReductionMethod method) -{ - switch (method) - { -#define IF_NEEDS_DISTANCE(X) \ - case X: \ - return tapkee::MethodTraits::needs_distance; \ - break; - IF_NEEDS_DISTANCE(tapkee::KernelLocalTangentSpaceAlignment); - IF_NEEDS_DISTANCE(tapkee::KernelLocallyLinearEmbedding); - IF_NEEDS_DISTANCE(tapkee::HessianLocallyLinearEmbedding); - IF_NEEDS_DISTANCE(tapkee::MultidimensionalScaling); - IF_NEEDS_DISTANCE(tapkee::LandmarkMultidimensionalScaling); - IF_NEEDS_DISTANCE(tapkee::Isomap); - IF_NEEDS_DISTANCE(tapkee::LandmarkIsomap); - IF_NEEDS_DISTANCE(tapkee::DiffusionMap); - IF_NEEDS_DISTANCE(tapkee::KernelPCA); - IF_NEEDS_DISTANCE(tapkee::PCA); - IF_NEEDS_DISTANCE(tapkee::RandomProjection); - IF_NEEDS_DISTANCE(tapkee::LaplacianEigenmaps); - IF_NEEDS_DISTANCE(tapkee::LocalityPreservingProjections); - IF_NEEDS_DISTANCE(tapkee::NeighborhoodPreservingEmbedding); - IF_NEEDS_DISTANCE(tapkee::LinearLocalTangentSpaceAlignment); - IF_NEEDS_DISTANCE(tapkee::StochasticProximityEmbedding); - IF_NEEDS_DISTANCE(tapkee::PassThru); - IF_NEEDS_DISTANCE(tapkee::FactorAnalysis); - IF_NEEDS_DISTANCE(tapkee::tDistributedStochasticNeighborEmbedding); - IF_NEEDS_DISTANCE(tapkee::ManifoldSculpting); -#undef IF_NEEDS_DISTANCE - } - return false; -} - tapkee::DimensionReductionMethod parse_reduction_method(const char* str) { if (!strcmp(str, "local_tangent_space_alignment") || !strcmp(str, "ltsa"))