diff --git a/src/shogun/mathematics/ajd/UWedge.cpp b/src/shogun/mathematics/ajd/UWedge.cpp new file mode 100644 index 00000000000..12b65b29633 --- /dev/null +++ b/src/shogun/mathematics/ajd/UWedge.cpp @@ -0,0 +1,109 @@ +#ifdef HAVE_EIGEN3 + +#include + +#include +#include + +#include +#include + +using namespace Eigen; + +typedef Matrix< float64_t, Dynamic, 1, ColMajor > EVector; +typedef Matrix< float64_t, Dynamic, Dynamic, ColMajor > EMatrix; + +using namespace shogun; + +SGMatrix CUWedge::diagonalize(SGNDArray &C, SGMatrix V0, + double eps, int itermax) +{ + int d = C.dims[0]; + int L = C.dims[2]; + + SGMatrix V; + if (V0.num_rows != 0) + { + V = V0.clone(); + } + else + { + Eigen::Map C0(C.get_matrix(0),d,d); + EigenSolver eig; + eig.compute(C0); + V = SGMatrix::create_identity_matrix(d,1); + Eigen::Map EV(V.matrix, d,d); + EV = eig.pseudoEigenvalueMatrix().cwiseAbs().cwiseSqrt().inverse() * eig.pseudoEigenvectors().transpose(); + } + Eigen::Map EV(V.matrix, d,d); + + index_t * Cs_dims = SG_MALLOC(index_t, 3); + Cs_dims[0] = d; + Cs_dims[1] = d; + Cs_dims[2] = L; + SGNDArray Cs(Cs_dims,3); + memcpy(Cs.array, C.array, Cs.dims[0]*Cs.dims[1]*Cs.dims[2]*sizeof(float64_t)); + + EMatrix Rs(d,L); + std::vector crit; + crit.push_back(0.0); + for(int l = 0; l < L; l++) + { + Eigen::Map Ci(C.get_matrix(l),d,d); + Eigen::Map Csi(Cs.get_matrix(l),d,d); + Ci = 0.5 * (Ci + Ci.transpose()); + Csi = EV * Ci * EV.transpose(); + Rs.col(l) = Csi.diagonal(); + crit.back() += Csi.cwiseAbs2().sum() - Rs.col(l).cwiseAbs2().sum(); + } + + double iter = 0; + double improve = 10; + while (improve > eps && iter < itermax) + { + EMatrix B = Rs * Rs.transpose(); + + EMatrix C1 = EMatrix::Zero(d,d); + for(int id = 0; id < d; id++) + { + // rowSums + for(int l = 0; l < L; l++) + { + Eigen::Map Csi(Cs.get_matrix(l),d,d); + C1.row(id) += Csi.row(id) * Rs(id,l); + } + } + + EMatrix D0 = B.cwiseProduct(B.transpose()) - B.diagonal() * B.diagonal().transpose(); + EMatrix A0 = EMatrix::Identity(d,d) + (C1.cwiseProduct(B) - B.diagonal().asDiagonal() * C1.transpose()).cwiseQuotient(D0+EMatrix::Identity(d,d)); + EV = A0.inverse() * EV; + + Eigen::Map C0(C.get_matrix(0),d,d); + EMatrix Raux = EV * C0 * EV.transpose(); + EMatrix aux = Raux.diagonal().cwiseAbs().cwiseSqrt().asDiagonal().inverse(); + EV = aux * EV; + + crit.push_back(0.0); + for(int l = 0; l < L; l++) + { + Eigen::Map Ci(C.get_matrix(l),d,d); + Eigen::Map Csi(Cs.get_matrix(l),d,d); + Csi = EV * Ci * EV.transpose(); + Rs.col(l) = Csi.diagonal(); + crit.back() += Csi.cwiseAbs2().sum() - Rs.col(l).cwiseAbs2().sum(); + } + + improve = CMath::abs(crit.back() - crit[iter]); + iter++; + } + + if(iter == itermax) + { + SG_SERROR("Convergence not reached\n") + } + + return V; + +} + +#endif //HAVE_EIGEN3 diff --git a/src/shogun/mathematics/ajd/UWedge.h b/src/shogun/mathematics/ajd/UWedge.h new file mode 100644 index 00000000000..347f81e8e2f --- /dev/null +++ b/src/shogun/mathematics/ajd/UWedge.h @@ -0,0 +1,80 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Written (W) 2013 Kevin Hughes + * + * Thanks to Andreas Ziehe and Cedric Gouy-Pailler + */ + +#ifndef UWEDGE_H_ +#define UWEDGE_H_ + +#ifdef HAVE_EIGEN3 + +#include + +#include +#include +#include +#include + +#include + +namespace shogun +{ + +/** @brief Class FFDiag + * + * An Approximate Joint Diagonalizer (AJD) Implementation + */ +class CUWedge : public CApproxJointDiagonalizer +{ + public: + + /** constructor */ + CUWedge() + { + }; + + /** destructor */ + virtual ~CUWedge() + { + } + + /** Computes the matrix V that best diagonalizes C + * @param C the set of matrices to be diagonalized + * @param V0 an estimate of the matrix V + * @param eps machine epsilon or desired epsilon + * @param itermax maximum number of iterations + * @return V the matrix the best diagonalizes C + */ + static SGMatrix diagonalize(SGNDArray &C, + SGMatrix V0= SGMatrix(NULL,0,0), + double eps=CMath::MACHINE_EPSILON, + int itermax=200); + + /** Computes the matrix V that best diagonalizes C + * @param C the set of matrices to be diagonalized + * @param V0 an estimate of the matrix V + * @param eps machine epsilon or desired epsilon + * @param itermax maximum number of iterations + * @return V the matrix the best diagonalizes C + */ + virtual SGMatrix compute(SGNDArray &C, + SGMatrix V0= SGMatrix(NULL,0,0), + double eps=CMath::MACHINE_EPSILON, + int itermax=200) + { + m_V = diagonalize(C,V0,eps,itermax); + return m_V; + } + + /** @return object name */ + virtual const char* get_name() const { return "UWedge"; } +}; +} +#endif //HAVE_EIGEN3 +#endif //UWEDGE_H_ diff --git a/tests/unit/mathematics/ajd/UWedge_unittest.cc b/tests/unit/mathematics/ajd/UWedge_unittest.cc new file mode 100644 index 00000000000..e1bb78697d2 --- /dev/null +++ b/tests/unit/mathematics/ajd/UWedge_unittest.cc @@ -0,0 +1,75 @@ +#include +#include +#include + +#ifdef HAVE_EIGEN3 + +#include +#include +#include + +#include +#include +#include + +#include + +using namespace Eigen; + +typedef Matrix< float64_t, Dynamic, Dynamic, ColMajor > EMatrix; +typedef Matrix< float64_t, Dynamic, 1, ColMajor > EVector; + +using namespace shogun; + +TEST(CUWedge, diagonalize) +{ + // Generating diagonal matrices + index_t * C_dims = SG_MALLOC(index_t, 3); + C_dims[0] = 10; + C_dims[1] = 10; + C_dims[2] = 30; + SGNDArray< float64_t > C(C_dims, 3); + + CMath::init_random(17); + + for (int i = 0; i < C_dims[2]; i++) + { + Eigen::Map tmp(C.get_matrix(i),C_dims[0], C_dims[1]); + tmp.setIdentity(); + + for (int j = 0; j < C_dims[0]; j++) + { + tmp(j,j) *= CMath::abs(CMath::random(1,5)); + } + } + + // Mixing and demixing matrices + EMatrix B(C_dims[0], C_dims[1]); + B.setRandom(); + EMatrix A = B.inverse(); + + for (int i = 0; i < C_dims[2]; i++) + { + Eigen::Map Ci(C.get_matrix(i),C_dims[0], C_dims[1]); + Ci = A * Ci * A.transpose(); + } + + /** Diagonalize **/ + SGMatrix V = CUWedge::diagonalize(C); + + // Test output size + EXPECT_EQ(V.num_rows, C_dims[0]); + EXPECT_EQ(V.num_cols, C_dims[1]); + + // Close to a permutation matrix (with random scales) + Eigen::Map EV(V.matrix,C_dims[0], C_dims[1]); + SGMatrix P(C_dims[0],C_dims[1]); + Eigen::Map EP(P.matrix,C_dims[0], C_dims[1]); + EP = EV * A; + + // Test if output is correct + bool isperm = is_permutation_matrix(P); + EXPECT_EQ(isperm,true); +} + +#endif //HAVE_EIGEN3