From fc4ece332bcee0e22cdba99dca04aad4a982639a Mon Sep 17 00:00:00 2001 From: Shangtong Zhang <302536811@qq.com> Date: Sat, 16 May 2015 23:56:38 +0800 Subject: [PATCH 1/2] implement a framework for manifold learning include Isomap, LLE, LE and MDS --- src/mlpack/methods/CMakeLists.txt | 2 +- .../eigen_solver/laplacian_solver.hpp | 52 +++ .../eigen_solver/standard_solver.hpp | 61 +++ .../eigenmap_rule/standard_eigenmap.hpp | 116 +++++ .../isomap_extended_kernel.hpp | 146 +++++++ .../extended_kernel/le_extended_kernel.hpp | 139 ++++++ .../extended_kernel/lle_extended_kernel.hpp | 123 ++++++ .../extended_kernel/mds_extended_kernel.hpp | 133 ++++++ .../manifold_learning/manifold_learning.hpp | 265 ++++++++++++ .../shortest_path/dijkstra.hpp | 125 ++++++ .../shortest_path/floyd_ warshall.hpp | 65 +++ .../similarity_rule/epsilon_similarity.hpp | 234 ++++++++++ .../similarity_rule/k_similarity.hpp | 223 ++++++++++ .../transform_rule/isomap_transform.hpp | 67 +++ .../transform_rule/le_transform.hpp | 92 ++++ .../transform_rule/lle_transform.hpp | 72 ++++ .../transform_rule/mds_transform.hpp | 56 +++ src/mlpack/tests/CMakeLists.txt | 1 + src/mlpack/tests/data/swissroll.dat | 400 ++++++++++++++++++ src/mlpack/tests/manifold_learning_test.cpp | 264 ++++++++++++ 20 files changed, 2635 insertions(+), 1 deletion(-) create mode 100644 src/mlpack/methods/manifold_learning/eigen_solver/laplacian_solver.hpp create mode 100644 src/mlpack/methods/manifold_learning/eigen_solver/standard_solver.hpp create mode 100644 src/mlpack/methods/manifold_learning/eigenmap_rule/standard_eigenmap.hpp create mode 100644 src/mlpack/methods/manifold_learning/extended_kernel/isomap_extended_kernel.hpp create mode 100644 src/mlpack/methods/manifold_learning/extended_kernel/le_extended_kernel.hpp create mode 100644 src/mlpack/methods/manifold_learning/extended_kernel/lle_extended_kernel.hpp create mode 100644 src/mlpack/methods/manifold_learning/extended_kernel/mds_extended_kernel.hpp create mode 100644 src/mlpack/methods/manifold_learning/manifold_learning.hpp create mode 100644 src/mlpack/methods/manifold_learning/shortest_path/dijkstra.hpp create mode 100644 src/mlpack/methods/manifold_learning/shortest_path/floyd_ warshall.hpp create mode 100644 src/mlpack/methods/manifold_learning/similarity_rule/epsilon_similarity.hpp create mode 100644 src/mlpack/methods/manifold_learning/similarity_rule/k_similarity.hpp create mode 100644 src/mlpack/methods/manifold_learning/transform_rule/isomap_transform.hpp create mode 100644 src/mlpack/methods/manifold_learning/transform_rule/le_transform.hpp create mode 100644 src/mlpack/methods/manifold_learning/transform_rule/lle_transform.hpp create mode 100644 src/mlpack/methods/manifold_learning/transform_rule/mds_transform.hpp create mode 100644 src/mlpack/tests/data/swissroll.dat create mode 100644 src/mlpack/tests/manifold_learning_test.cpp diff --git a/src/mlpack/methods/CMakeLists.txt b/src/mlpack/methods/CMakeLists.txt index 2b8b09aa587..62ae93dabee 100644 --- a/src/mlpack/methods/CMakeLists.txt +++ b/src/mlpack/methods/CMakeLists.txt @@ -11,13 +11,13 @@ set(DIRS hmm kernel_pca kmeans - mean_shift lars linear_regression local_coordinate_coding logistic_regression lsh # mvu + mean_shift matrix_completion naive_bayes nca diff --git a/src/mlpack/methods/manifold_learning/eigen_solver/laplacian_solver.hpp b/src/mlpack/methods/manifold_learning/eigen_solver/laplacian_solver.hpp new file mode 100644 index 00000000000..a46b3e84039 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/eigen_solver/laplacian_solver.hpp @@ -0,0 +1,52 @@ +/** + * @file laplacian_solver.hpp + * @author Shangtong Zhang + * + * Calculate eigen vectors after transforming a matrix to laplacian matrix. + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_EIGEN_SOLVER_LAPLACIAN_SOLVER +#define __MLPACK_METHODS_MANIFOLD_LEARNING_EIGEN_SOLVER_LAPLACIAN_SOLVER + +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for calculateing eigen vectors + * after transforming a matrix to laplacian matrix + */ +template< + typename MatType = arma::mat, + typename VecType = arma::colvec> +class LaplacianSolver +{ + public: + /** + * Calculate eigen vectors. + * Equation (S - M) * eigenvector = eigenvalue * S * eigenvector will be solved. + * + * @param M desired matrix + * @param eigvec store eigen vectors + * @param eigval store eigen values + */ + static void Solve(const MatType& M, MatType& eigvec, VecType& eigval) + { + arma::rowvec sumM = sum(M, 0); + MatType S(M.n_rows, M.n_cols); + S.zeros(); + S.diag() = sumM; + MatType L = S - M; + arma::cx_colvec cxEigval; + arma::cx_mat cxEigvec; + arma::eig_gen(cxEigval, cxEigvec, S.i() * L); + eigval = arma::real(cxEigval); + eigvec = arma::real(cxEigvec); + } + +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/eigen_solver/standard_solver.hpp b/src/mlpack/methods/manifold_learning/eigen_solver/standard_solver.hpp new file mode 100644 index 00000000000..3ef5836cc67 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/eigen_solver/standard_solver.hpp @@ -0,0 +1,61 @@ +/** + * @file standard_solver.hpp + * @author Shangtong Zhang + * + * Calculate eigen vectors directly. + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_EIGEN_SOLVER_STANDARD_SOLVER +#define __MLPACK_METHODS_MANIFOLD_LEARNING_EIGEN_SOLVER_STANDARD_SOLVER + +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for calculateing eigen vectors directly. + * @tparam Symmetric If the matrix is symmetric + */ +template< + bool Symmetric = false, + typename MatType = arma::mat, + typename VecType = arma::colvec> +class StandardSolver +{ + public: + + /** + * Calculate eigen vectors for symmetric matrix + * @param M desired matrix + * @param eigvec store eigen vectors + * @param eigval store eigen values + */ + template + static typename std::enable_if::type + Solve(const MatType& M, MatType& eigvec, VecType& eigval) + { + arma::eig_sym(eigval, eigvec, M); + } + + /** + * Calculate eigen vectors for general matrix + * @param M desired matrix + * @param eigvec store eigen vectors + * @param eigval store eigen values + */ + template + static typename std::enable_if::type + Solve(const MatType& M, MatType& eigvec, VecType& eigval) + { + arma::cx_vec cxEigval; + arma::cx_mat cxEigvec; + arma::eig_gen(cxEigval, cxEigvec, M); + eigval = arma::real(cxEigval); + eigvec = arma::real(cxEigvec); + } +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/eigenmap_rule/standard_eigenmap.hpp b/src/mlpack/methods/manifold_learning/eigenmap_rule/standard_eigenmap.hpp new file mode 100644 index 00000000000..f084e445985 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/eigenmap_rule/standard_eigenmap.hpp @@ -0,0 +1,116 @@ +/** + * @file standard_eigenmap.hpp + * @author Shangtong Zhang + * + * Implementation for calculating eigen vectors and map them to + * embedding vectors. + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_EIGENMAP_RULE_STANDARD_EIGENMAP +#define __MLPACK_METHODS_MANIFOLD_LEARNING_EIGENMAP_RULE_STANDARD_EIGENMAP + +#include +#include +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for calculating eigen vectors from transformed + * similarity matrix and map them to embedding vectors. + * + * @tparam SolverType How to get eigen vectors from transformed similarity matrix. + * @tparam SkippedEigvec # of eigen vectors to be skipped + * @tparam UseSqrtEigval If true, eigen vector will be multiplied by square root of + * of corresponding eigen value when mapping. + */ + +template< + typename SolverType, + size_t SkippedEigvec = 0, + bool UseSqrtEigval = true, + typename MatType = arma::mat> +class StandardEigenmap +{ + public: + + /** + * Calculating embedding vectors. + * @param M Tranformed similarity matrix + * @param embeddingMat Store embedding vectors, each column represents a point + * @dim dimension of embedding vectors + */ + void Eigenmap(const MatType& M, + MatType& embeddingMat, + size_t dim) + { + // Get eigen vectors and eigen values + arma::colvec eigval; + arma::mat eigvec; + SolverType::Solve(M, eigvec, eigval); + + // Sort eigen values in descending order + arma::uvec ind = arma::sort_index(eigval, "descend"); + + orderedEigvec.set_size(M.n_cols, dim); + orderedEigval.set_size(dim, 1); + size_t dimCount = 0; + + // Map eigen vectors to embedding vectors + for (size_t i = SkippedEigvec; i < ind.n_elem; ++i) + { + if (dimCount >= dim || eigval(ind(i)) < 0) + break; + orderedEigvec.col(dimCount) = eigvec.unsafe_col(ind(i)); + if (UseSqrtEigval) + orderedEigvec.col(dimCount) *= std::sqrt(eigval(ind(i))); + orderedEigval(dimCount) = eigval(ind(i)); + dimCount++; + } + embeddingMat = orderedEigvec.t(); + } + + //! Get eigen vectors + MatType& Eigvec() const { return orderedEigvec; } + //! Modify eigen vectors + MatType& Eigvec() { return orderedEigvec; } + + //! Get eigen values + arma::colvec& Eigval() const { return orderedEigval; } + //! Modify eigen values + arma::colvec& Eigval() { return orderedEigval; } + + private: + //! Locally-stored eigen vectors + MatType orderedEigvec; + + //! Locally-stored eigen values + arma::colvec orderedEigval; +}; + +// typedef for convenience + +// define eigenmap rule for MDS +template +using MDSEigenmap = StandardEigenmap< + StandardSolver, 0, true, MatType>; + +// define eigenmap rule for Isomap +template +using IsomapEigenmap = StandardEigenmap< + StandardSolver, 0, true, MatType>; + +// define eigenmap rule for LLE +template +using LLEEigenmap = StandardEigenmap< + StandardSolver, 1, false, MatType>; + +// define eigenmap rule for LE +template +using LEEigenmap = StandardEigenmap< + LaplacianSolver, 1, false, MatType>; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/extended_kernel/isomap_extended_kernel.hpp b/src/mlpack/methods/manifold_learning/extended_kernel/isomap_extended_kernel.hpp new file mode 100644 index 00000000000..b9b948f7655 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/extended_kernel/isomap_extended_kernel.hpp @@ -0,0 +1,146 @@ +/** + * @file isomap_extended_kernel.hpp + * @author Shangtong Zhang + * + * This is a kernel used to calculate the embedding vector for a new data point + * for Isomap + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_EXTENDED_KERNEL_ISOMAP_EXTENDED_KERNEL +#define __MLPACK_METHODS_MANIFOLD_LEARNING_EXTENDED_KERNEL_ISOMAP_EXTENDED_KERNEL + +#include + +namespace mlpack { +namespace manifold { + +/** + * This is a kernel used to calculate the embedding vector for a new data point + * for Isomap. + * @tparam SimilarityRule How to build similarity (affinity) matrix given a dataset + * @tparam TransformRule How to transform similarity matrix + * @tparam EigenmapRule How to generate embedding vectors from transformed + * similarity matrix + */ +template< + typename SimilarityRule = KIsomapSimilarity<>, + typename TransformRule = IsomapTransform<>, + typename EigenmapRule = IsomapEigenmap<>, + typename MatType = arma::mat, + typename VecType = arma::colvec> +class IsomapExtendedKernel +{ + public: + /** + * Create a IsomapExtendedKernel object and set the parameters. + * @param data Original data set + * @param similarity Adopted similarity rule + * @param transform Adopted transform rule + * @param eigenmap Adopted eigenmap rule + */ + IsomapExtendedKernel(const MatType& data, + SimilarityRule& similarity, + TransformRule& transform, + EigenmapRule& eigenmap) : + data(data), similarity(similarity), transform(transform), + eigenmap(eigenmap), searcher(data), distMat(transform.DistMat()) + { + /* nothing to do */ + } + + /** + * Fit the new data point + * @param point new data point + */ + void FitPoint(const VecType& point) + { + similarity.BuildSimilarityVec(searcher, point, neighbors, distances); + + // precalculate the expectation over the whole original data set + entireE = 0; + for (size_t i = 0; i < data.n_cols; ++i) + for (size_t j = i; j < data.n_cols; ++j) + entireE += std::pow(GramD(i, j), 2); + entireE /= (data.n_cols * (data.n_cols + 1) / 2); + + // precalculate the expectation for the new point + pointE = 0; + for (size_t i = 0; i < data.n_cols; ++i) + pointE += std::pow(GramD(i), 2); + pointE /= data.n_cols; + } + + /** + * Evaluate the value for the new data point and a given point + * @param target the index of the given point + */ + double Evaluate(size_t target) + { + return -0.5 * (std::pow(GramD(target), 2) - + pointE - Expectation(target) + entireE); + } + + private: + + // calculate the extended distance between point i and point j in original data + double GramD(size_t i, size_t j) + { + return distMat(i, j); + } + + /** + * calculate the extended distance between the new point and a given point + * @param target the index of the given point + */ + double GramD(size_t target) + { + double shortest = arma::datum::inf; + for (size_t i = 0; i < neighbors.size(); ++i) + shortest = std::min(distances[i] + distMat(neighbors[i], target), shortest); + return shortest; + } + + // calculate the expectation for a point i + double Expectation(size_t i) + { + double E = 0; + for (size_t j = 0; j < data.n_cols; ++j) + E += std::pow(GramD(i, j), 2); + return E / data.n_cols; + } + + //! Locally-stored original data set + const MatType& data; + + //! Locally-stored similarity rule + SimilarityRule& similarity; + + //! Locally-stored transform rule + TransformRule& transform; + + //! Locally-stored eigenmap rule + EigenmapRule& eigenmap; + + //! Locally-stored searcher over the original data set + typename std::remove_reference:: + type::SearcherType searcher; + + //! Locally-stored distance matrix + MatType& distMat; + + //! Locally-stored neighbors of the new point + std::vector neighbors; + + //! Locally-stored distances between the new point and its neighbors + std::vector distances; + + //! Expectation over the whole data set + double entireE; + + //! Expectation for the new point + double pointE; +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/extended_kernel/le_extended_kernel.hpp b/src/mlpack/methods/manifold_learning/extended_kernel/le_extended_kernel.hpp new file mode 100644 index 00000000000..c4d2722b35c --- /dev/null +++ b/src/mlpack/methods/manifold_learning/extended_kernel/le_extended_kernel.hpp @@ -0,0 +1,139 @@ +/** + * @file le_extended_kernel.hpp + * @author Shangtong Zhang + * + * This is a kernel used to calculate the embedding vector for a new data point + * for LE + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_EXTENDED_KERNEL_LE_EXTENDED_KERNEL +#define __MLPACK_METHODS_MANIFOLD_LEARNING_EXTENDED_KERNEL_LE_EXTENDED_KERNEL + +#include + +namespace mlpack { +namespace manifold { + +/** + * This is a kernel used to calculate the embedding vector for a new data point + * for LE. + * @tparam SimilarityRule How to build similarity (affinity) matrix given a dataset + * @tparam TransformRule How to transform similarity matrix + * @tparam EigenmapRule How to generate embedding vectors from transformed + * similarity matrix + */ +template< + typename SimilarityRule = KLESimilarity<>, + typename TransformRule = LETransform<>, + typename EigenmapRule = LEEigenmap<>, + typename MatType = arma::mat, + typename VecType = arma::colvec> +class LEExtendedKernel +{ + public: + /** + * Create a LEExtendedKernel object and set the parameters. + * @param data Original data set + * @param similarity Adopted similarity rule + * @param transform Adopted transform rule + * @param eigenmap Adopted eigenmap rule + */ + LEExtendedKernel(const MatType& data, + SimilarityRule& similarity, + TransformRule& transform, + EigenmapRule& eigenmap) : + data(data), similarity(similarity), transform(transform), + eigenmap(eigenmap), searcher(data) + { + /* nothing to do */ + } + + /** + * Fit the new data point + * @param point new data point + */ + void FitPoint(const VecType& point) + { + // precalculate the expectation for the new point + pointE = 0; + similarity.BuildSimilarityVec(searcher, point, neighbors, distances); + kValue.zeros(data.n_cols); + for (size_t i = 0; i < distances.size(); ++i) + { + if (transform.NNKernel()) + kValue(neighbors[i]) = 1; + else + kValue(neighbors[i]) = transform.Kernel().Evaluate(distances[i]); + + pointE += kValue(neighbors[i]); + } + pointE /= data.n_cols; + } + + /** + * Evaluate the value for the new data point and a given point + * @param target the index of the given point + */ + double Evaluate(size_t target) + { + return 1.0 / data.n_cols * K(target) / + std::sqrt(pointE * Expectation(target)); + } + + private: + + /** + * Calculate the value for the new point and a give point + * @param target the index of the given point + */ + double K(size_t target) + { + return kValue(target); + } + + /** + * calculate the expectation for a point + * @param fixed the index of the given point + */ + double Expectation(size_t fixed) + { + double E = 0; + + for (size_t i = 0; i < data.n_cols; ++i) + E += transform.GramM()(i, fixed); + + return E / (data.n_cols - 1); + } + + //! Locally-stored original data set + const MatType& data; + + //! Locally-stored similarity rule + SimilarityRule& similarity; + + //! Locally-stored transform rule + TransformRule& transform; + + //! Locally-stored eigenmap rule + EigenmapRule& eigenmap; + + //! Locally-stored searcher over the original data set + typename std::remove_reference:: + type::SearcherType searcher; + + //! Locally-stored neighbors of the new point + std::vector neighbors; + + //! Locally-stored distances between the new point and its neighbors + std::vector distances; + + //! Precalculated kernel value between the new point and its neighbors + arma::colvec kValue; + + //! Expectation for the new point + double pointE; +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/extended_kernel/lle_extended_kernel.hpp b/src/mlpack/methods/manifold_learning/extended_kernel/lle_extended_kernel.hpp new file mode 100644 index 00000000000..9425116f162 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/extended_kernel/lle_extended_kernel.hpp @@ -0,0 +1,123 @@ +/** + * @file lle_extended_kernel.hpp + * @author Shangtong Zhang + * + * This is a kernel used to calculate the embedding vector for a new data point + * for LLE + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_EXTENDED_KERNEL_LLE_EXTENDED_KERNEL +#define __MLPACK_METHODS_MANIFOLD_LEARNING_EXTENDED_KERNEL_LLE_EXTENDED_KERNEL + +#include + +namespace mlpack { +namespace manifold { + +/** + * This is a kernel used to calculate the embedding vector for a new data point + * for LLE. + * @tparam SimilarityRule How to build similarity (affinity) matrix given a dataset + * @tparam TransformRule How to transform similarity matrix + * @tparam EigenmapRule How to generate embedding vectors from transformed + * similarity matrix + */ +template< + typename SimilarityRule = KLLESimilarity<>, + typename TransformRule = LLETransform<>, + typename EigenmapRule = LLEEigenmap<>, + typename MatType = arma::mat, + typename VecType = arma::colvec> +class LLEExtendedKernel +{ + public: + /** + * Create a LLEExtendedKernel object and set the parameters. + * @param data Original data set + * @param similarity Adopted similarity rule + * @param transform Adopted transform rule + * @param eigenmap Adopted eigenmap rule + */ + LLEExtendedKernel(const MatType& data, + SimilarityRule& similarity, + TransformRule& transform, + EigenmapRule& eigenmap) : + data(data), similarity(similarity), transform(transform), + eigenmap(eigenmap), searcher(data) + { + /* nothing to do */ + } + + /** + * Fit the new data point + * @param point new data point + */ + void FitPoint(const VecType& point) + { + similarity.BuildSimilarityVec(searcher, point, neighbors, distances); + + // calculate the wieght for reconstruction + arma::mat neighborData; + for (size_t i = 0; i < neighbors.size(); ++i) + { + neighborData.insert_cols(neighborData.n_cols, data.unsafe_col(neighbors[i])); + } + + for (size_t i = 0; i < neighborData.n_cols; ++i) + neighborData.col(i) -= point; + + MatType cov = neighborData.t() * neighborData; + + if (neighbors.size() > data.n_rows) + cov += arma::eye(neighbors.size(), neighbors.size()) * + similarity.Tol() * arma::trace(cov); + + arma::colvec onesVec(neighbors.size()); + onesVec.ones(); + arma::colvec w = arma::solve(cov, onesVec); + w /= arma::accu(w); + weight.zeros(data.n_cols, 1); + for (size_t i = 0; i < neighbors.size(); ++i) + weight(neighbors[i]) = w(i); + } + + /** + * Evaluate the value for the new data point and a given point + * @param target the index of the given point + */ + double Evaluate(size_t target) + { + return (transform.Mu() - 1) * weight(target); + } + + private: + + //! Locally-stored original data set + const MatType& data; + + //! Locally-stored similarity rule + SimilarityRule& similarity; + + //! Locally-stored transform rule + TransformRule& transform; + + //! Locally-stored eigenmap rule + EigenmapRule& eigenmap; + + //! Locally-stored searcher over the original data set + typename std::remove_reference:: + type::SearcherType searcher; + + //! Locally-stored neighbors of the new point + std::vector neighbors; + + //! Locally-stored distances between the new point and its neighbors + std::vector distances; + + //! Precalculated weight for reconstruction + arma::colvec weight; +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/extended_kernel/mds_extended_kernel.hpp b/src/mlpack/methods/manifold_learning/extended_kernel/mds_extended_kernel.hpp new file mode 100644 index 00000000000..13d25239f46 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/extended_kernel/mds_extended_kernel.hpp @@ -0,0 +1,133 @@ +/** + * @file mds_extended_kernel.hpp + * @author Shangtong Zhang + * + * This is a kernel used to calculate the embedding vector for a new data point + * for MDS + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_EXTENDED_KERNEL_MDS_EXTENDED_KERNEL +#define __MLPACK_METHODS_MANIFOLD_LEARNING_EXTENDED_KERNEL_MDS_EXTENDED_KERNEL + +#include +#include + +namespace mlpack { +namespace manifold { + +/** + * This is a kernel used to calculate the embedding vector for a new data point + * for MDS. + * @tparam SimilarityRule How to build similarity (affinity) matrix given a dataset + * @tparam TransformRule How to transform similarity matrix + * @tparam EigenmapRule How to generate embedding vectors from transformed + * similarity matrix + */ +template< + typename SimilarityRule = MDSSimilarity<>, + typename TransformRule = MDSTransform<>, + typename EigenmapRule = MDSEigenmap<>, + typename MatType = arma::mat, + typename VecType = arma::colvec> +class MDSExtendedKernel +{ + public: + /** + * Create a MDSExtendedKernel object and set the parameters. + * @param data Original data set + * @param similarity Adopted similarity rule + * @param transform Adopted transform rule + * @param eigenmap Adopted eigenmap rule + */ + MDSExtendedKernel(const MatType& data, + SimilarityRule& similarity, + TransformRule& transform, + EigenmapRule& eigenmap) : + data(data), similarity(similarity), transform(transform), + eigenmap(eigenmap), similarityMat(similarity.SimilarityMat()) + { + /* nothing to do */ + } + + /** + * Fit the new data point + * @param point new data point + */ + void FitPoint(const VecType& point) + { + distances.zeros(data.n_cols); + + // precalculate the expectation over the whole original data set + entireE = 0; + for (size_t i = 0; i < data.n_cols; ++i) + for (size_t j = i; j < data.n_cols; ++j) + entireE += std::pow(similarityMat(i, j), 2); + entireE /= data.n_cols * (data.n_cols + 1) / 2; + + // precalculate the expectation for the new point + pointE = 0; + for (size_t i = 0; i < data.n_cols; ++i) + { + distances(i) = metric::SquaredEuclideanDistance::Evaluate( + data.unsafe_col(i), point); + pointE += distances(i); + } + pointE /= data.n_cols; + + } + + /** + * Evaluate the value for the new data point and a given point + * @param target the index of the given point + */ + double Evaluate(size_t target) + { + return -0.5 * (distances(target) - pointE - Expectation(target) + entireE); + + } + + private: + + /** + * calculate the expectation for a point + * @param fixed the index of the given point + */ + double Expectation(size_t fixed) + { + double E = 0; + + for (size_t i = 0; i < data.n_cols; ++i) + E += std::pow(similarityMat(fixed, i), 2); + + return E / data.n_cols; + } + + //! Locally-stored original data set + const MatType& data; + + //! Locally-stored similarity rule + SimilarityRule& similarity; + + //! Locally-stored transform rule + TransformRule& transform; + + //! Locally-stored eigenmap rule + EigenmapRule& eigenmap; + + //! Locally-stored similarity matrix + const MatType& similarityMat; + + //! Expectation over the whole data set + double entireE; + + //! Expectation for the new point + double pointE; + + //! Distances between the new point and original data points + arma::colvec distances; + +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/manifold_learning.hpp b/src/mlpack/methods/manifold_learning/manifold_learning.hpp new file mode 100644 index 00000000000..09a6ede99d5 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/manifold_learning.hpp @@ -0,0 +1,265 @@ +/** + * @file manifold_learning.hpp + * @author Shangtong Zhang + * + * Manifold Learning + * + * This class implements a framework for some manifold learning + * algorithms including Locally Linear Embedding(LLE), + * Isometirc Feature Mapping(Isomap), Multidimensional Scaling(MDS) + * and Laplacian Eigenmaps(LE). + * + * This implementation is based on + * Bengio, Yoshua, et al. "Out-of-sample extensions for lle, isomap, mds, eigenmaps, + * and spectral clustering." + * Advances in neural information processing systems 16 (2004): 177-184. + * + */ + +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_MANIFOLD_HPP +#define __MLPACK_METHODS_MANIFOLD_LEARNING_MANIFOLD_HPP + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace mlpack { +namespace manifold /** Manifold Learning. */ { + +/** + * @tparam SimlarityRule How to build similarity (affinity) matrix given a dataset + * @tparam TransformRule How to transform similarity matrix + * @tparam EigenmapRule How to generate embedding vectors from transformed + * similarity matrix + * @tparam ExtendedKernelType This kernel is used to generate the embedding vector + * for a new point without recalculating the eigen value of the whole dataset + */ +template< + typename SimilarityRule, + typename TransformRule, + typename EigenmapRule, + typename ExtendedKernelType, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat, + typename VecType = arma::colvec> +class Manifold +{ + public: + /** + * Create a Manifold Learning object and set the parameters. + * @param data The given dataset to perform manifold learning + * @param dim The dimension of disired embedding vectors + * @param similarity How to build similarity (affinity) matrix given a dataset + * @param transform How to transform similarity matrix + * @param eigenmap How to generate embedding vectors from transformed + * similarity matrix + */ + Manifold(const MatType& data, + size_t dim, + const SimilarityRule& similarity = SimilarityRule(), + const TransformRule& transform = TransformRule(), + const EigenmapRule& eigenmap = EigenmapRule()) : + data(data), similarity(similarity), transform(transform), + eigenmap(eigenmap), dim(dim), + extendedKernel(data, this->similarity, this->transform, this->eigenmap) + { + /* Nothing to do */ + } + + /** + * Transform given data set into embedding vectors. + * @param embeddingMat store embedding vectors + * each column represent an embedding vector. + */ + void Transform(MatType& embeddingMat) + { + eigenmap.Eigenmap( + transform.Transform( + similarity.BuildSimilarityMat(data)), embeddingMat, dim); + } + + /** + * Transform a new point into its embedding form. + * @param newPoint new point to be transformed. + * @param embeddingVec store the embedding vector. + */ + void Transform(const VecType& newPoint, VecType& embeddingVec) + { + extendedKernel.FitPoint(newPoint); + embeddingVec.set_size(dim, 1); + for (size_t i = 0; i < dim; ++i) + { + embeddingVec(i) = 0; + + for (size_t j = 0; j < data.n_cols; ++j) + embeddingVec(i) += eigenmap.Eigvec()(j, i) * extendedKernel.Evaluate(j); + + embeddingVec(i) /= eigenmap.Eigval()(i); + } + } + + //! Get dataset + MatType& Data() const { return data; } + + //! Get similarity rule + SimilarityRule& Similarity() const { return similarity; } + //! Modify similarity rule + SimilarityRule& Similarity() { return similarity; } + + //! Get transform rule + TransformRule& Transform() const { return transform; } + //! Modify transform rule + TransformRule& Transform() { return transform; } + + //! Get eigenmap rule + EigenmapRule& Eigenmap() const { return eigenmap; } + //! Modify eigenmap rule + EigenmapRule& Eigenmap() { return eigenmap; } + + //! Get extended kernel + ExtendedKernelType& ExtendedKernel() const { return extendedKernel; } + //! Modify extended kernel + ExtendedKernelType& ExtendedKernel() { return extendedKernel; } + + //! Get dimension of embedding vectors + size_t Dim() const { return dim; } + //! Modify dimension of embedding vectors + size_t& Dim() { return dim; } + + private: + //! Locally-stored dataset + const MatType& data; + + //! Instantiated similarity rule + SimilarityRule similarity; + + //! Instantiated transform rule + TransformRule transform; + + //! Instantiated eigenmap rule + EigenmapRule eigenmap; + + //! Locally-stored dimension of embedding vectors + size_t dim; + + //! Instantiated extended kernel + ExtendedKernelType extendedKernel; + +}; + +// typedef for convenience + +// define MDS +template< + typename MatType = arma::mat, + typename VecType = arma::colvec> +using MDS = Manifold< + MDSSimilarity, + MDSTransform, + MDSEigenmap, + MDSExtendedKernel< + MDSSimilarity, + MDSTransform, + MDSEigenmap, + MatType, + VecType>, + arma::mat>; + +/** + * define Isomap + * @tparam IsomapSimilarityRule How to build similarity matrix. + * Use k = 50 neighbors of each point by default. + * @tparam ShortestPath How to calculate shortest path within a graph + * @tparam MetricType metric to calculate distances + */ +template< + typename IsomapSimilarityRule = KIsomapSimilarity<>, + typename ShortestPath = FloydWarshall<>, + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat, + typename VecType = arma::colvec> +using Isomap = Manifold< + IsomapSimilarityRule, + IsomapTransform, + IsomapEigenmap, + IsomapExtendedKernel< + IsomapSimilarityRule, + IsomapTransform, + IsomapEigenmap, + MatType, + VecType>, + SimilarityMatType >; + +/** + * define LLE + * @tparam LLESimilarityRule How to build similarity matrix. + * Use k = 50 neighbors of each point by default. + * @tparam MetricType metric to calculate distances + */ + +template< + typename LLESimilarityRule = KLLESimilarity<>, + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat, + typename VecType = arma::colvec> +using LLE = Manifold< + LLESimilarityRule, + LLETransform, + LLEEigenmap, + LLEExtendedKernel< + LLESimilarityRule, + LLETransform, + LLEEigenmap, + MatType, + VecType>, + SimilarityMatType >; + +/** + * define LE + * @tparam LESimilarityRule How to build similarity matrix. + * Use k = 50 neighbors of each point by default. + * @tparam LETransformRule How to transform similarity matrix. + * Use nearest neighbor kernel by default. + * @tparam MetricType metric to calculate distances + */ +template< + typename LESimilarityRule = KLESimilarity<>, + typename LETransformRule = LETransform<>, + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat, + typename VecType = arma::colvec> +using LE = Manifold< + LESimilarityRule, + LETransformRule, + LEEigenmap, + LEExtendedKernel< + LESimilarityRule, + LETransformRule, + LEEigenmap, + MatType, + VecType>, + SimilarityMatType >; + +} // namespace manifold +} // namespace mlpack + + +#endif // __MLPACK_METHODS_MANIFOLD_LEARNING_MANIFOLD_LEARNING_HPP \ No newline at end of file diff --git a/src/mlpack/methods/manifold_learning/shortest_path/dijkstra.hpp b/src/mlpack/methods/manifold_learning/shortest_path/dijkstra.hpp new file mode 100644 index 00000000000..ffed90180a1 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/shortest_path/dijkstra.hpp @@ -0,0 +1,125 @@ +/** + * @file dijkstra.hpp + * @author Shangtong Zhang + * + * Implementation for dijkstra algorithm + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_SHORTEST_PATH_DIJKSTRA +#define __MLPACK_METHODS_MANIFOLD_LEARNING_SHORTEST_PATH_DIJKSTRA + +#include + +#include "queue" + +namespace mlpack { +namespace manifold { + +/** + * store vertext and its priority + */ +class Vex +{ + public: + size_t vex; + double priority; + Vex(size_t vex, double priority) + : vex(vex), priority(priority) + { + /* nothing to do */ + } +}; + +/** + * comparer for two Vex class + */ +class VexComp +{ + public: + bool operator()(const Vex& v1, const Vex& v2) + { + return v1.priority > v2.priority; + } +}; + +/** + * Implementation for dijkstra algorithm + */ +template< + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +class Dijkstra +{ + public: + + /** + * Solve multiple source shortest path problem + * @param neighborMat The neighbor matrix for the graph, it can be asymmetrical. + * neighborMat(i, j) = 0 (i != j) means vertex i and vertex j aren't connected. + * @param distMat distMat(i, j) is the shortest distance from i to j. + */ + static void Solve(const SimilarityMatType& neighborMat, MatType& distMat) + { + size_t nVex = neighborMat.n_cols; + distMat.zeros(nVex, nVex); + + // perform dijkstra algorithm for each vertex + for (size_t vex = 0; vex < nVex; ++vex) + { + // priority queue to store visited vertex + std::priority_queue, VexComp> q; + + // initialize distance vector for current vertex + for (size_t i = 0; i < nVex; ++i) + { + if (i != vex) + { + distMat(vex, i) = arma::datum::inf; + q.push(Vex(i, arma::datum::inf)); + } + else + { + distMat(vex, i) = 0; + q.push(Vex(i, 0)); + } + } + + // entries to trace if a vertex has been visited + arma::Col visited(nVex); + visited.zeros(); + + // perform nVex epoch + for (size_t epoch = 0; epoch < nVex; ++epoch) + { + // get the vertex u with shortest path to current vertex + size_t u = q.top().vex; + while (visited(u) == 1) + { + q.pop(); + u = q.top().vex; + } + q.pop(); + + // update distance of u's neighbors + for (size_t v = 0; v < nVex; ++v) + { + if (neighborMat(u, v) != 0 && visited(v) == 0) + { + double newD = distMat(vex, u) + neighborMat(u, v); + if (distMat(vex, v) > newD) + { + distMat(vex, v) = newD; + q.push(Vex(v, newD)); + } + } + } + + visited(u) = 1; + } + } + } +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif \ No newline at end of file diff --git a/src/mlpack/methods/manifold_learning/shortest_path/floyd_ warshall.hpp b/src/mlpack/methods/manifold_learning/shortest_path/floyd_ warshall.hpp new file mode 100644 index 00000000000..ffd64686444 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/shortest_path/floyd_ warshall.hpp @@ -0,0 +1,65 @@ +/** + * @file floyd_warshall.hpp + * @author Shangtong Zhang + * + * Implementation for floyd warshall algorithm + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_SHORTEST_PATH_FLOYD_WARSHALL +#define __MLPACK_METHODS_MANIFOLD_LEARNING_SHORTEST_PATH_FLOYD_WARSHALL + +#include +#include + +namespace mlpack { +namespace manifold { + +/** + * Implementation for floyd warshall algorithm + */ +template< + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +class FloydWarshall +{ + public: + + /** + * Solve multiple source shortest path problem + * @param neighborMat The neighbor matrix for the graph, it can be asymmetrical. + * neighborMat(i, j) = 0 (i != j) means vertex i and vertex j aren't connected. + * @param distMat distMat(i, j) is the shortest distance from i to j. + */ + static void Solve(const SimilarityMatType& neighborMat, MatType& distMat) + { + size_t nVex = neighborMat.n_cols; + distMat.zeros(nVex, nVex); + for (size_t i = 0; i < nVex; ++i) + { + for (size_t j = 0; j < nVex; ++j) + { + if (i != j && neighborMat(i, j) == 0) + distMat(i, j) = arma::datum::inf; + else + distMat(i, j) = neighborMat(i, j); + } + } + + for (size_t k = 0; k < nVex; ++k) + { + for (size_t i = 0; i < nVex; ++i) + { + for (size_t j = 0; j < nVex; ++j) + { + double newD = distMat(i, k) + distMat(k, j); + if (newD < distMat(i ,j)) + distMat(i, j) = newD; + } + } + } + } +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif \ No newline at end of file diff --git a/src/mlpack/methods/manifold_learning/similarity_rule/epsilon_similarity.hpp b/src/mlpack/methods/manifold_learning/similarity_rule/epsilon_similarity.hpp new file mode 100644 index 00000000000..069496eb14d --- /dev/null +++ b/src/mlpack/methods/manifold_learning/similarity_rule/epsilon_similarity.hpp @@ -0,0 +1,234 @@ +/** + * @file epsilon_similarity.hpp + * @author Shangtong Zhang + * + * Implementation for building similarity matrix on given dataset + * using neighbors of each point, distance between a neighbor and + * the point should be less than a given epsilon + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_SIMILARITY_RULE_EPSILON_SIMILARITY +#define __MLPACK_METHODS_MANIFOLD_LEARNING_SIMILARITY_RULE_EPSILON_SIMILARITY + +#include +#include +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for building a similarity matrix on given dataset + * using neighbors of each point, distance between a neighbor and + * the point should be less than a given epsilon + * + * Let M the similarity matrix. + * M(i, j) = 0 if point j doesn't appear in the neighbors of point i + * otherwise M(i, j) will be the distance between point i and point j + * + * @tparam UseLocalWeight If true, this class will try to reconstruct point i from + * its neighbors. Thus M(i, j) will be the weight for point j when + * reconstructing point i. This is used for LLE. + * @tparam MetricType metric to calculate distances + */ +template< + bool UseLocalWeight = false, + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +class EpsilonSimilarity +{ + public: + + /** + * Create an EpsilonSimilarity object and set the parameters. + * @param epsilon Point i is the neighbor of point j if and only if the distance + * between point i and point j is less than epsilon. + * @param tol regularizer used for calculate local weight for LLE + */ + EpsilonSimilarity(double epsilon = arma::datum::inf, double tol = 1e-3) : + epsilon(epsilon), tol(tol) + { + assert(epsilon > 0); + } + + // searcher type of this similarity rule + typedef range::RangeSearch SearcherType; + + /** + * Given a new point, calculate its similarity vector. + * @param searcher In Searcher containing original dataset. + * @param data In New point + * @param neighbors Out store neighbors of the new point + * @param distances Out store distances between the new point and its neighbors + */ + void BuildSimilarityVec(SearcherType& searcher, + const arma::colvec& data, + std::vector& neighbors, + std::vector& distances) + { + math::Range validRadius(0, epsilon); + std::vector > allDistances; + std::vector > allNeighbors; + searcher.Search(data, validRadius, allNeighbors, allDistances); + neighbors.clear(); + neighbors = allNeighbors[0]; + distances.clear(); + distances = allDistances[0]; + } + + /** + * Build similarity matrix on given dataset without calculating + * weight for reconstruction. The similarity matrix is stored in this object. + * + * @param data The dataset + * @return The reference of similarity matrix(M) + * M(i, j) = 0 if point j doesn't appear in the neighbors of point i + * otherwise M(i, j) will be the distance between point i and point j + */ + template + typename std::enable_if::type + BuildSimilarityMat(const MatType& data) + { + assert(data.n_cols); + similarityMat.zeros(data.n_cols, data.n_cols); + + if (epsilon == arma::datum::inf) + { + // just calculate the distance of each pair + for (size_t i = 0; i < similarityMat.n_rows; ++i) + for (size_t j = i + 1; j < similarityMat.n_cols; ++j) + similarityMat(i, j) = similarityMat(j, i) = + MetricType::Evaluate(data.unsafe_col(i), data.unsafe_col(j)); + } + else + { + range::RangeSearch rangeSearcher(data); + math::Range validRadius(0, epsilon); + std::vector > neighbors; + std::vector > distances; + rangeSearcher.Search(validRadius, neighbors, distances); + + for (size_t i = 0; i < neighbors.size(); ++i) + for (size_t j = 0; j < neighbors[i].size(); ++j) + similarityMat(i, neighbors[i][j]) = distances[i][j]; + } + return similarityMat; + } + + /** + * Build similarity matrix on given dataset while calculating + * weight for reconstruction. The similarity matrix is stored in this object. + * + * @param data The dataset + * @return The reference of similarity matrix(M) + * M(i, j) = 0 if point j doesn't appear in the neighbors of point i. + * Otherwise M(i, j) will be the weight for point j when reconstructing point i. + */ + template + typename std::enable_if::type + BuildSimilarityMat(const MatType& data) + { + assert(data.n_cols); + + similarityMat.zeros(data.n_cols, data.n_cols); + + range::RangeSearch rangeSearcher(data); + math::Range validRadius(0, epsilon); + std::vector > neighbors; + std::vector > distances; + rangeSearcher.Search(validRadius, neighbors, distances); + + // calculate weight for reconstruction + for (size_t i = 0; i < data.n_cols; ++i) + { + size_t nNeighbors = neighbors[i].size(); + + // retrieve neighbors of point i and shift i's neighbors to origin + MatType neighborData(data.n_rows, nNeighbors); + for (size_t j = 0; j < nNeighbors; ++j) + neighborData.col(j) = data.unsafe_col(neighbors[i][j]) - data.unsafe_col(i); + + // calculate the local covariance + MatType cov = neighborData.t() * neighborData; + + // if regularization is needed + if (nNeighbors > data.n_rows) + cov += arma::eye(nNeighbors, nNeighbors) * tol * arma::trace(cov); + + // solve cov * weight = 1 + arma::colvec onesVec(nNeighbors); + onesVec.ones(); + arma::colvec w = arma::solve(cov, onesVec); + + // enforce sum(w)=1 + w /= arma::accu(w); + + // store the weight + for (size_t j = 0; j < nNeighbors; ++j) + similarityMat(i, neighbors[i][j]) = w(j); + } + return similarityMat; + } + + //! Get epsilon + double Epsilon() const { return epsilon; } + //! Modify epsilon + double& Epsilon() { return epsilon; } + + //! Get similarity matrix + SimilarityMatType& SimilarityMat() const { return similarityMat; } + //! Modify simiarity matrix + SimilarityMatType& SimilarityMat() { return similarityMat; } + + //! Get tolerance for regularization + double& Tol() { return tol; } + //! Modify tolerance for regularization + double Tol() const { return tol; } + + private: + //! Locally-stored epsilon + double epsilon; + + //! Locally-stored similarity matrix + SimilarityMatType similarityMat; + + //! Locally-stored tolerance for regularization + double tol; +}; + +// typedef for convenience + +// define similarity rule for MDS +template< + typename MatType = arma::mat> +using MDSSimilarity = + EpsilonSimilarity; + +// define similarity rule for Isomap +template< + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +using EpsilonIsomapSimilarity = + EpsilonSimilarity; + +// define similarity rule for LE +template< + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +using EpsilonLESimilarity = + EpsilonSimilarity; + +// define similarity rule for LLE +template< + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +using EpsilonLLESimilarity = + EpsilonSimilarity; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/similarity_rule/k_similarity.hpp b/src/mlpack/methods/manifold_learning/similarity_rule/k_similarity.hpp new file mode 100644 index 00000000000..d634171a711 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/similarity_rule/k_similarity.hpp @@ -0,0 +1,223 @@ +/** + * @file k_similarity.hpp + * @author Shangtong Zhang + * + * Implementation for building similarity matrix on given dataset + * using k nearest neighbors of each point. + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_SIMILARITY_RULE_K_SIMILARITY +#define __MLPACK_METHODS_MANIFOLD_LEARNING_SIMILARITY_RULE_K_SIMILARITY + +#include +#include +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for building a similarity matrix on + * given dataset using k nearest neighbors of each point. + * + * Let M the similarity matrix. + * M(i, j) = 0 if point j doesn't appear in the k nearest neighbors of point i + * otherwise M(i, j) will be the distance between point i and point j + * + * @tparam UseLocalWeight If true, this class will try to reconstruct point i from + * its k nearest neighbors. Thus M(i, j) will be the weight for point j when + * reconstructing point i. This is used for LLE. + * @tparam MetricType metric to calculate distances + */ + +template< + bool UseLocalWeight = false, + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +class KSimilarity +{ + public: + + /** + * Create a KSimilarity object and set the parameters. + * @param nNeighbors number of neighbors to use + * @param tol regularizer used for calculate local weight for LLE + */ + KSimilarity(size_t nNeighbors = 50, double tol = 1e-3) : + nNeighbors(nNeighbors), tol(tol) + { + /* nothing to do */ + } + + // searcher type of this similarity rule + typedef neighbor::NeighborSearch + SearcherType; + + /** + * Given a new point, calculate its similarity vector. + * @param searcher In Searcher containing original dataset. + * @param data In New point + * @param neighbors Out store neighbors of the new point + * @param distances Out store distances between the new point and its neighbors + */ + void BuildSimilarityVec(SearcherType& searcher, + const arma::colvec& data, + std::vector& neighbors, + std::vector& distances) + { + arma::Mat neighborsMat; + arma::mat distancesMat; + searcher.Search(data, nNeighbors, neighborsMat, distancesMat); + neighbors.clear(); + distances.clear(); + for (size_t i = 0; i < neighborsMat.n_rows; ++i) + { + neighbors.push_back(neighborsMat(i, 0)); + distances.push_back(distancesMat(i, 0)); + } + } + + /** + * Build similarity matrix on given dataset without calculating + * weight for reconstruction. The similarity matrix is stored in this object. + * + * @param data The dataset + * @return The reference of similarity matrix(M) + * M(i, j) = 0 if point j doesn't appear in the k nearest neighbors of point i + * otherwise M(i, j) will be the distance between point i and point j + */ + template + typename std::enable_if::type + BuildSimilarityMat(const MatType& data) + { + assert(data.n_cols); + + similarityMat.zeros(data.n_cols, data.n_cols); + neighbor::NeighborSearch neighborSearcher(data); + arma::Mat neighbors; + arma::mat distances; + neighborSearcher.Search(nNeighbors, neighbors, distances); + for (size_t i = 0; i < neighbors.n_cols; ++i) + { + for (size_t j = 0; j < nNeighbors; ++j) + { + similarityMat(i, neighbors(j, i)) = distances(j, i); + } + } + return similarityMat; + } + + /** + * Build similarity matrix on given dataset while calculating + * weight for reconstruction. The similarity matrix is stored in this object. + * + * @param data The dataset + * @return The reference of similarity matrix(M) + * M(i, j) = 0 if point j doesn't appear in the k nearest neighbors of point i. + * Otherwise M(i, j) will be the weight for point j when reconstructing point i. + */ + template + typename std::enable_if::type + BuildSimilarityMat(const MatType& data) + { + assert(data.n_cols); + + similarityMat.zeros(data.n_cols, data.n_cols); + neighbor::NeighborSearch neighborSearcher(data); + arma::Mat neighbors; + arma::mat distances; + neighborSearcher.Search(nNeighbors, neighbors, distances); + + arma::colvec onesVec(nNeighbors); + onesVec.ones(); + + // calculate weight for reconstruction + for (size_t i = 0; i < data.n_cols; ++i) + { + // retrieve neighbors of point i + MatType neighborData; + for (size_t index = 0; index < nNeighbors; ++index) + neighborData.insert_cols( + neighborData.n_cols, data.unsafe_col(neighbors(index, i))); + + // shift i's neighbors to origin + for (size_t j = 0; j < nNeighbors; ++j) + neighborData.col(j) -= data.unsafe_col(i); + + // calculate the local covariance + MatType cov = neighborData.t() * neighborData; + + // if regularization is needed + if (nNeighbors > data.n_rows) + cov += arma::eye(nNeighbors, nNeighbors) * tol * arma::trace(cov); + + // solve cov * weight = 1 + arma::colvec w = arma::solve(cov, onesVec); + + // enforce sum(w)=1 + w /= arma::accu(w); + + // store the weight + for (size_t j = 0; j < nNeighbors; ++j) + similarityMat(i, neighbors(j, i)) = w(j); + } + return similarityMat; + + } + + //! Get # of neighbors + size_t NNeighbors() const { return nNeighbors; } + //! Modify # of neighbors + size_t& NNeighbors() { return nNeighbors; } + + //! Get similarity matrix + SimilarityMatType& SimilarityMat() const { return similarityMat; } + //! Modify similarity matrix + SimilarityMatType& SimilarityMat() { return similarityMat; } + + //! Get tolerance for regularization + double& Tol() { return tol; } + //! Modify tolerance for regularization + double Tol() const { return tol; } + + private: + //! Locally-stored # of neighbors + size_t nNeighbors; + + //! Locally-stored similarity matrix + SimilarityMatType similarityMat; + + //! Locally-stored tolerance for regularization + double tol; +}; + +// typedef for convenience + +// define similarity rule for Isomap +template< + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +using KIsomapSimilarity = + KSimilarity; + +// define similarity rule for LE +template< + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +using KLESimilarity = + KSimilarity; + +// define similarity rule for LLE +template< + typename MetricType = metric::EuclideanDistance, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +using KLLESimilarity = + KSimilarity; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/transform_rule/isomap_transform.hpp b/src/mlpack/methods/manifold_learning/transform_rule/isomap_transform.hpp new file mode 100644 index 00000000000..a744a85fe6a --- /dev/null +++ b/src/mlpack/methods/manifold_learning/transform_rule/isomap_transform.hpp @@ -0,0 +1,67 @@ +/** + * @file isomap_transform.hpp + * @author Shangtong Zhang + * + * Implementation for transforming similarity matrix for Isomap + */ + +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_TRANSFORM_RULE_ISOMAP_TRANSFORM +#define __MLPACK_METHODS_MANIFOLD_LEARNING_TRANSFORM_RULE_ISOMAP_TRANSFORM + +#include + +#include +#include + +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for transforming similarity matrix for Isomap + * @tparam ShortestPath How to calculate shortest path within a graph + */ +template< + typename ShortestPath = Dijkstra<>, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +class IsomapTransform +{ + public: + + /** + * Transform similarity matrix + * @param M Similarity matrix to be transformed + * @return reference for transformed similarity matrix + */ + MatType& Transform(const SimilarityMatType& M) + { + ShortestPath::Solve(M, distMat); + MDSTransform<> mdsTransform; + gramM = mdsTransform.Transform(arma::square(distMat)); + return gramM; + } + + //! Get distances matrix + MatType& DistMat() const { return distMat; } + //! Modify distances matrix + MatType& DistMat() { return distMat; } + + //! Get transformed similarity matrix + MatType& GramM() const { return gramM; } + //! Modify transformed similarity matrix + MatType& GramM() { return gramM; } + + private: + //! Locally-stored distances matrix + MatType distMat; + + //! Locally-stored transformed similarity matrix + MatType gramM; +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/transform_rule/le_transform.hpp b/src/mlpack/methods/manifold_learning/transform_rule/le_transform.hpp new file mode 100644 index 00000000000..eb6d1d0d0e8 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/transform_rule/le_transform.hpp @@ -0,0 +1,92 @@ +/** + * @file le_transform.hpp + * @author Shangtong Zhang + * + * Implementation for transforming similarity matrix for LE + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_TRANSFORM_RULE_LE_TRANSFORM +#define __MLPACK_METHODS_MANIFOLD_LEARNING_TRANSFORM_RULE_LE_TRANSFORM + +#include +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for transforming similarity matrix for LE + * @tparam KernelType Kernel for applying transformation + */ +template< + typename KernelType = kernel::GaussianKernel, + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +class LETransform +{ + public: + + /** + * Create a LETransform object and set the parameters. + * @param useNNKernel If true, nearest neighbor kernel will be used, thus + * @tparam KernelType and @param kernel will be ignored. It just sets + * M(i, j) to 1 if point i and point j are neighbors or 0 if they aren't + * neighbors. + * @param kernel Kernel to apply tranformation + */ + LETransform(bool useNNKernel = true, + const KernelType& kernel = KernelType()) : + kernel(kernel), useNNKernel(useNNKernel) + { + /* nothing to do */ + } + + /** + * Transform similarity matrix + * @param M Similarity matrix to be transformed + * @return reference for transformed similarity matrix + */ + MatType& Transform(const SimilarityMatType& M) + { + gramM.set_size(M.n_rows, M.n_cols); + + for (size_t i = 0; i < M.n_rows; ++i) + for (size_t j = 0; j < M.n_cols; ++j) + { + if (M(i, j) == 0) + gramM(i, j) = 0; + else + gramM(i, j) = useNNKernel ? 1 : kernel.Evaluate(M(i, j)); + } + + return gramM; + } + + //! Get kernel + KernelType& Kernel() { return kernel; } + //! Modify kernel + KernelType& Kernel() const { return kernel; } + + //! Get transformed similarity matrix + MatType& GramM() const { return gramM; } + //! Modify transformed similarity matrix + MatType& GramM() { return gramM; } + + //! If use nearest neighbor kernel + bool NNKernel() const { return useNNKernel; } + + private: + //! Locally-stored kernel + KernelType kernel; + + //! If use nearest neighbor kernel + bool useNNKernel; + + //! Locally-stored transformed similarity matrix + MatType gramM; + +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/transform_rule/lle_transform.hpp b/src/mlpack/methods/manifold_learning/transform_rule/lle_transform.hpp new file mode 100644 index 00000000000..fa43cc11468 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/transform_rule/lle_transform.hpp @@ -0,0 +1,72 @@ +/** + * @file lle_transform.hpp + * @author Shangtong Zhang + * + * Implementation for transforming similarity matrix for LLE + */ + +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_TRANSFORM_RULE_LLE_TRANSFORM +#define __MLPACK_METHODS_MANIFOLD_LEARNING_TRANSFORM_RULE_LLE_TRANSFORM + +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for transforming similarity matrix for LLE + */ +template< + typename SimilarityMatType = arma::sp_mat, + typename MatType = arma::mat> +class LLETransform +{ + public: + + /** + * Create a LLETransform object and set the parameters. + * @param mu Parameter for transformation. + */ + LLETransform(double mu = 1e4): + mu(mu) + { + /* nothing to do */ + } + + /** + * Transform similarity matrix + * @param M Similarity matrix to be transformed + * @return reference for transformed similarity matrix + */ + MatType& Transform(const SimilarityMatType& M) + { + MatType identity = arma::eye(M.n_rows, M.n_cols); + MatType tmp = identity - M; + gramM = tmp.t() * tmp; + gramM = mu * identity - gramM; + return gramM; + } + + //! Get transformed similarity matrix + MatType& GramM() const { return gramM; } + //! Modify transformed similarity matrix + MatType& GramM() { return gramM; } + + //! Get mu + double Mu() const { return mu; } + //! Modify mu + double& Mu() { return mu; } + + private: + //! Locally-stored transformed similarity matrix + MatType gramM; + + //! Locally-stored mu + double mu; + +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/methods/manifold_learning/transform_rule/mds_transform.hpp b/src/mlpack/methods/manifold_learning/transform_rule/mds_transform.hpp new file mode 100644 index 00000000000..d2f52666d62 --- /dev/null +++ b/src/mlpack/methods/manifold_learning/transform_rule/mds_transform.hpp @@ -0,0 +1,56 @@ +/** + * @file mds_transform.hpp + * @author Shangtong Zhang + * + * Implementation for transforming similarity matrix for MDS + */ +#ifndef __MLPACK_METHODS_MANIFOLD_LEARNING_TRANSFORM_RULE_MDS_TRANSFORM +#define __MLPACK_METHODS_MANIFOLD_LEARNING_TRANSFORM_RULE_MDS_TRANSFORM + +#include + +namespace mlpack { +namespace manifold { + +/** + * This class is an implementation for transforming similarity matrix for MDS + */ +template< + typename MatType = arma::mat> +class MDSTransform +{ + public: + + /** + * Transform similarity matrix + * @param M Similarity matrix to be transformed + * @return reference for transformed similarity matrix + */ + MatType& Transform(const MatType& M) + { + size_t nData = M.n_rows; + arma::rowvec S = arma::sum(M); + gramM.zeros(nData, nData); + double sumS = sum(S) / nData / nData; + + for (size_t i = 0; i < nData; ++i) + for (size_t j = 0; j < nData; ++j) + gramM(i, j) = -0.5 * (M(i, j) - S(i) / nData - S(j) / nData + sumS); + return gramM; + } + + //! Get transformed similarity matrix + MatType& GramM() const { return gramM; } + //! Modify transformed similarity matrix + MatType& GramM() { return gramM; } + + private: + //! Locally-stored transformed similarity matrix + MatType gramM; + +}; + +}; // namespace manifold +}; // namespace mlpack + +#endif diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index cb9103dd771..d3497039c35 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(mlpack_test math_test.cpp matrix_completion_test.cpp mean_shift_test.cpp + manifold_learning_test.cpp metric_test.cpp nbc_test.cpp nca_test.cpp diff --git a/src/mlpack/tests/data/swissroll.dat b/src/mlpack/tests/data/swissroll.dat new file mode 100644 index 00000000000..dbf14e920d5 --- /dev/null +++ b/src/mlpack/tests/data/swissroll.dat @@ -0,0 +1,400 @@ + -1.4681146e+00 7.7696486e+00 7.9024511e+00 + -9.2953555e+00 7.9942871e+00 8.4721663e-01 + 2.6439870e+00 6.0168790e+00 -4.5253750e+00 + -4.0690189e+00 6.4797356e+00 7.3054108e+00 + 2.7529181e-01 7.0530050e+00 7.8139173e+00 + 6.1667612e+00 7.6096586e+00 -5.6194351e-01 + 5.0075635e+00 8.6287365e+00 4.9858229e+00 + 8.9068084e-02 7.2100370e+00 7.8421187e+00 + 9.1649358e-01 8.7615507e+00 -1.1040422e+01 + -6.8187650e+00 7.9754248e+00 -7.6789180e+00 + 6.0957396e+00 8.6741168e+00 -8.1599607e-01 + -4.6831020e+00 7.6269471e+00 -9.4367986e+00 + -2.9853396e+00 6.8431841e+00 7.6645301e+00 + 3.0123550e+00 6.0186009e+00 6.7995493e+00 + -2.8996942e+00 7.6554890e+00 7.6859466e+00 + 3.8685151e+00 8.3185514e+00 6.1848291e+00 + 3.3937501e+00 7.2074119e+00 6.5487180e+00 + -8.1521830e+00 6.9592136e+00 3.7890071e+00 + -7.7502034e+00 7.1913582e+00 4.3937732e+00 + -7.7929185e+00 6.4034067e+00 4.3343675e+00 + -2.5512004e+00 7.0069902e+00 7.7630369e+00 + 6.2922393e+00 7.3192606e+00 5.8694264e-02 + -2.9197541e+00 7.5458411e+00 7.6810186e+00 + -8.7370418e+00 7.4362169e+00 2.6505278e+00 + -1.0745321e+00 8.1113352e+00 7.9162999e+00 + -5.3712750e+00 7.6093177e+00 6.6325251e+00 + -2.9972263e+00 9.3140155e+00 7.6614798e+00 + 4.3976637e+00 7.8120238e+00 5.6965794e+00 + 4.6820884e-01 9.3044938e+00 7.7797952e+00 + 6.1029393e+00 6.7768785e+00 2.7954793e+00 + -4.2725189e+00 8.0265470e+00 7.2187786e+00 + 6.3374849e+00 7.2397491e+00 4.4272334e-01 + 6.3608772e+00 8.1001425e+00 9.4798153e-01 + 6.1431339e+00 8.0939308e+00 2.6504176e+00 + -7.1084186e-01 5.3139784e+00 -4.4999167e+00 + -7.9017291e+00 6.1729569e+00 4.1781957e+00 + 2.2526468e-01 6.0589864e+00 7.8219475e+00 + 6.0382789e+00 7.9018445e+00 3.0058576e+00 + -7.5413029e+00 8.9702013e+00 4.6702192e+00 + 5.0945044e+00 7.1731858e+00 -2.7481935e+00 + 3.2591674e+00 8.3123230e+00 6.6411328e+00 + 4.0710589e+00 8.0455401e+00 6.0094151e+00 + 2.7185729e-01 6.4483677e+00 7.8144794e+00 + 3.2119793e-01 7.8974670e+00 7.8062533e+00 + 6.2284371e+00 6.7481053e+00 2.2872230e+00 + 2.7987110e+00 9.0162669e+00 6.9258465e+00 + 3.6374210e+00 7.4674335e+00 6.3697071e+00 + -2.1970839e+00 9.1359997e+00 7.8251165e+00 + -5.7897903e+00 7.0749415e+00 6.3500034e+00 + -5.9016659e+00 8.0894334e+00 6.2681677e+00 + 6.2267772e+00 7.4372088e+00 2.2952886e+00 + 2.0694386e+00 5.4780411e+00 7.2892932e+00 + 6.2858601e+00 6.5178685e+00 1.6957087e-02 + 6.3524455e+00 8.1125113e+00 6.5864131e-01 + 2.6454704e+00 7.4451139e+00 7.0106200e+00 + -8.3469671e+00 6.3812680e+00 3.4520357e+00 + 6.0686320e+00 6.8736215e+00 2.9101722e+00 + -1.3693288e-01 7.7495177e+00 7.8701877e+00 + 3.9839129e+00 6.5069810e+00 6.0865064e+00 + -5.9577253e+00 8.4749502e+00 6.2261012e+00 + 6.3586931e+00 6.8592905e+00 8.1677370e-01 + 2.3796713e+00 9.3088626e+00 7.1467885e+00 + -1.5883086e+00 6.4201337e+00 7.8943313e+00 + -5.8412598e+00 7.6991894e+00 6.3126999e+00 + -8.3971394e+00 5.9789734e+00 3.3594372e+00 + 2.0091495e+00 6.7763689e+00 7.3150304e+00 + 5.7830108e+00 6.9067497e+00 -1.6302677e+00 + 6.0110766e+00 7.9013363e+00 3.0876273e+00 + 6.3609998e+00 8.4421333e+00 9.9544553e-01 + -8.9713871e+00 7.8004860e+00 -4.0676431e+00 + 5.6772203e+00 7.1269293e+00 3.8941118e+00 + -3.1670376e+00 8.3154885e+00 7.6158153e+00 + 3.7971281e+00 8.2988870e+00 6.2436020e+00 + -4.2741788e+00 7.6202053e+00 7.2180455e+00 + 6.0588774e+00 8.0712476e+00 2.9414726e+00 + 5.9931707e+00 7.9127960e+00 -1.1243792e+00 + 5.9496693e+00 6.5130381e+00 -1.2405440e+00 + -1.0688979e+00 8.2595683e+00 7.9163564e+00 + 3.7103998e+00 6.8427987e+00 6.3129841e+00 + 3.8178983e+00 6.8960815e+00 6.2266594e+00 + -7.8039330e+00 7.6769468e+00 4.3188798e+00 + 4.8585810e-01 7.1924965e+00 7.7764213e+00 + 1.1972909e+00 7.3681796e+00 7.6041298e+00 + -8.5762870e+00 8.0953577e+00 3.0055908e+00 + 6.1343282e+00 8.5468328e+00 2.6833176e+00 + -2.7538807e+00 7.3020414e+00 7.7201550e+00 + -3.8571486e+00 7.8276782e+00 7.3889174e+00 + 4.0834021e+00 7.2616985e+00 5.9982912e+00 + 1.0637670e+00 7.7295969e+00 7.6419870e+00 + 6.3259297e+00 7.9399979e+00 3.2271698e-01 + 6.3369926e+00 6.8831341e+00 4.3706851e-01 + 1.8748947e+00 7.7748368e+00 7.3701350e+00 + -2.9600460e+00 8.1011020e+00 7.6709575e+00 + -7.9630470e+00 7.5923080e+00 -6.1892662e+00 + 5.8256646e+00 9.2298414e+00 3.5711364e+00 + 1.2751768e+00 6.8914426e+00 7.5808299e+00 + 3.1357396e+00 6.7629402e+00 6.7220924e+00 + 4.1991739e+00 5.7501207e+00 -3.6548833e+00 + 5.0304568e+00 8.4104826e+00 4.9550684e+00 + 4.7789552e+00 8.3670826e+00 -3.1164525e+00 + -3.8986339e+00 1.2420107e+01 7.3730942e+00 + 6.2578053e+00 1.3398476e+01 2.1349455e+00 + 1.9089152e+00 1.2683703e+01 7.3564566e+00 + 5.4419619e+00 1.2790790e+01 4.3317004e+00 + 3.9360512e-01 1.2612945e+01 7.7935879e+00 + 5.6295398e+00 1.2939952e+01 3.9892073e+00 + -1.0831595e+00 1.2601662e+01 7.9162056e+00 + -3.0972810e+00 1.5287335e+01 7.6350478e+00 + -9.0039234e+00 1.1333335e+01 1.9463402e+00 + 3.8068796e+00 1.0645701e+01 6.2356634e+00 + 3.2416169e+00 1.1359319e+01 -4.2707323e+00 + 6.1921178e+00 1.1406657e+01 2.4533151e+00 + -7.4538140e+00 1.2066391e+01 4.7795917e+00 + 6.3607063e+00 1.2331530e+01 9.2660853e-01 + -4.8259854e+00 1.2281466e+01 6.9496478e+00 + 1.7376060e+00 1.3041334e+01 7.4233995e+00 + -7.8931961e+00 1.2889266e+01 4.1907041e+00 + 4.0751587e+00 1.3251229e+01 -3.7516277e+00 + 3.8272723e+00 1.4278256e+01 6.2189708e+00 + 6.2919014e+00 1.3723063e+01 5.6434883e-02 + -5.7695448e+00 1.1216744e+01 -8.6625045e+00 + -3.7795581e+00 1.0171045e+01 7.4178305e+00 + -7.5889388e+00 1.3401931e+01 4.6091377e+00 + 6.3609358e+00 1.0664361e+01 1.0176138e+00 + 5.1534088e+00 1.2566757e+01 4.7835912e+00 + 4.2373089e+00 1.2535479e+01 5.8551184e+00 + -5.8259679e+00 1.4727168e+01 6.3238441e+00 + 4.2656884e+00 1.2430786e+01 5.8277814e+00 + -2.7934824e+00 1.1992677e+01 7.7111438e+00 + 3.6566928e+00 1.2735810e+01 -4.0387254e+00 + 4.6463398e+00 1.2745805e+01 5.4294555e+00 + 6.1668491e+00 1.2570045e+01 2.5582160e+00 + 5.5427577e+00 1.1891419e+01 -2.0878434e+00 + -1.2283041e+00 1.1277407e+01 7.9132122e+00 + 5.5980002e-01 1.2816500e+01 7.7618232e+00 + 2.3733686e+00 1.1157131e+01 7.1498560e+00 + 6.1242632e+00 1.1467816e+01 -7.1897731e-01 + -6.0276085e+00 1.3831216e+01 6.1726461e+00 + 2.9848064e-02 1.2081097e+01 7.8501227e+00 + 4.3753580e+00 1.2359678e+01 5.7192388e+00 + 2.4455091e+00 1.3399822e+01 7.1143064e+00 + 4.1818027e+00 1.2199889e+01 5.9077268e+00 + 4.9510528e+00 1.3529366e+01 -2.9235483e+00 + 4.3062501e+00 1.2154934e+01 5.7881812e+00 + 6.1793560e+00 1.3512802e+01 2.5072360e+00 + 6.3375834e+00 1.3129335e+01 1.5348571e+00 + 6.3320256e+00 1.2286985e+01 3.8300297e-01 + 5.4026175e+00 1.1634303e+01 4.3980735e+00 + 3.8855794e+00 1.1456892e+01 -3.8888670e+00 + -4.8506005e+00 1.2229931e+01 6.9364872e+00 + -1.3258447e+00 1.2061859e+01 7.9097092e+00 + 2.7328128e+00 1.2091326e+01 6.9628814e+00 + 2.8295793e+00 1.3483545e+01 6.9081925e+00 + 6.1230533e+00 1.2202303e+01 2.7244870e+00 + -5.2545457e+00 1.3643679e+01 6.7050539e+00 + 3.4487794e+00 1.1968380e+01 6.5096394e+00 + 5.9473626e+00 1.3472566e+01 3.2667230e+00 + -7.4357447e+00 1.1977750e+01 4.8017425e+00 + 3.9794061e+00 1.2676578e+01 6.0904256e+00 + 5.5932568e+00 1.3470738e+01 4.0591869e+00 + 4.3481187e+00 1.2086028e+01 5.7466382e+00 + 6.2045803e+00 1.2061729e+01 2.3985973e+00 + 6.3500746e+00 1.4503391e+01 6.1588615e-01 + -8.2678921e+00 1.3450993e+01 -5.6712106e+00 + -8.8255572e+00 1.2067996e+01 2.4357085e+00 + 3.6250212e-01 1.3148941e+01 7.7991152e+00 + 6.2378119e+00 1.2139924e+01 -2.5154291e-01 + 6.2292290e+00 1.3205885e+01 2.2833572e+00 + 3.7055146e+00 1.3915849e+01 6.3168279e+00 + -3.5123836e+00 1.0895484e+01 7.5107085e+00 + 6.1271202e+00 1.3528853e+01 -7.0891957e-01 + 2.2848128e+00 1.3957968e+01 -4.6378771e+00 + 5.8883965e+00 1.2547471e+01 -1.3929143e+00 + 1.6034704e-01 1.4246257e+01 7.8318696e+00 + -2.9484679e-01 1.2655388e+01 7.8858435e+00 + -7.7562622e-01 1.1262880e+01 7.9137608e+00 + 3.4310383e+00 1.0306506e+01 6.5223217e+00 + 1.3021599e+00 1.2166593e+01 7.5725463e+00 + 5.1837553e+00 1.3213543e+01 4.7395261e+00 + -4.0678368e+00 1.2817408e+01 7.3058955e+00 + 6.0739962e+00 1.2913610e+01 -8.8620846e-01 + -8.0247195e-01 1.1922914e+01 7.9144510e+00 + 6.2057261e+00 1.2644002e+01 2.3934557e+00 + 4.5546056e+00 1.0861334e+01 5.5312216e+00 + -1.5903827e+00 1.1739910e+01 7.8941752e+00 + -5.4031731e+00 1.1681207e+01 6.6122454e+00 + 6.3509990e+00 1.3019729e+01 6.3193999e-01 + -6.8986679e+00 1.2485840e+01 5.3997706e+00 + -2.4594743e+00 1.1344471e+01 7.7806762e+00 + 3.0430607e+00 1.2490475e+01 6.7805902e+00 + 3.8131490e+00 1.1810189e+01 6.2305448e+00 + 3.9395441e+00 1.1833301e+01 6.1248061e+00 + 4.3959683e+00 1.3364149e+01 5.6983087e+00 + 2.4444511e+00 1.2613419e+01 7.1148348e+00 + 2.2509657e+00 1.2898363e+01 7.2079914e+00 + -3.7862004e+00 1.3383970e+01 7.4153899e+00 + -8.3221051e+00 1.2680258e+01 3.4969807e+00 + -8.9781470e-01 1.3050855e+01 7.9161641e+00 + 3.8952960e+00 1.3182964e+01 6.1623817e+00 + -2.1767074e+00 1.3670609e+01 7.8281967e+00 + 1.2596728e+01 7.9758606e+00 1.4787486e+00 + 5.2422912e+00 8.9122327e+00 -1.0202187e+01 + 8.5395970e+00 7.5226085e+00 1.0390229e+01 + 1.2437878e+01 7.4521306e+00 3.0528713e+00 + 1.2605279e+01 9.2013347e+00 8.6866895e-01 + 1.1725898e+01 6.9902883e+00 5.6480040e+00 + 1.2519460e+01 7.4971450e+00 2.4731806e+00 + 6.1684875e+00 8.4198671e+00 -9.7749175e+00 + 1.2016390e+01 7.6498087e+00 -2.7971903e+00 + 1.2076259e+01 8.9049334e+00 -2.6045978e+00 + 9.8885282e+00 8.5341215e+00 -6.7416438e+00 + -6.3708181e-01 7.7915703e+00 1.4167787e+01 + 6.8361276e+00 6.7223015e+00 -9.4016502e+00 + 1.0242926e+01 8.0666961e+00 -6.2826083e+00 + 8.3937048e+00 6.1173788e+00 -8.2766271e+00 + 3.6723288e+00 7.7444747e+00 -1.0713891e+01 + 1.1898483e+01 8.3084388e+00 -3.1475516e+00 + 1.1524254e+01 7.7130417e+00 -4.0822706e+00 + 1.4996721e+00 8.3796772e+00 1.3949692e+01 + 1.1646681e+01 9.5388763e+00 -3.8001588e+00 + 4.8731493e+00 8.4239324e+00 -1.0345513e+01 + 4.7533248e-01 7.7669174e+00 1.4095445e+01 + 5.3799912e+00 8.1416615e+00 1.2637132e+01 + 1.1736757e+01 7.9254854e+00 -3.5791907e+00 + -1.9063217e-02 6.1852765e+00 -1.0993824e+01 + 1.0515379e+01 7.0835888e+00 -5.8956602e+00 + 1.2040276e+01 8.7246878e+00 -2.7216795e+00 + 1.2496194e+01 7.4564158e+00 2.6595197e+00 + 1.1588201e+01 8.0824233e+00 -3.9373481e+00 + 1.2034203e+01 6.4934999e+00 4.7655156e+00 + 1.2214856e+01 7.5645167e+00 4.1228913e+00 + 2.8239457e+00 8.1002919e+00 -1.0889103e+01 + 6.1016445e+00 6.1384850e+00 -9.8091560e+00 + 8.1292035e+00 7.8475926e+00 -8.4963308e+00 + 1.0069038e+01 7.3181568e+00 -6.5135938e+00 + 1.1278948e+01 6.5604652e+00 -4.5960530e+00 + 1.2494297e+01 7.4624668e+00 -6.7411523e-01 + -9.4607848e+00 5.6036955e+00 -4.3592709e-01 + 1.0430817e+01 5.3720232e+00 -6.0192656e+00 + 5.2846363e+00 6.3230767e+00 1.2685721e+01 + 4.8463401e+00 6.5094678e+00 -1.0355351e+01 + 8.6895264e+00 6.3269677e+00 1.0244861e+01 + 1.2335746e+01 5.7745722e+00 3.6000177e+00 + 1.2414315e+01 7.7882281e+00 -1.1876358e+00 + 1.2597101e+01 5.9058163e+00 1.4688947e+00 + -6.6258283e-01 7.6102188e+00 -1.0914852e+01 + 1.2274355e+01 8.2870667e+00 -1.8665148e+00 + 4.6845525e-01 7.4977732e+00 1.4096164e+01 + 1.2591907e+01 7.5931088e+00 4.0279073e-01 + 1.2537456e+01 7.1218429e+00 -3.1346408e-01 + 8.1927767e+00 6.0173239e+00 -8.4447135e+00 + 1.2410348e+01 7.4561814e+00 -1.2099030e+00 + 1.2557373e+01 8.4608252e+00 2.1035797e+00 + 1.2097688e+01 9.2382449e+00 4.5538382e+00 + 1.0976332e+01 7.0697938e+00 -5.1564434e+00 + 1.1705527e+01 5.8726773e+00 -3.6572105e+00 + -5.4750116e+00 7.6663475e+00 1.3452206e+01 + -7.0104861e+00 7.8762659e+00 -7.4662171e+00 + -8.2227583e+00 7.2730495e+00 1.2220590e+01 + 1.2368376e+01 6.3510877e+00 3.4389408e+00 + 8.0308052e+00 9.5243326e+00 1.0851627e+01 + -1.7235513e+00 5.1404765e+00 -1.0697883e+01 + 9.4349694e+00 6.9900280e+00 -7.2683305e+00 + 1.1504098e+01 6.1783744e+00 -4.1269164e+00 + 1.2110886e+01 6.8638718e+00 4.5081056e+00 + -1.7870982e+00 7.8178514e+00 -1.0681331e+01 + 1.1920912e+01 7.6380480e+00 5.1142111e+00 + 3.2446554e+00 6.7892649e+00 -1.0810737e+01 + 1.2566190e+01 8.2770035e+00 -2.2690258e-03 + 1.0958117e+01 8.1223939e+00 7.2735231e+00 + 1.2393666e+01 8.1473809e+00 3.3061320e+00 + 7.1518910e+00 7.0743683e+00 1.1547224e+01 + 7.9689234e+00 8.5485808e+00 1.0904604e+01 + 8.9294470e+00 8.1607071e+00 -7.7888359e+00 + 1.2525829e+01 1.0008772e+01 2.4178711e+00 + 6.1386560e+00 8.5634596e+00 -9.7902659e+00 + 2.0302262e+00 8.6569217e+00 -1.0992297e+01 + 8.7745644e+00 7.5529788e+00 1.0160363e+01 + 1.2472570e+01 6.2116140e+00 -8.2840528e-01 + 1.2353653e+01 7.1287788e+00 -1.5057598e+00 + 8.9038255e+00 6.7422081e+00 1.0028969e+01 + 1.1309910e+01 6.9360311e+00 6.5995493e+00 + 1.2336062e+01 8.0551386e+00 3.5985060e+00 + 5.1884363e+00 6.9432219e+00 1.2733795e+01 + 8.7272508e+00 6.6048869e+00 1.0207563e+01 + 1.2548732e+01 7.0906723e+00 2.1979290e+00 + 8.5865962e+00 7.3391132e+00 -8.1078934e+00 + 8.9239278e+00 7.9093344e+00 -7.7941769e+00 + 5.8946773e+00 6.5473640e+00 1.2358254e+01 + -1.1671932e+00 7.8173175e+00 -1.0825428e+01 + 1.2424108e+01 7.5780201e+00 -1.1316274e+00 + -4.5363164e+00 8.8243854e+00 -9.5262091e+00 + 7.8191859e+00 7.2868295e+00 1.1030149e+01 + 9.3537085e+00 7.3655214e+00 9.5416770e+00 + 1.2474593e+01 6.3286442e+00 -8.1460482e-01 + 1.2312351e+01 6.1147373e+00 -1.7000326e+00 + -8.3267153e+00 7.8105083e+00 -5.5624025e+00 + 1.1385726e+01 7.2505109e+00 6.4406757e+00 + -6.5453093e+00 8.0037441e+00 -7.9627146e+00 + -7.4077549e+00 6.6073386e+00 -6.9837111e+00 + 1.2578777e+01 1.4408512e+01 1.7061762e-01 + 6.0962678e+00 1.2622231e+01 -9.8118862e+00 + 1.2150163e+01 1.3547033e+01 4.3680814e+00 + 1.0795898e+01 1.2273080e+01 7.5552184e+00 + 9.3888458e+00 1.2337498e+01 9.5015048e+00 + 8.5795434e+00 1.3190052e+01 -8.1141948e+00 + 1.2011131e+01 1.3055757e+01 4.8393286e+00 + 1.2593238e+01 1.1379745e+01 4.3164771e-01 + 9.6619748e+00 1.0967307e+01 9.1777113e+00 + 1.1623885e+01 1.1402132e+01 5.9014148e+00 + 9.0268935e+00 1.1084227e+01 9.9004347e+00 + 1.2125663e+01 1.2559571e+01 -2.4366473e+00 + 1.2072143e+01 1.2088749e+01 -2.6182023e+00 + 7.9514131e+00 1.2131989e+01 1.0919477e+01 + -6.0225144e+00 1.1139037e+01 -8.4497246e+00 + 1.0092751e+01 1.3279567e+01 -6.4827715e+00 + 2.6019230e+00 1.2939411e+01 -1.0923806e+01 + 1.0916591e+01 1.2410378e+01 -5.2592576e+00 + 1.0981714e+01 1.3521180e+01 7.2311631e+00 + 9.6648722e+00 1.1626021e+01 9.1741621e+00 + 5.3999156e+00 1.2914700e+01 -1.0136441e+01 + 1.0333113e+01 1.2848441e+01 -6.1580788e+00 + 1.2605496e+01 1.2849254e+01 8.9211696e-01 + 1.1433133e+01 1.1770753e+01 -4.2804276e+00 + 1.2448616e+01 1.2826840e+01 2.9863902e+00 + 1.2188921e+01 1.1985118e+01 4.2235559e+00 + 6.2869857e+00 1.1603554e+01 -9.7128575e+00 + 1.1960525e+01 1.1296732e+01 -2.9675492e+00 + -6.0886515e+00 1.3537816e+01 -8.3917689e+00 + 6.4435845e+00 1.1654056e+01 1.2028138e+01 + 9.1139875e+00 1.2327086e+01 -7.6061632e+00 + 3.3444532e+00 1.1291348e+01 -1.0789701e+01 + 1.1623174e+01 1.2202873e+01 -3.8558645e+00 + 8.3817166e-01 9.2679622e+00 -1.1039580e+01 + 1.2431697e+01 1.1413041e+01 -1.0871537e+00 + 9.6681202e+00 1.1073564e+01 -7.0053674e+00 + -7.5540746e+00 1.1485549e+01 1.2584639e+01 + 6.5230822e+00 1.2286733e+01 1.1977359e+01 + -8.3763437e+00 1.2174652e+01 -5.4680111e+00 + 1.2042210e+01 1.4444398e+01 4.7395265e+00 + 1.1777724e+00 1.1928227e+01 -1.1039212e+01 + 1.1617832e+01 1.2249968e+01 -3.8684187e+00 + 1.2603597e+01 1.0930685e+01 1.2395477e+00 + 1.0289710e+01 1.2022617e+01 8.3441196e+00 + 1.1529390e+01 1.1162023e+01 -4.0708166e+00 + 8.5625034e-01 1.2530299e+01 1.4050233e+01 + 1.0263422e+01 1.3353087e+01 -6.2546018e+00 + 1.2392607e+01 1.2904253e+01 3.3118523e+00 + 1.0877474e+01 1.1799380e+01 7.4155771e+00 + 1.2582960e+01 1.0869457e+01 2.3681383e-01 + 9.1816405e+00 1.3960013e+01 9.7338264e+00 + 1.2402572e+01 1.4550043e+01 3.2574542e+00 + 7.7277162e+00 1.2620501e+01 -8.8058862e+00 + -3.1701589e+00 1.1510098e+01 -1.0213967e+01 + -3.1534491e+00 1.3697771e+01 1.4008032e+01 + 9.3172698e+00 1.1907344e+01 -7.3953152e+00 + 1.2594739e+01 1.2030191e+01 4.6608073e-01 + 1.1484013e+01 1.3386377e+01 6.2260570e+00 + 1.2599533e+01 1.1114780e+01 5.9547315e-01 + 6.5443080e+00 1.0543246e+01 -9.5719594e+00 + 1.0356605e+01 1.2920684e+01 -6.1250812e+00 + 1.2149455e+01 1.2900738e+01 -2.3524666e+00 + 2.2023749e+00 1.2595142e+01 1.3804377e+01 + 6.9845247e+00 1.2996684e+01 -9.3105868e+00 + 1.0002386e+01 1.3582241e+01 8.7426939e+00 + 1.2434612e+01 1.3470448e+01 3.0726604e+00 + 1.1717455e+01 1.1931430e+01 -3.6275950e+00 + 4.9510621e+00 1.3309972e+01 -1.0316485e+01 + 1.1473100e+01 1.2673247e+01 -4.1946611e+00 + 1.2268186e+01 1.1994457e+01 -1.8925823e+00 + 3.8633010e-01 1.1306694e+01 -1.1023838e+01 + 1.2591865e+01 1.3146971e+01 1.5926149e+00 + 7.3622069e+00 1.2146377e+01 -9.0645874e+00 + 1.2246094e+01 1.2546435e+01 -1.9839486e+00 + 1.2371707e+01 1.1707052e+01 3.4218759e+00 + 6.5450394e+00 1.0949486e+01 -9.5715468e+00 + 1.1451881e+01 1.2671586e+01 -4.2404129e+00 + 1.2336427e+01 1.2437861e+01 3.5967597e+00 + -3.4700984e+00 1.3699028e+01 -1.0083765e+01 + 7.6562874e+00 1.3301704e+01 1.1162612e+01 + -1.0573178e+01 1.3553305e+01 1.0533159e+01 + 8.4397803e+00 1.1751123e+01 1.0484540e+01 + 1.1305352e+01 1.1563674e+01 -4.5436129e+00 + 1.2089569e+01 1.1230913e+01 4.5816587e+00 + 5.1759538e+00 1.2997981e+01 -1.0229035e+01 + -3.4274109e+00 1.5289081e+01 1.3963376e+01 + 8.6223202e+00 1.3227572e+01 1.0310583e+01 + 9.9809540e+00 1.1726936e+01 8.7712094e+00 + 6.8346799e+00 1.3336634e+01 -9.4025236e+00 + 1.2409978e+01 1.1371670e+01 3.2161360e+00 + 9.6866237e+00 1.1075530e+01 -6.9838138e+00 + 1.1329013e+01 1.3217442e+01 -4.4960778e+00 + 9.5947187e+00 1.1722094e+01 -7.0898544e+00 + 5.2852614e+00 1.2815986e+01 -1.0184537e+01 + 6.5035268e+00 1.3906535e+01 -9.5948589e+00 + 1.1824063e+01 1.2901125e+01 -3.3523347e+00 + -2.0869471e+00 1.3429660e+01 -1.0597603e+01 + 1.1234205e+01 1.0894198e+01 6.7529120e+00 + 1.2172804e+01 1.3161536e+01 -2.2675471e+00 + 1.0643550e+01 1.4638502e+01 7.8059390e+00 diff --git a/src/mlpack/tests/manifold_learning_test.cpp b/src/mlpack/tests/manifold_learning_test.cpp new file mode 100644 index 00000000000..e3b5b77c2fe --- /dev/null +++ b/src/mlpack/tests/manifold_learning_test.cpp @@ -0,0 +1,264 @@ +/** + * @file manifold_learning_test.cpp + * @author Shangtong Zhang + */ + +#include +#include + +#include +#include "old_boost_test_definitions.hpp" + +using namespace mlpack; +using namespace manifold; + +BOOST_AUTO_TEST_SUITE(ManifoldLearningTest); + +// Test rules for generate similarity mat +BOOST_AUTO_TEST_CASE(SimilarityRuleTest) +{ + // Generate test data randomly + arma::mat data; + size_t nData = 6; + size_t nDim = 5; + data.randu(nDim, nData); + + // Generate correct answer by simplest approach + arma::mat kAnswer; + size_t nNeighbors = 4; + kAnswer.zeros(nData, nData); + for (size_t i = 0; i < nData; ++i) + { + arma::colvec dist(nData); + for (size_t j = 0; j < nData; ++j) + { + dist(j) = metric::EuclideanDistance::Evaluate( + data.unsafe_col(i), data.unsafe_col(j)); + } + arma::uvec ind = arma::sort_index(dist, "ascend"); + for (size_t j = 1; j < nNeighbors + 1; ++j) + kAnswer(i, ind(j)) = dist(ind(j)); + } + + // Generate result using similarity rule class + manifold::KSimilarity + kBuilder(nNeighbors); + arma::mat kResult = kBuilder.BuildSimilarityMat(data); + BOOST_REQUIRE_EQUAL(0, arma::accu(arma::abs(kAnswer - kResult))); + + // Generate correct answer by simplest approach + arma::mat epsilonAnswer; + double epsilon = 1.5; + epsilonAnswer.zeros(nData, nData); + for (size_t i = 0; i < nData; ++i) + { + arma::colvec dist(nData); + for (size_t j = 0; j < nData; ++j) + { + dist(j) = metric::EuclideanDistance::Evaluate( + data.unsafe_col(i), data.unsafe_col(j)); + } + arma::uvec ind = arma::find(dist < epsilon); + for (size_t j = 0; j < ind.n_elem; ++j) + epsilonAnswer(i, ind(j)) = dist(ind(j)); + } + + // Generate result using similarity rule class + manifold::EpsilonSimilarity + epsilonBuilder(epsilon); + arma::mat epsilonResult = epsilonBuilder.BuildSimilarityMat(data); + BOOST_REQUIRE_EQUAL(0, arma::accu(arma::abs(epsilonAnswer - epsilonResult))); + + /** + * Test local weight for reconstruction + * First generate the local weight matrix using the rule class, + * then disturb the weight matrix randomly. + * The reconstruction error using original weight matrix should always be + * less than error using disturbed weight matrix. + */ + + size_t nEpoch = 5; + manifold::KSimilarity + kWeightBuilder(nNeighbors); + arma::mat kWeight = kWeightBuilder.BuildSimilarityMat(data); + for (size_t epoch = 0; epoch < nEpoch; ++epoch) + { + arma::mat disturbedKWeight = + kWeight + arma::randn(nData, nData); + for (size_t i = 0; i < nData; ++i) + { + arma::colvec v1(nDim); + v1.zeros(); + arma::colvec v2(nDim); + v2.zeros(); + for (size_t j = 0; j < nData; ++j) + { + if (j != i) + { + v1 += kWeight(i, j) * data.unsafe_col(j); + v2 += disturbedKWeight(i, j) * data.unsafe_col(j); + } + } + double err1 = metric::EuclideanDistance::Evaluate(v1, data.unsafe_col(i)); + double err2 = metric::EuclideanDistance::Evaluate(v2, data.unsafe_col(i)); + BOOST_REQUIRE_LE(err1, err2); + } + } + + manifold::EpsilonSimilarity + epsilonWeightBuilder(epsilon); + arma::mat epsilonWeight = epsilonWeightBuilder.BuildSimilarityMat(data); + for (size_t epoch = 0; epoch < nEpoch; ++epoch) + { + arma::mat disturbedEpsilonWeight = + epsilonWeight + arma::randn(nData, nData); + for (size_t i = 0; i < nData; ++i) + { + arma::colvec v1(nDim); + v1.zeros(); + arma::colvec v2(nDim); + v2.zeros(); + for (size_t j = 0; j < nData; ++j) + { + if (j != i) + { + v1 += epsilonWeight(i, j) * data.unsafe_col(j); + v2 += disturbedEpsilonWeight(i, j) * data.unsafe_col(j); + } + } + double err1 = metric::EuclideanDistance::Evaluate(v1, data.unsafe_col(i)); + double err2 = metric::EuclideanDistance::Evaluate(v2, data.unsafe_col(i)); + BOOST_REQUIRE_LE(err1, err2); + } + } +} + +// Test MDS +BOOST_AUTO_TEST_CASE(MDSTest) +{ + + arma::mat tdata("-4 34 7 10;" + "23 35 13 6;" + "14 -44 18 7;" + "2 5 6 7;" + "15 25 12 19;" + "56 90 23 4;" + "12 14 19 80;"); + arma::mat data = tdata.rows(0, tdata.n_rows - 2).t(); + arma::colvec newVec = tdata.row(tdata.n_rows - 1).t(); + MDS<> mds(data, 2); + arma::mat embedding; + mds.Transform(embedding); + + arma::colvec newEmbedding; + mds.Transform(newVec, newEmbedding); + + // calculate the kruskal stress + MDSSimilarity<> similarity; + arma::mat simMatRaw = similarity.BuildSimilarityMat(data); + arma::mat simMatEmbedding = similarity.BuildSimilarityMat(embedding); + double kruskal = arma::accu(simMatRaw - simMatEmbedding) / arma::accu(simMatRaw); + BOOST_REQUIRE_LE(kruskal, 0.025); + +} + +// Test shortest path solver +BOOST_AUTO_TEST_CASE(ShortestPathTest) +{ + // a small symmetrical graph + arma::mat graph("0 4 5 6 0 0;" + "0 0 6 0 3 5;" + "0 0 0 9 0 0;" + "0 0 0 0 4 1;" + "0 0 0 0 0 5;" + "0 0 0 0 0 0;"); + graph = graph + graph.t(); + arma::mat dist("0 4 5 6 7 7;" + "4 0 6 6 3 5;" + "5 6 0 9 9 10;" + "6 6 9 0 4 1;" + "7 3 9 4 0 5;" + "7 5 10 1 5 0;"); + + arma::mat floydDist; + FloydWarshall::Solve(graph, floydDist); + arma::mat dijkstraDist; + Dijkstra::Solve(graph, dijkstraDist); + + BOOST_REQUIRE_EQUAL(0, arma::accu(arma::abs(dist - floydDist))); + BOOST_REQUIRE_EQUAL(0, arma::accu(arma::abs(dist - dijkstraDist))); + + // randomly generate a big asymmetrical graph + arma::mat bigGraph; + bigGraph.randn(400, 400); + bigGraph = arma::abs(bigGraph); + bigGraph.diag().zeros(); + FloydWarshall::Solve(bigGraph, floydDist); + Dijkstra::Solve(bigGraph, dijkstraDist); + BOOST_REQUIRE_LE(arma::accu(arma::abs(dijkstraDist - floydDist)), 1e-5); +} + +/** + * Test Isomap, LLE, LE on swissroll dataset. + * The swissroll dataset is generated according to + * http://people.cs.uchicago.edu/~dinoj/manifold/swissroll.html + * Difference is that swissroll.dat contains 400 points totally + * rather than 1600 points. + * Each algorithm will generate a xxx.dat file containing corresponding + * embedding vectors. + * These embedding vectors can be visualized in matlab using the following code: + * @code + * color = ['r' 'b' 'g' 'y']; + * load('lle.dat'); + * lle = lle'; + * X = lle(:,1); + * Y = lle(:,2); + * step = 100; + * sp = 1; + * ep = step; + * figure + * for i = 1 : 4 + * scatter(X(sp:ep,:), Y(sp:ep,:), color(i)); + * hold on + * sp = sp + step; + * ep = ep + step; + * end + * hold off + * @endcode + */ +BOOST_AUTO_TEST_CASE(SwissrollTest) +{ + arma::mat rawData; + arma::mat data; + arma::colvec newVec; + rawData.load("swissroll.dat"); +// data = rawData.rows(0, rawData.n_rows - 2).t(); + data = rawData.t(); + newVec = rawData.row(rawData.n_rows - 1).t(); + size_t dim = 2; + + arma::mat lleEmbedding; + LLE<> lle(data, dim); + lle.Transform(lleEmbedding); + lleEmbedding.save("lle.dat", arma::raw_ascii); + arma::colvec newLLEEmbedding; + lle.Transform(newVec, newLLEEmbedding); + + arma::mat leEmbedding; + LE<> le(data, dim); + le.Transform(leEmbedding); + leEmbedding.save("le.dat", arma::raw_ascii); + arma::colvec newLEEmbedding; + le.Transform(newVec, newLEEmbedding); + + arma::mat isomapEmbedding; + Isomap<> isomap(data, dim); + isomap.Transform(isomapEmbedding); + isomapEmbedding.save("isomap.dat", arma::raw_ascii); + arma::colvec newIsomapEmbedding; + isomap.Transform(newVec, newIsomapEmbedding); + +} + + +BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file From 515317cba616cef55a557e411a35e5f00567f780 Mon Sep 17 00:00:00 2001 From: Shangtong Zhang <302536811@qq.com> Date: Sun, 17 May 2015 10:08:18 +0800 Subject: [PATCH 2/2] revert to older armadillo expression --- .../manifold_learning/eigenmap_rule/standard_eigenmap.hpp | 2 +- src/mlpack/tests/manifold_learning_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mlpack/methods/manifold_learning/eigenmap_rule/standard_eigenmap.hpp b/src/mlpack/methods/manifold_learning/eigenmap_rule/standard_eigenmap.hpp index f084e445985..6b1f90bf60a 100644 --- a/src/mlpack/methods/manifold_learning/eigenmap_rule/standard_eigenmap.hpp +++ b/src/mlpack/methods/manifold_learning/eigenmap_rule/standard_eigenmap.hpp @@ -50,7 +50,7 @@ class StandardEigenmap SolverType::Solve(M, eigvec, eigval); // Sort eigen values in descending order - arma::uvec ind = arma::sort_index(eigval, "descend"); + arma::uvec ind = arma::sort_index(eigval, 1); orderedEigvec.set_size(M.n_cols, dim); orderedEigval.set_size(dim, 1); diff --git a/src/mlpack/tests/manifold_learning_test.cpp b/src/mlpack/tests/manifold_learning_test.cpp index e3b5b77c2fe..335053b785d 100644 --- a/src/mlpack/tests/manifold_learning_test.cpp +++ b/src/mlpack/tests/manifold_learning_test.cpp @@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(SimilarityRuleTest) dist(j) = metric::EuclideanDistance::Evaluate( data.unsafe_col(i), data.unsafe_col(j)); } - arma::uvec ind = arma::sort_index(dist, "ascend"); + arma::uvec ind = arma::sort_index(dist); for (size_t j = 1; j < nNeighbors + 1; ++j) kAnswer(i, ind(j)) = dist(ind(j)); }