Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert DimensionReductionMethod to a class instead of enum #94

Merged
merged 6 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion include/tapkee/defines.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
/* Tapkee includes */
#include <tapkee/exceptions.hpp>
#include <tapkee/traits/callbacks_traits.hpp>
#include <tapkee/traits/methods_traits.hpp>
/* End of Tapkee includes */

#include <iterator>
Expand Down
190 changes: 103 additions & 87 deletions include/tapkee/defines/methods.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,97 +4,12 @@
*/
#pragma once

/* Tapkee includes */
#include <tapkee/defines/types.hpp>
#include <tapkee/traits/methods_traits.hpp>
/* 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 <typename M> struct Method
{
Expand All @@ -119,6 +34,107 @@ template <typename M> struct Method
const char* name_;
};

typedef std::tuple<bool, bool, bool> KFDTraits;

struct DimensionReductionMethod : public Method<DimensionReductionMethod>
{
DimensionReductionMethod(const char* n, const KFDTraits& traits)
: Method<DimensionReductionMethod>(n)
, needs_kernel(std::get<0>(traits))
, needs_distance(std::get<1>(traits))
, needs_features(std::get<2>(traits))
{
}
using Method<DimensionReductionMethod>::operator==;
bool needs_kernel;
bool needs_distance;
bool needs_features;
};

static const KFDTraits RequiresKernel{true, false, false};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe here consider std::bitset<3>, it will look a bit more concise, I think particularly below in the lookup with traits.test(i) instead of std::get<i>(traits). Although for readability or expressiveness below, probably a struct with three names fields would be preferable. I think all three are good and probably prefer the bitset one most :-)

static const KFDTraits RequiresKernelAndFeatures{true, false, true};
static const KFDTraits RequiresDistance{false, true, false};
static const KFDTraits RequiresDistanceAndFeatures{false, true, true};
static const KFDTraits 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>
{
NeighborsMethod(const char* n) : Method<NeighborsMethod>(n)
Expand Down
81 changes: 38 additions & 43 deletions include/tapkee/methods.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,58 +22,53 @@ class DynamicImplementation : public ImplementationBase<RandomAccessIterator, Ke
{
public:
using ImplementationBase<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback>::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<KernelCallback>::value)
{
throw unsupported_method_error("Kernel callback is missed");
}
if (method.needs_distance && is_dummy<DistanceCallback>::value)
{
throw unsupported_method_error("Distance callback is missed");
}
if (method.needs_features && is_dummy<FeaturesCallback>::value)
{
throw unsupported_method_error("Features callback is missed");
}

#define tapkee_method_handle(X) \
case X: { \
timed_context tctx__("[+] embedding with " #X); \
if (MethodTraits<X>::needs_kernel && is_dummy<KernelCallback>::value) \
{ \
throw unsupported_method_error("Kernel callback is missed"); \
} \
if (MethodTraits<X>::needs_distance && is_dummy<DistanceCallback>::value) \
{ \
throw unsupported_method_error("Distance callback is missed"); \
} \
if (MethodTraits<X>::needs_features && is_dummy<FeaturesCallback>::value) \
{ \
throw unsupported_method_error("Features callback is missed"); \
} \
if (method.is(X)) { \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be method == X so that Method::is(const Method&) is not needed? Just wild-asking, it can be that the method is required in other parts of the code. I was thinking toward removing Method or at least the inheritance relationship there for a bit more of code reduction.

auto self = static_cast<ImplementationBase<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback>>(*this); \
auto implementation = \
X ## Implementation<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback>(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();
}
Expand Down
16 changes: 8 additions & 8 deletions include/tapkee/methods/base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ class ImplementationBase

};

#define __TAPKEE_IMPLEMENTATION(Method) \
template <class RandomAccessIterator, class KernelCallback, class DistanceCallback, class FeaturesCallback> \
class Method ## Implementation : public ImplementationBase<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback> \
{ \
public: \
Method ## Implementation(const ImplementationBase<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback>& other) : \
ImplementationBase<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback>(other) \
{ \
#define __TAPKEE_IMPLEMENTATION(Method) \
template <class RandomAccessIterator, class KernelCallback, class DistanceCallback, class FeaturesCallback> \
class Method ## Implementation : public ImplementationBase<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback> \
{ \
public: \
Method ## Implementation(const ImplementationBase<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback>& other) : \
ImplementationBase<RandomAccessIterator, KernelCallback, DistanceCallback, FeaturesCallback>(other) \
{ \
}
#define __TAPKEE_END_IMPLEMENTATION() };

Expand Down
28 changes: 0 additions & 28 deletions include/tapkee/traits/methods_traits.hpp

This file was deleted.

Loading
Loading