// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2009 Benoit Jacob // // Eigen is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // // Alternatively, 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 2 of // the License, or (at your option) any later version. // // Eigen is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the // GNU General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License and a copy of the GNU General Public License along with // Eigen. If not, see . #ifndef EIGEN_JACOBISVD_H #define EIGEN_JACOBISVD_H /** \ingroup SVD_Module * \nonstableyet * * \class JacobiSVD * * \brief Jacobi SVD decomposition of a square matrix * * \param MatrixType the type of the matrix of which we are computing the SVD decomposition * \param ComputeU whether the U matrix should be computed * \param ComputeV whether the V matrix should be computed * * \sa MatrixBase::jacobiSvd() */ template class JacobiSVD { private: typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; enum { ComputeU = 1, ComputeV = 1, RowsAtCompileTime = MatrixType::RowsAtCompileTime, ColsAtCompileTime = MatrixType::ColsAtCompileTime, DiagSizeAtCompileTime = EIGEN_ENUM_MIN(RowsAtCompileTime,ColsAtCompileTime), MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, MaxDiagSizeAtCompileTime = EIGEN_ENUM_MIN(MaxRowsAtCompileTime,MaxColsAtCompileTime), MatrixOptions = MatrixType::Options }; typedef Matrix DummyMatrixType; typedef typename ei_meta_if, DummyMatrixType>::ret MatrixUType; typedef typename ei_meta_if, DummyMatrixType>::ret MatrixVType; typedef Matrix SingularValuesType; typedef Matrix RowType; typedef Matrix ColType; public: JacobiSVD() : m_isInitialized(false) {} JacobiSVD(const MatrixType& matrix) : m_isInitialized(false) { compute(matrix); } JacobiSVD& compute(const MatrixType& matrix); const MatrixUType& matrixU() const { ei_assert(m_isInitialized && "JacobiSVD is not initialized."); return m_matrixU; } const SingularValuesType& singularValues() const { ei_assert(m_isInitialized && "JacobiSVD is not initialized."); return m_singularValues; } const MatrixUType& matrixV() const { ei_assert(m_isInitialized && "JacobiSVD is not initialized."); return m_matrixV; } protected: MatrixUType m_matrixU; MatrixVType m_matrixV; SingularValuesType m_singularValues; bool m_isInitialized; template friend struct ei_svd_precondition_2x2_block_to_be_real; }; template::IsComplex> struct ei_svd_precondition_2x2_block_to_be_real { static void run(MatrixType&, JacobiSVD&, int, int) {} }; template struct ei_svd_precondition_2x2_block_to_be_real { typedef JacobiSVD SVD; typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; enum { ComputeU = SVD::ComputeU, ComputeV = SVD::ComputeV }; static void run(MatrixType& work_matrix, JacobiSVD& svd, int p, int q) { Scalar z; PlanarRotation rot; RealScalar n = ei_sqrt(ei_abs2(work_matrix.coeff(p,p)) + ei_abs2(work_matrix.coeff(q,p))); if(n==0) { z = ei_abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q); work_matrix.row(p) *= z; if(ComputeU) svd.m_matrixU.col(p) *= ei_conj(z); z = ei_abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q); work_matrix.row(q) *= z; if(ComputeU) svd.m_matrixU.col(q) *= ei_conj(z); } else { rot.c() = ei_conj(work_matrix.coeff(p,p)) / n; rot.s() = work_matrix.coeff(q,p) / n; work_matrix.applyOnTheLeft(p,q,rot); if(ComputeU) svd.m_matrixU.applyOnTheRight(p,q,rot.adjoint()); if(work_matrix.coeff(p,q) != Scalar(0)) { Scalar z = ei_abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q); work_matrix.col(q) *= z; if(ComputeV) svd.m_matrixV.col(q) *= z; } if(work_matrix.coeff(q,q) != Scalar(0)) { z = ei_abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q); work_matrix.row(q) *= z; if(ComputeU) svd.m_matrixU.col(q) *= ei_conj(z); } } } }; template void ei_real_2x2_jacobi_svd(const MatrixType& matrix, int p, int q, PlanarRotation *j_left, PlanarRotation *j_right) { Matrix m; m << ei_real(matrix.coeff(p,p)), ei_real(matrix.coeff(p,q)), ei_real(matrix.coeff(q,p)), ei_real(matrix.coeff(q,q)); PlanarRotation rot1; RealScalar t = m.coeff(0,0) + m.coeff(1,1); RealScalar d = m.coeff(1,0) - m.coeff(0,1); if(t == RealScalar(0)) { rot1.c() = 0; rot1.s() = d > 0 ? 1 : -1; } else { RealScalar u = d / t; rot1.c() = RealScalar(1) / ei_sqrt(1 + ei_abs2(u)); rot1.s() = rot1.c() * u; } m.applyOnTheLeft(0,1,rot1); j_right->makeJacobi(m,0,1); *j_left = rot1 * j_right->transpose(); } template JacobiSVD& JacobiSVD::compute(const MatrixType& matrix) { MatrixType work_matrix; int rows = matrix.rows(); int cols = matrix.cols(); int diagSize = std::min(rows, cols); if(ComputeU) m_matrixU = MatrixUType::Zero(rows,rows); if(ComputeV) m_matrixV = MatrixVType::Zero(cols,cols); m_singularValues.resize(diagSize); const RealScalar precision = 2 * epsilon(); if(rows > cols) { FullPivotingHouseholderQR qr(matrix); work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView(); if(ComputeU) m_matrixU = qr.matrixQ(); if(ComputeV) for(int i = 0; i < cols; i++) m_matrixV.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); } else if(rows < cols) { FullPivotingHouseholderQR qr(MatrixType(matrix.adjoint())); work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView().adjoint(); if(ComputeV) m_matrixV = qr.matrixQ(); if(ComputeU) for(int i = 0; i < rows; i++) m_matrixU.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); } else { work_matrix = matrix; if(ComputeU) m_matrixU.diagonal().setOnes(); if(ComputeV) m_matrixV.diagonal().setOnes(); } sweep_again: for(int p = 1; p < diagSize; ++p) { for(int q = 0; q < p; ++q) { if(std::max(ei_abs(work_matrix.coeff(p,q)),ei_abs(work_matrix.coeff(q,p))) > std::max(ei_abs(work_matrix.coeff(p,p)),ei_abs(work_matrix.coeff(q,q)))*precision) { ei_svd_precondition_2x2_block_to_be_real::run(work_matrix, *this, p, q); PlanarRotation j_left, j_right; ei_real_2x2_jacobi_svd(work_matrix, p, q, &j_left, &j_right); work_matrix.applyOnTheLeft(p,q,j_left); if(ComputeU) m_matrixU.applyOnTheRight(p,q,j_left.transpose()); work_matrix.applyOnTheRight(p,q,j_right); if(ComputeV) m_matrixV.applyOnTheRight(p,q,j_right); } } } RealScalar biggestOnDiag = work_matrix.diagonal().cwise().abs().maxCoeff(); RealScalar maxAllowedOffDiag = biggestOnDiag * precision; for(int p = 0; p < diagSize; ++p) { for(int q = 0; q < p; ++q) if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) goto sweep_again; for(int q = p+1; q < diagSize; ++q) if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) goto sweep_again; } for(int i = 0; i < diagSize; ++i) { RealScalar a = ei_abs(work_matrix.coeff(i,i)); m_singularValues.coeffRef(i) = a; if(ComputeU && (a!=RealScalar(0))) m_matrixU.col(i) *= work_matrix.coeff(i,i)/a; } for(int i = 0; i < diagSize; i++) { int pos; m_singularValues.end(diagSize-i).maxCoeff(&pos); if(pos) { pos += i; std::swap(m_singularValues.coeffRef(i), m_singularValues.coeffRef(pos)); if(ComputeU) m_matrixU.col(pos).swap(m_matrixU.col(i)); if(ComputeV) m_matrixV.col(pos).swap(m_matrixV.col(i)); } } m_isInitialized = true; return *this; } #endif // EIGEN_JACOBISVD_H