diff options
| author | Stanislaw Halik <sthalik@misaki.pl> | 2019-03-03 21:09:10 +0100 |
|---|---|---|
| committer | Stanislaw Halik <sthalik@misaki.pl> | 2019-03-03 21:10:13 +0100 |
| commit | f0238cfb6997c4acfc2bd200de7295f3fa36968f (patch) | |
| tree | b215183760e4f615b9c1dabc1f116383b72a1b55 /eigen/Eigen/src/SVD | |
| parent | 543edd372a5193d04b3de9f23c176ab439e51b31 (diff) | |
don't index Eigen
Diffstat (limited to 'eigen/Eigen/src/SVD')
| -rw-r--r-- | eigen/Eigen/src/SVD/BDCSVD.h | 1246 | ||||
| -rw-r--r-- | eigen/Eigen/src/SVD/JacobiSVD.h | 804 | ||||
| -rw-r--r-- | eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h | 91 | ||||
| -rw-r--r-- | eigen/Eigen/src/SVD/SVDBase.h | 315 | ||||
| -rw-r--r-- | eigen/Eigen/src/SVD/UpperBidiagonalization.h | 414 |
5 files changed, 0 insertions, 2870 deletions
diff --git a/eigen/Eigen/src/SVD/BDCSVD.h b/eigen/Eigen/src/SVD/BDCSVD.h deleted file mode 100644 index 1134d66..0000000 --- a/eigen/Eigen/src/SVD/BDCSVD.h +++ /dev/null @@ -1,1246 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// We used the "A Divide-And-Conquer Algorithm for the Bidiagonal SVD" -// research report written by Ming Gu and Stanley C.Eisenstat -// The code variable names correspond to the names they used in their -// report -// -// Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com> -// Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr> -// Copyright (C) 2013 Jean Ceccato <jean.ceccato@ensimag.fr> -// Copyright (C) 2013 Pierre Zoppitelli <pierre.zoppitelli@ensimag.fr> -// Copyright (C) 2013 Jitse Niesen <jitse@maths.leeds.ac.uk> -// Copyright (C) 2014-2017 Gael Guennebaud <gael.guennebaud@inria.fr> -// -// Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef EIGEN_BDCSVD_H -#define EIGEN_BDCSVD_H -// #define EIGEN_BDCSVD_DEBUG_VERBOSE -// #define EIGEN_BDCSVD_SANITY_CHECKS - -namespace Eigen { - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE -IOFormat bdcsvdfmt(8, 0, ", ", "\n", " [", "]"); -#endif - -template<typename _MatrixType> class BDCSVD; - -namespace internal { - -template<typename _MatrixType> -struct traits<BDCSVD<_MatrixType> > -{ - typedef _MatrixType MatrixType; -}; - -} // end namespace internal - - -/** \ingroup SVD_Module - * - * - * \class BDCSVD - * - * \brief class Bidiagonal Divide and Conquer SVD - * - * \tparam _MatrixType the type of the matrix of which we are computing the SVD decomposition - * - * This class first reduces the input matrix to bi-diagonal form using class UpperBidiagonalization, - * and then performs a divide-and-conquer diagonalization. Small blocks are diagonalized using class JacobiSVD. - * You can control the switching size with the setSwitchSize() method, default is 16. - * For small matrice (<16), it is thus preferable to directly use JacobiSVD. For larger ones, BDCSVD is highly - * recommended and can several order of magnitude faster. - * - * \warning this algorithm is unlikely to provide accurate result when compiled with unsafe math optimizations. - * For instance, this concerns Intel's compiler (ICC), which perfroms such optimization by default unless - * you compile with the \c -fp-model \c precise option. Likewise, the \c -ffast-math option of GCC or clang will - * significantly degrade the accuracy. - * - * \sa class JacobiSVD - */ -template<typename _MatrixType> -class BDCSVD : public SVDBase<BDCSVD<_MatrixType> > -{ - typedef SVDBase<BDCSVD> Base; - -public: - using Base::rows; - using Base::cols; - using Base::computeU; - using Base::computeV; - - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; - typedef typename NumTraits<RealScalar>::Literal Literal; - enum { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - ColsAtCompileTime = MatrixType::ColsAtCompileTime, - DiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(RowsAtCompileTime, ColsAtCompileTime), - MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, - MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, - MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_FIXED(MaxRowsAtCompileTime, MaxColsAtCompileTime), - MatrixOptions = MatrixType::Options - }; - - typedef typename Base::MatrixUType MatrixUType; - typedef typename Base::MatrixVType MatrixVType; - typedef typename Base::SingularValuesType SingularValuesType; - - typedef Matrix<Scalar, Dynamic, Dynamic, ColMajor> MatrixX; - typedef Matrix<RealScalar, Dynamic, Dynamic, ColMajor> MatrixXr; - typedef Matrix<RealScalar, Dynamic, 1> VectorType; - typedef Array<RealScalar, Dynamic, 1> ArrayXr; - typedef Array<Index,1,Dynamic> ArrayXi; - typedef Ref<ArrayXr> ArrayRef; - typedef Ref<ArrayXi> IndicesRef; - - /** \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via BDCSVD::compute(const MatrixType&). - */ - BDCSVD() : m_algoswap(16), m_numIters(0) - {} - - - /** \brief Default Constructor with memory preallocation - * - * Like the default constructor but with preallocation of the internal data - * according to the specified problem size. - * \sa BDCSVD() - */ - BDCSVD(Index rows, Index cols, unsigned int computationOptions = 0) - : m_algoswap(16), m_numIters(0) - { - allocate(rows, cols, computationOptions); - } - - /** \brief Constructor performing the decomposition of given matrix. - * - * \param matrix the matrix to decompose - * \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed. - * By default, none is computed. This is a bit - field, the possible bits are #ComputeFullU, #ComputeThinU, - * #ComputeFullV, #ComputeThinV. - * - * Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not - * available with the (non - default) FullPivHouseholderQR preconditioner. - */ - BDCSVD(const MatrixType& matrix, unsigned int computationOptions = 0) - : m_algoswap(16), m_numIters(0) - { - compute(matrix, computationOptions); - } - - ~BDCSVD() - { - } - - /** \brief Method performing the decomposition of given matrix using custom options. - * - * \param matrix the matrix to decompose - * \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed. - * By default, none is computed. This is a bit - field, the possible bits are #ComputeFullU, #ComputeThinU, - * #ComputeFullV, #ComputeThinV. - * - * Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not - * available with the (non - default) FullPivHouseholderQR preconditioner. - */ - BDCSVD& compute(const MatrixType& matrix, unsigned int computationOptions); - - /** \brief Method performing the decomposition of given matrix using current options. - * - * \param matrix the matrix to decompose - * - * This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int). - */ - BDCSVD& compute(const MatrixType& matrix) - { - return compute(matrix, this->m_computationOptions); - } - - void setSwitchSize(int s) - { - eigen_assert(s>3 && "BDCSVD the size of the algo switch has to be greater than 3"); - m_algoswap = s; - } - -private: - void allocate(Index rows, Index cols, unsigned int computationOptions); - void divide(Index firstCol, Index lastCol, Index firstRowW, Index firstColW, Index shift); - void computeSVDofM(Index firstCol, Index n, MatrixXr& U, VectorType& singVals, MatrixXr& V); - void computeSingVals(const ArrayRef& col0, const ArrayRef& diag, const IndicesRef& perm, VectorType& singVals, ArrayRef shifts, ArrayRef mus); - void perturbCol0(const ArrayRef& col0, const ArrayRef& diag, const IndicesRef& perm, const VectorType& singVals, const ArrayRef& shifts, const ArrayRef& mus, ArrayRef zhat); - void computeSingVecs(const ArrayRef& zhat, const ArrayRef& diag, const IndicesRef& perm, const VectorType& singVals, const ArrayRef& shifts, const ArrayRef& mus, MatrixXr& U, MatrixXr& V); - void deflation43(Index firstCol, Index shift, Index i, Index size); - void deflation44(Index firstColu , Index firstColm, Index firstRowW, Index firstColW, Index i, Index j, Index size); - void deflation(Index firstCol, Index lastCol, Index k, Index firstRowW, Index firstColW, Index shift); - template<typename HouseholderU, typename HouseholderV, typename NaiveU, typename NaiveV> - void copyUV(const HouseholderU &householderU, const HouseholderV &householderV, const NaiveU &naiveU, const NaiveV &naivev); - void structured_update(Block<MatrixXr,Dynamic,Dynamic> A, const MatrixXr &B, Index n1); - static RealScalar secularEq(RealScalar x, const ArrayRef& col0, const ArrayRef& diag, const IndicesRef &perm, const ArrayRef& diagShifted, RealScalar shift); - -protected: - MatrixXr m_naiveU, m_naiveV; - MatrixXr m_computed; - Index m_nRec; - ArrayXr m_workspace; - ArrayXi m_workspaceI; - int m_algoswap; - bool m_isTranspose, m_compU, m_compV; - - using Base::m_singularValues; - using Base::m_diagSize; - using Base::m_computeFullU; - using Base::m_computeFullV; - using Base::m_computeThinU; - using Base::m_computeThinV; - using Base::m_matrixU; - using Base::m_matrixV; - using Base::m_isInitialized; - using Base::m_nonzeroSingularValues; - -public: - int m_numIters; -}; //end class BDCSVD - - -// Method to allocate and initialize matrix and attributes -template<typename MatrixType> -void BDCSVD<MatrixType>::allocate(Index rows, Index cols, unsigned int computationOptions) -{ - m_isTranspose = (cols > rows); - - if (Base::allocate(rows, cols, computationOptions)) - return; - - m_computed = MatrixXr::Zero(m_diagSize + 1, m_diagSize ); - m_compU = computeV(); - m_compV = computeU(); - if (m_isTranspose) - std::swap(m_compU, m_compV); - - if (m_compU) m_naiveU = MatrixXr::Zero(m_diagSize + 1, m_diagSize + 1 ); - else m_naiveU = MatrixXr::Zero(2, m_diagSize + 1 ); - - if (m_compV) m_naiveV = MatrixXr::Zero(m_diagSize, m_diagSize); - - m_workspace.resize((m_diagSize+1)*(m_diagSize+1)*3); - m_workspaceI.resize(3*m_diagSize); -}// end allocate - -template<typename MatrixType> -BDCSVD<MatrixType>& BDCSVD<MatrixType>::compute(const MatrixType& matrix, unsigned int computationOptions) -{ -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "\n\n\n======================================================================================================================\n\n\n"; -#endif - allocate(matrix.rows(), matrix.cols(), computationOptions); - using std::abs; - - const RealScalar considerZero = (std::numeric_limits<RealScalar>::min)(); - - //**** step -1 - If the problem is too small, directly falls back to JacobiSVD and return - if(matrix.cols() < m_algoswap) - { - // FIXME this line involves temporaries - JacobiSVD<MatrixType> jsvd(matrix,computationOptions); - if(computeU()) m_matrixU = jsvd.matrixU(); - if(computeV()) m_matrixV = jsvd.matrixV(); - m_singularValues = jsvd.singularValues(); - m_nonzeroSingularValues = jsvd.nonzeroSingularValues(); - m_isInitialized = true; - return *this; - } - - //**** step 0 - Copy the input matrix and apply scaling to reduce over/under-flows - RealScalar scale = matrix.cwiseAbs().maxCoeff(); - if(scale==Literal(0)) scale = Literal(1); - MatrixX copy; - if (m_isTranspose) copy = matrix.adjoint()/scale; - else copy = matrix/scale; - - //**** step 1 - Bidiagonalization - // FIXME this line involves temporaries - internal::UpperBidiagonalization<MatrixX> bid(copy); - - //**** step 2 - Divide & Conquer - m_naiveU.setZero(); - m_naiveV.setZero(); - // FIXME this line involves a temporary matrix - m_computed.topRows(m_diagSize) = bid.bidiagonal().toDenseMatrix().transpose(); - m_computed.template bottomRows<1>().setZero(); - divide(0, m_diagSize - 1, 0, 0, 0); - - //**** step 3 - Copy singular values and vectors - for (int i=0; i<m_diagSize; i++) - { - RealScalar a = abs(m_computed.coeff(i, i)); - m_singularValues.coeffRef(i) = a * scale; - if (a<considerZero) - { - m_nonzeroSingularValues = i; - m_singularValues.tail(m_diagSize - i - 1).setZero(); - break; - } - else if (i == m_diagSize - 1) - { - m_nonzeroSingularValues = i + 1; - break; - } - } - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE -// std::cout << "m_naiveU\n" << m_naiveU << "\n\n"; -// std::cout << "m_naiveV\n" << m_naiveV << "\n\n"; -#endif - if(m_isTranspose) copyUV(bid.householderV(), bid.householderU(), m_naiveV, m_naiveU); - else copyUV(bid.householderU(), bid.householderV(), m_naiveU, m_naiveV); - - m_isInitialized = true; - return *this; -}// end compute - - -template<typename MatrixType> -template<typename HouseholderU, typename HouseholderV, typename NaiveU, typename NaiveV> -void BDCSVD<MatrixType>::copyUV(const HouseholderU &householderU, const HouseholderV &householderV, const NaiveU &naiveU, const NaiveV &naiveV) -{ - // Note exchange of U and V: m_matrixU is set from m_naiveV and vice versa - if (computeU()) - { - Index Ucols = m_computeThinU ? m_diagSize : householderU.cols(); - m_matrixU = MatrixX::Identity(householderU.cols(), Ucols); - m_matrixU.topLeftCorner(m_diagSize, m_diagSize) = naiveV.template cast<Scalar>().topLeftCorner(m_diagSize, m_diagSize); - householderU.applyThisOnTheLeft(m_matrixU); // FIXME this line involves a temporary buffer - } - if (computeV()) - { - Index Vcols = m_computeThinV ? m_diagSize : householderV.cols(); - m_matrixV = MatrixX::Identity(householderV.cols(), Vcols); - m_matrixV.topLeftCorner(m_diagSize, m_diagSize) = naiveU.template cast<Scalar>().topLeftCorner(m_diagSize, m_diagSize); - householderV.applyThisOnTheLeft(m_matrixV); // FIXME this line involves a temporary buffer - } -} - -/** \internal - * Performs A = A * B exploiting the special structure of the matrix A. Splitting A as: - * A = [A1] - * [A2] - * such that A1.rows()==n1, then we assume that at least half of the columns of A1 and A2 are zeros. - * We can thus pack them prior to the the matrix product. However, this is only worth the effort if the matrix is large - * enough. - */ -template<typename MatrixType> -void BDCSVD<MatrixType>::structured_update(Block<MatrixXr,Dynamic,Dynamic> A, const MatrixXr &B, Index n1) -{ - Index n = A.rows(); - if(n>100) - { - // If the matrices are large enough, let's exploit the sparse structure of A by - // splitting it in half (wrt n1), and packing the non-zero columns. - Index n2 = n - n1; - Map<MatrixXr> A1(m_workspace.data() , n1, n); - Map<MatrixXr> A2(m_workspace.data()+ n1*n, n2, n); - Map<MatrixXr> B1(m_workspace.data()+ n*n, n, n); - Map<MatrixXr> B2(m_workspace.data()+2*n*n, n, n); - Index k1=0, k2=0; - for(Index j=0; j<n; ++j) - { - if( (A.col(j).head(n1).array()!=Literal(0)).any() ) - { - A1.col(k1) = A.col(j).head(n1); - B1.row(k1) = B.row(j); - ++k1; - } - if( (A.col(j).tail(n2).array()!=Literal(0)).any() ) - { - A2.col(k2) = A.col(j).tail(n2); - B2.row(k2) = B.row(j); - ++k2; - } - } - - A.topRows(n1).noalias() = A1.leftCols(k1) * B1.topRows(k1); - A.bottomRows(n2).noalias() = A2.leftCols(k2) * B2.topRows(k2); - } - else - { - Map<MatrixXr,Aligned> tmp(m_workspace.data(),n,n); - tmp.noalias() = A*B; - A = tmp; - } -} - -// The divide algorithm is done "in place", we are always working on subsets of the same matrix. The divide methods takes as argument the -// place of the submatrix we are currently working on. - -//@param firstCol : The Index of the first column of the submatrix of m_computed and for m_naiveU; -//@param lastCol : The Index of the last column of the submatrix of m_computed and for m_naiveU; -// lastCol + 1 - firstCol is the size of the submatrix. -//@param firstRowW : The Index of the first row of the matrix W that we are to change. (see the reference paper section 1 for more information on W) -//@param firstRowW : Same as firstRowW with the column. -//@param shift : Each time one takes the left submatrix, one must add 1 to the shift. Why? Because! We actually want the last column of the U submatrix -// to become the first column (*coeff) and to shift all the other columns to the right. There are more details on the reference paper. -template<typename MatrixType> -void BDCSVD<MatrixType>::divide (Index firstCol, Index lastCol, Index firstRowW, Index firstColW, Index shift) -{ - // requires rows = cols + 1; - using std::pow; - using std::sqrt; - using std::abs; - const Index n = lastCol - firstCol + 1; - const Index k = n/2; - const RealScalar considerZero = (std::numeric_limits<RealScalar>::min)(); - RealScalar alphaK; - RealScalar betaK; - RealScalar r0; - RealScalar lambda, phi, c0, s0; - VectorType l, f; - // We use the other algorithm which is more efficient for small - // matrices. - if (n < m_algoswap) - { - // FIXME this line involves temporaries - JacobiSVD<MatrixXr> b(m_computed.block(firstCol, firstCol, n + 1, n), ComputeFullU | (m_compV ? ComputeFullV : 0)); - if (m_compU) - m_naiveU.block(firstCol, firstCol, n + 1, n + 1).real() = b.matrixU(); - else - { - m_naiveU.row(0).segment(firstCol, n + 1).real() = b.matrixU().row(0); - m_naiveU.row(1).segment(firstCol, n + 1).real() = b.matrixU().row(n); - } - if (m_compV) m_naiveV.block(firstRowW, firstColW, n, n).real() = b.matrixV(); - m_computed.block(firstCol + shift, firstCol + shift, n + 1, n).setZero(); - m_computed.diagonal().segment(firstCol + shift, n) = b.singularValues().head(n); - return; - } - // We use the divide and conquer algorithm - alphaK = m_computed(firstCol + k, firstCol + k); - betaK = m_computed(firstCol + k + 1, firstCol + k); - // The divide must be done in that order in order to have good results. Divide change the data inside the submatrices - // and the divide of the right submatrice reads one column of the left submatrice. That's why we need to treat the - // right submatrix before the left one. - divide(k + 1 + firstCol, lastCol, k + 1 + firstRowW, k + 1 + firstColW, shift); - divide(firstCol, k - 1 + firstCol, firstRowW, firstColW + 1, shift + 1); - - if (m_compU) - { - lambda = m_naiveU(firstCol + k, firstCol + k); - phi = m_naiveU(firstCol + k + 1, lastCol + 1); - } - else - { - lambda = m_naiveU(1, firstCol + k); - phi = m_naiveU(0, lastCol + 1); - } - r0 = sqrt((abs(alphaK * lambda) * abs(alphaK * lambda)) + abs(betaK * phi) * abs(betaK * phi)); - if (m_compU) - { - l = m_naiveU.row(firstCol + k).segment(firstCol, k); - f = m_naiveU.row(firstCol + k + 1).segment(firstCol + k + 1, n - k - 1); - } - else - { - l = m_naiveU.row(1).segment(firstCol, k); - f = m_naiveU.row(0).segment(firstCol + k + 1, n - k - 1); - } - if (m_compV) m_naiveV(firstRowW+k, firstColW) = Literal(1); - if (r0<considerZero) - { - c0 = Literal(1); - s0 = Literal(0); - } - else - { - c0 = alphaK * lambda / r0; - s0 = betaK * phi / r0; - } - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(m_naiveU.allFinite()); - assert(m_naiveV.allFinite()); - assert(m_computed.allFinite()); -#endif - - if (m_compU) - { - MatrixXr q1 (m_naiveU.col(firstCol + k).segment(firstCol, k + 1)); - // we shiftW Q1 to the right - for (Index i = firstCol + k - 1; i >= firstCol; i--) - m_naiveU.col(i + 1).segment(firstCol, k + 1) = m_naiveU.col(i).segment(firstCol, k + 1); - // we shift q1 at the left with a factor c0 - m_naiveU.col(firstCol).segment( firstCol, k + 1) = (q1 * c0); - // last column = q1 * - s0 - m_naiveU.col(lastCol + 1).segment(firstCol, k + 1) = (q1 * ( - s0)); - // first column = q2 * s0 - m_naiveU.col(firstCol).segment(firstCol + k + 1, n - k) = m_naiveU.col(lastCol + 1).segment(firstCol + k + 1, n - k) * s0; - // q2 *= c0 - m_naiveU.col(lastCol + 1).segment(firstCol + k + 1, n - k) *= c0; - } - else - { - RealScalar q1 = m_naiveU(0, firstCol + k); - // we shift Q1 to the right - for (Index i = firstCol + k - 1; i >= firstCol; i--) - m_naiveU(0, i + 1) = m_naiveU(0, i); - // we shift q1 at the left with a factor c0 - m_naiveU(0, firstCol) = (q1 * c0); - // last column = q1 * - s0 - m_naiveU(0, lastCol + 1) = (q1 * ( - s0)); - // first column = q2 * s0 - m_naiveU(1, firstCol) = m_naiveU(1, lastCol + 1) *s0; - // q2 *= c0 - m_naiveU(1, lastCol + 1) *= c0; - m_naiveU.row(1).segment(firstCol + 1, k).setZero(); - m_naiveU.row(0).segment(firstCol + k + 1, n - k - 1).setZero(); - } - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(m_naiveU.allFinite()); - assert(m_naiveV.allFinite()); - assert(m_computed.allFinite()); -#endif - - m_computed(firstCol + shift, firstCol + shift) = r0; - m_computed.col(firstCol + shift).segment(firstCol + shift + 1, k) = alphaK * l.transpose().real(); - m_computed.col(firstCol + shift).segment(firstCol + shift + k + 1, n - k - 1) = betaK * f.transpose().real(); - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - ArrayXr tmp1 = (m_computed.block(firstCol+shift, firstCol+shift, n, n)).jacobiSvd().singularValues(); -#endif - // Second part: try to deflate singular values in combined matrix - deflation(firstCol, lastCol, k, firstRowW, firstColW, shift); -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - ArrayXr tmp2 = (m_computed.block(firstCol+shift, firstCol+shift, n, n)).jacobiSvd().singularValues(); - std::cout << "\n\nj1 = " << tmp1.transpose().format(bdcsvdfmt) << "\n"; - std::cout << "j2 = " << tmp2.transpose().format(bdcsvdfmt) << "\n\n"; - std::cout << "err: " << ((tmp1-tmp2).abs()>1e-12*tmp2.abs()).transpose() << "\n"; - static int count = 0; - std::cout << "# " << ++count << "\n\n"; - assert((tmp1-tmp2).matrix().norm() < 1e-14*tmp2.matrix().norm()); -// assert(count<681); -// assert(((tmp1-tmp2).abs()<1e-13*tmp2.abs()).all()); -#endif - - // Third part: compute SVD of combined matrix - MatrixXr UofSVD, VofSVD; - VectorType singVals; - computeSVDofM(firstCol + shift, n, UofSVD, singVals, VofSVD); - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(UofSVD.allFinite()); - assert(VofSVD.allFinite()); -#endif - - if (m_compU) - structured_update(m_naiveU.block(firstCol, firstCol, n + 1, n + 1), UofSVD, (n+2)/2); - else - { - Map<Matrix<RealScalar,2,Dynamic>,Aligned> tmp(m_workspace.data(),2,n+1); - tmp.noalias() = m_naiveU.middleCols(firstCol, n+1) * UofSVD; - m_naiveU.middleCols(firstCol, n + 1) = tmp; - } - - if (m_compV) structured_update(m_naiveV.block(firstRowW, firstColW, n, n), VofSVD, (n+1)/2); - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(m_naiveU.allFinite()); - assert(m_naiveV.allFinite()); - assert(m_computed.allFinite()); -#endif - - m_computed.block(firstCol + shift, firstCol + shift, n, n).setZero(); - m_computed.block(firstCol + shift, firstCol + shift, n, n).diagonal() = singVals; -}// end divide - -// Compute SVD of m_computed.block(firstCol, firstCol, n + 1, n); this block only has non-zeros in -// the first column and on the diagonal and has undergone deflation, so diagonal is in increasing -// order except for possibly the (0,0) entry. The computed SVD is stored U, singVals and V, except -// that if m_compV is false, then V is not computed. Singular values are sorted in decreasing order. -// -// TODO Opportunities for optimization: better root finding algo, better stopping criterion, better -// handling of round-off errors, be consistent in ordering -// For instance, to solve the secular equation using FMM, see http://www.stat.uchicago.edu/~lekheng/courses/302/classics/greengard-rokhlin.pdf -template <typename MatrixType> -void BDCSVD<MatrixType>::computeSVDofM(Index firstCol, Index n, MatrixXr& U, VectorType& singVals, MatrixXr& V) -{ - const RealScalar considerZero = (std::numeric_limits<RealScalar>::min)(); - using std::abs; - ArrayRef col0 = m_computed.col(firstCol).segment(firstCol, n); - m_workspace.head(n) = m_computed.block(firstCol, firstCol, n, n).diagonal(); - ArrayRef diag = m_workspace.head(n); - diag(0) = Literal(0); - - // Allocate space for singular values and vectors - singVals.resize(n); - U.resize(n+1, n+1); - if (m_compV) V.resize(n, n); - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - if (col0.hasNaN() || diag.hasNaN()) - std::cout << "\n\nHAS NAN\n\n"; -#endif - - // Many singular values might have been deflated, the zero ones have been moved to the end, - // but others are interleaved and we must ignore them at this stage. - // To this end, let's compute a permutation skipping them: - Index actual_n = n; - while(actual_n>1 && diag(actual_n-1)==Literal(0)) --actual_n; - Index m = 0; // size of the deflated problem - for(Index k=0;k<actual_n;++k) - if(abs(col0(k))>considerZero) - m_workspaceI(m++) = k; - Map<ArrayXi> perm(m_workspaceI.data(),m); - - Map<ArrayXr> shifts(m_workspace.data()+1*n, n); - Map<ArrayXr> mus(m_workspace.data()+2*n, n); - Map<ArrayXr> zhat(m_workspace.data()+3*n, n); - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "computeSVDofM using:\n"; - std::cout << " z: " << col0.transpose() << "\n"; - std::cout << " d: " << diag.transpose() << "\n"; -#endif - - // Compute singVals, shifts, and mus - computeSingVals(col0, diag, perm, singVals, shifts, mus); - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << " j: " << (m_computed.block(firstCol, firstCol, n, n)).jacobiSvd().singularValues().transpose().reverse() << "\n\n"; - std::cout << " sing-val: " << singVals.transpose() << "\n"; - std::cout << " mu: " << mus.transpose() << "\n"; - std::cout << " shift: " << shifts.transpose() << "\n"; - - { - Index actual_n = n; - while(actual_n>1 && abs(col0(actual_n-1))<considerZero) --actual_n; - std::cout << "\n\n mus: " << mus.head(actual_n).transpose() << "\n\n"; - std::cout << " check1 (expect0) : " << ((singVals.array()-(shifts+mus)) / singVals.array()).head(actual_n).transpose() << "\n\n"; - std::cout << " check2 (>0) : " << ((singVals.array()-diag) / singVals.array()).head(actual_n).transpose() << "\n\n"; - std::cout << " check3 (>0) : " << ((diag.segment(1,actual_n-1)-singVals.head(actual_n-1).array()) / singVals.head(actual_n-1).array()).transpose() << "\n\n\n"; - std::cout << " check4 (>0) : " << ((singVals.segment(1,actual_n-1)-singVals.head(actual_n-1))).transpose() << "\n\n\n"; - } -#endif - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(singVals.allFinite()); - assert(mus.allFinite()); - assert(shifts.allFinite()); -#endif - - // Compute zhat - perturbCol0(col0, diag, perm, singVals, shifts, mus, zhat); -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << " zhat: " << zhat.transpose() << "\n"; -#endif - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(zhat.allFinite()); -#endif - - computeSingVecs(zhat, diag, perm, singVals, shifts, mus, U, V); - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "U^T U: " << (U.transpose() * U - MatrixXr(MatrixXr::Identity(U.cols(),U.cols()))).norm() << "\n"; - std::cout << "V^T V: " << (V.transpose() * V - MatrixXr(MatrixXr::Identity(V.cols(),V.cols()))).norm() << "\n"; -#endif - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(U.allFinite()); - assert(V.allFinite()); - assert((U.transpose() * U - MatrixXr(MatrixXr::Identity(U.cols(),U.cols()))).norm() < 1e-14 * n); - assert((V.transpose() * V - MatrixXr(MatrixXr::Identity(V.cols(),V.cols()))).norm() < 1e-14 * n); - assert(m_naiveU.allFinite()); - assert(m_naiveV.allFinite()); - assert(m_computed.allFinite()); -#endif - - // Because of deflation, the singular values might not be completely sorted. - // Fortunately, reordering them is a O(n) problem - for(Index i=0; i<actual_n-1; ++i) - { - if(singVals(i)>singVals(i+1)) - { - using std::swap; - swap(singVals(i),singVals(i+1)); - U.col(i).swap(U.col(i+1)); - if(m_compV) V.col(i).swap(V.col(i+1)); - } - } - - // Reverse order so that singular values in increased order - // Because of deflation, the zeros singular-values are already at the end - singVals.head(actual_n).reverseInPlace(); - U.leftCols(actual_n).rowwise().reverseInPlace(); - if (m_compV) V.leftCols(actual_n).rowwise().reverseInPlace(); - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - JacobiSVD<MatrixXr> jsvd(m_computed.block(firstCol, firstCol, n, n) ); - std::cout << " * j: " << jsvd.singularValues().transpose() << "\n\n"; - std::cout << " * sing-val: " << singVals.transpose() << "\n"; -// std::cout << " * err: " << ((jsvd.singularValues()-singVals)>1e-13*singVals.norm()).transpose() << "\n"; -#endif -} - -template <typename MatrixType> -typename BDCSVD<MatrixType>::RealScalar BDCSVD<MatrixType>::secularEq(RealScalar mu, const ArrayRef& col0, const ArrayRef& diag, const IndicesRef &perm, const ArrayRef& diagShifted, RealScalar shift) -{ - Index m = perm.size(); - RealScalar res = Literal(1); - for(Index i=0; i<m; ++i) - { - Index j = perm(i); - // The following expression could be rewritten to involve only a single division, - // but this would make the expression more sensitive to overflow. - res += (col0(j) / (diagShifted(j) - mu)) * (col0(j) / (diag(j) + shift + mu)); - } - return res; - -} - -template <typename MatrixType> -void BDCSVD<MatrixType>::computeSingVals(const ArrayRef& col0, const ArrayRef& diag, const IndicesRef &perm, - VectorType& singVals, ArrayRef shifts, ArrayRef mus) -{ - using std::abs; - using std::swap; - using std::sqrt; - - Index n = col0.size(); - Index actual_n = n; - // Note that here actual_n is computed based on col0(i)==0 instead of diag(i)==0 as above - // because 1) we have diag(i)==0 => col0(i)==0 and 2) if col0(i)==0, then diag(i) is already a singular value. - while(actual_n>1 && col0(actual_n-1)==Literal(0)) --actual_n; - - for (Index k = 0; k < n; ++k) - { - if (col0(k) == Literal(0) || actual_n==1) - { - // if col0(k) == 0, then entry is deflated, so singular value is on diagonal - // if actual_n==1, then the deflated problem is already diagonalized - singVals(k) = k==0 ? col0(0) : diag(k); - mus(k) = Literal(0); - shifts(k) = k==0 ? col0(0) : diag(k); - continue; - } - - // otherwise, use secular equation to find singular value - RealScalar left = diag(k); - RealScalar right; // was: = (k != actual_n-1) ? diag(k+1) : (diag(actual_n-1) + col0.matrix().norm()); - if(k==actual_n-1) - right = (diag(actual_n-1) + col0.matrix().norm()); - else - { - // Skip deflated singular values, - // recall that at this stage we assume that z[j]!=0 and all entries for which z[j]==0 have been put aside. - // This should be equivalent to using perm[] - Index l = k+1; - while(col0(l)==Literal(0)) { ++l; eigen_internal_assert(l<actual_n); } - right = diag(l); - } - - // first decide whether it's closer to the left end or the right end - RealScalar mid = left + (right-left) / Literal(2); - RealScalar fMid = secularEq(mid, col0, diag, perm, diag, Literal(0)); -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << right-left << "\n"; - std::cout << "fMid = " << fMid << " " << secularEq(mid-left, col0, diag, perm, diag-left, left) << " " << secularEq(mid-right, col0, diag, perm, diag-right, right) << "\n"; - std::cout << " = " << secularEq(0.1*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.2*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.3*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.4*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.49*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.5*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.51*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.6*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.7*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.8*(left+right), col0, diag, perm, diag, 0) - << " " << secularEq(0.9*(left+right), col0, diag, perm, diag, 0) << "\n"; -#endif - RealScalar shift = (k == actual_n-1 || fMid > Literal(0)) ? left : right; - - // measure everything relative to shift - Map<ArrayXr> diagShifted(m_workspace.data()+4*n, n); - diagShifted = diag - shift; - - // initial guess - RealScalar muPrev, muCur; - if (shift == left) - { - muPrev = (right - left) * RealScalar(0.1); - if (k == actual_n-1) muCur = right - left; - else muCur = (right - left) * RealScalar(0.5); - } - else - { - muPrev = -(right - left) * RealScalar(0.1); - muCur = -(right - left) * RealScalar(0.5); - } - - RealScalar fPrev = secularEq(muPrev, col0, diag, perm, diagShifted, shift); - RealScalar fCur = secularEq(muCur, col0, diag, perm, diagShifted, shift); - if (abs(fPrev) < abs(fCur)) - { - swap(fPrev, fCur); - swap(muPrev, muCur); - } - - // rational interpolation: fit a function of the form a / mu + b through the two previous - // iterates and use its zero to compute the next iterate - bool useBisection = fPrev*fCur>Literal(0); - while (fCur!=Literal(0) && abs(muCur - muPrev) > Literal(8) * NumTraits<RealScalar>::epsilon() * numext::maxi<RealScalar>(abs(muCur), abs(muPrev)) && abs(fCur - fPrev)>NumTraits<RealScalar>::epsilon() && !useBisection) - { - ++m_numIters; - - // Find a and b such that the function f(mu) = a / mu + b matches the current and previous samples. - RealScalar a = (fCur - fPrev) / (Literal(1)/muCur - Literal(1)/muPrev); - RealScalar b = fCur - a / muCur; - // And find mu such that f(mu)==0: - RealScalar muZero = -a/b; - RealScalar fZero = secularEq(muZero, col0, diag, perm, diagShifted, shift); - - muPrev = muCur; - fPrev = fCur; - muCur = muZero; - fCur = fZero; - - - if (shift == left && (muCur < Literal(0) || muCur > right - left)) useBisection = true; - if (shift == right && (muCur < -(right - left) || muCur > Literal(0))) useBisection = true; - if (abs(fCur)>abs(fPrev)) useBisection = true; - } - - // fall back on bisection method if rational interpolation did not work - if (useBisection) - { -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "useBisection for k = " << k << ", actual_n = " << actual_n << "\n"; -#endif - RealScalar leftShifted, rightShifted; - if (shift == left) - { - // to avoid overflow, we must have mu > max(real_min, |z(k)|/sqrt(real_max)), - // the factor 2 is to be more conservative - leftShifted = numext::maxi<RealScalar>( (std::numeric_limits<RealScalar>::min)(), Literal(2) * abs(col0(k)) / sqrt((std::numeric_limits<RealScalar>::max)()) ); - - // check that we did it right: - eigen_internal_assert( (numext::isfinite)( (col0(k)/leftShifted)*(col0(k)/(diag(k)+shift+leftShifted)) ) ); - // I don't understand why the case k==0 would be special there: - // if (k == 0) rightShifted = right - left; else - rightShifted = (k==actual_n-1) ? right : ((right - left) * RealScalar(0.51)); // theoretically we can take 0.5, but let's be safe - } - else - { - leftShifted = -(right - left) * RealScalar(0.51); - if(k+1<n) - rightShifted = -numext::maxi<RealScalar>( (std::numeric_limits<RealScalar>::min)(), abs(col0(k+1)) / sqrt((std::numeric_limits<RealScalar>::max)()) ); - else - rightShifted = -(std::numeric_limits<RealScalar>::min)(); - } - - RealScalar fLeft = secularEq(leftShifted, col0, diag, perm, diagShifted, shift); - -#if defined EIGEN_INTERNAL_DEBUGGING || defined EIGEN_BDCSVD_DEBUG_VERBOSE - RealScalar fRight = secularEq(rightShifted, col0, diag, perm, diagShifted, shift); -#endif - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - if(!(fLeft * fRight<0)) - { - std::cout << "fLeft: " << leftShifted << " - " << diagShifted.head(10).transpose() << "\n ; " << bool(left==shift) << " " << (left-shift) << "\n"; - std::cout << k << " : " << fLeft << " * " << fRight << " == " << fLeft * fRight << " ; " << left << " - " << right << " -> " << leftShifted << " " << rightShifted << " shift=" << shift << "\n"; - } -#endif - eigen_internal_assert(fLeft * fRight < Literal(0)); - - while (rightShifted - leftShifted > Literal(2) * NumTraits<RealScalar>::epsilon() * numext::maxi<RealScalar>(abs(leftShifted), abs(rightShifted))) - { - RealScalar midShifted = (leftShifted + rightShifted) / Literal(2); - fMid = secularEq(midShifted, col0, diag, perm, diagShifted, shift); - if (fLeft * fMid < Literal(0)) - { - rightShifted = midShifted; - } - else - { - leftShifted = midShifted; - fLeft = fMid; - } - } - - muCur = (leftShifted + rightShifted) / Literal(2); - } - - singVals[k] = shift + muCur; - shifts[k] = shift; - mus[k] = muCur; - - // perturb singular value slightly if it equals diagonal entry to avoid division by zero later - // (deflation is supposed to avoid this from happening) - // - this does no seem to be necessary anymore - -// if (singVals[k] == left) singVals[k] *= 1 + NumTraits<RealScalar>::epsilon(); -// if (singVals[k] == right) singVals[k] *= 1 - NumTraits<RealScalar>::epsilon(); - } -} - - -// zhat is perturbation of col0 for which singular vectors can be computed stably (see Section 3.1) -template <typename MatrixType> -void BDCSVD<MatrixType>::perturbCol0 - (const ArrayRef& col0, const ArrayRef& diag, const IndicesRef &perm, const VectorType& singVals, - const ArrayRef& shifts, const ArrayRef& mus, ArrayRef zhat) -{ - using std::sqrt; - Index n = col0.size(); - Index m = perm.size(); - if(m==0) - { - zhat.setZero(); - return; - } - Index last = perm(m-1); - // The offset permits to skip deflated entries while computing zhat - for (Index k = 0; k < n; ++k) - { - if (col0(k) == Literal(0)) // deflated - zhat(k) = Literal(0); - else - { - // see equation (3.6) - RealScalar dk = diag(k); - RealScalar prod = (singVals(last) + dk) * (mus(last) + (shifts(last) - dk)); - - for(Index l = 0; l<m; ++l) - { - Index i = perm(l); - if(i!=k) - { - Index j = i<k ? i : perm(l-1); - prod *= ((singVals(j)+dk) / ((diag(i)+dk))) * ((mus(j)+(shifts(j)-dk)) / ((diag(i)-dk))); -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - if(i!=k && std::abs(((singVals(j)+dk)*(mus(j)+(shifts(j)-dk)))/((diag(i)+dk)*(diag(i)-dk)) - 1) > 0.9 ) - std::cout << " " << ((singVals(j)+dk)*(mus(j)+(shifts(j)-dk)))/((diag(i)+dk)*(diag(i)-dk)) << " == (" << (singVals(j)+dk) << " * " << (mus(j)+(shifts(j)-dk)) - << ") / (" << (diag(i)+dk) << " * " << (diag(i)-dk) << ")\n"; -#endif - } - } -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "zhat(" << k << ") = sqrt( " << prod << ") ; " << (singVals(last) + dk) << " * " << mus(last) + shifts(last) << " - " << dk << "\n"; -#endif - RealScalar tmp = sqrt(prod); - zhat(k) = col0(k) > Literal(0) ? tmp : -tmp; - } - } -} - -// compute singular vectors -template <typename MatrixType> -void BDCSVD<MatrixType>::computeSingVecs - (const ArrayRef& zhat, const ArrayRef& diag, const IndicesRef &perm, const VectorType& singVals, - const ArrayRef& shifts, const ArrayRef& mus, MatrixXr& U, MatrixXr& V) -{ - Index n = zhat.size(); - Index m = perm.size(); - - for (Index k = 0; k < n; ++k) - { - if (zhat(k) == Literal(0)) - { - U.col(k) = VectorType::Unit(n+1, k); - if (m_compV) V.col(k) = VectorType::Unit(n, k); - } - else - { - U.col(k).setZero(); - for(Index l=0;l<m;++l) - { - Index i = perm(l); - U(i,k) = zhat(i)/(((diag(i) - shifts(k)) - mus(k)) )/( (diag(i) + singVals[k])); - } - U(n,k) = Literal(0); - U.col(k).normalize(); - - if (m_compV) - { - V.col(k).setZero(); - for(Index l=1;l<m;++l) - { - Index i = perm(l); - V(i,k) = diag(i) * zhat(i) / (((diag(i) - shifts(k)) - mus(k)) )/( (diag(i) + singVals[k])); - } - V(0,k) = Literal(-1); - V.col(k).normalize(); - } - } - } - U.col(n) = VectorType::Unit(n+1, n); -} - - -// page 12_13 -// i >= 1, di almost null and zi non null. -// We use a rotation to zero out zi applied to the left of M -template <typename MatrixType> -void BDCSVD<MatrixType>::deflation43(Index firstCol, Index shift, Index i, Index size) -{ - using std::abs; - using std::sqrt; - using std::pow; - Index start = firstCol + shift; - RealScalar c = m_computed(start, start); - RealScalar s = m_computed(start+i, start); - RealScalar r = numext::hypot(c,s); - if (r == Literal(0)) - { - m_computed(start+i, start+i) = Literal(0); - return; - } - m_computed(start,start) = r; - m_computed(start+i, start) = Literal(0); - m_computed(start+i, start+i) = Literal(0); - - JacobiRotation<RealScalar> J(c/r,-s/r); - if (m_compU) m_naiveU.middleRows(firstCol, size+1).applyOnTheRight(firstCol, firstCol+i, J); - else m_naiveU.applyOnTheRight(firstCol, firstCol+i, J); -}// end deflation 43 - - -// page 13 -// i,j >= 1, i!=j and |di - dj| < epsilon * norm2(M) -// We apply two rotations to have zj = 0; -// TODO deflation44 is still broken and not properly tested -template <typename MatrixType> -void BDCSVD<MatrixType>::deflation44(Index firstColu , Index firstColm, Index firstRowW, Index firstColW, Index i, Index j, Index size) -{ - using std::abs; - using std::sqrt; - using std::conj; - using std::pow; - RealScalar c = m_computed(firstColm+i, firstColm); - RealScalar s = m_computed(firstColm+j, firstColm); - RealScalar r = sqrt(numext::abs2(c) + numext::abs2(s)); -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "deflation 4.4: " << i << "," << j << " -> " << c << " " << s << " " << r << " ; " - << m_computed(firstColm + i-1, firstColm) << " " - << m_computed(firstColm + i, firstColm) << " " - << m_computed(firstColm + i+1, firstColm) << " " - << m_computed(firstColm + i+2, firstColm) << "\n"; - std::cout << m_computed(firstColm + i-1, firstColm + i-1) << " " - << m_computed(firstColm + i, firstColm+i) << " " - << m_computed(firstColm + i+1, firstColm+i+1) << " " - << m_computed(firstColm + i+2, firstColm+i+2) << "\n"; -#endif - if (r==Literal(0)) - { - m_computed(firstColm + i, firstColm + i) = m_computed(firstColm + j, firstColm + j); - return; - } - c/=r; - s/=r; - m_computed(firstColm + i, firstColm) = r; - m_computed(firstColm + j, firstColm + j) = m_computed(firstColm + i, firstColm + i); - m_computed(firstColm + j, firstColm) = Literal(0); - - JacobiRotation<RealScalar> J(c,-s); - if (m_compU) m_naiveU.middleRows(firstColu, size+1).applyOnTheRight(firstColu + i, firstColu + j, J); - else m_naiveU.applyOnTheRight(firstColu+i, firstColu+j, J); - if (m_compV) m_naiveV.middleRows(firstRowW, size).applyOnTheRight(firstColW + i, firstColW + j, J); -}// end deflation 44 - - -// acts on block from (firstCol+shift, firstCol+shift) to (lastCol+shift, lastCol+shift) [inclusive] -template <typename MatrixType> -void BDCSVD<MatrixType>::deflation(Index firstCol, Index lastCol, Index k, Index firstRowW, Index firstColW, Index shift) -{ - using std::sqrt; - using std::abs; - const Index length = lastCol + 1 - firstCol; - - Block<MatrixXr,Dynamic,1> col0(m_computed, firstCol+shift, firstCol+shift, length, 1); - Diagonal<MatrixXr> fulldiag(m_computed); - VectorBlock<Diagonal<MatrixXr>,Dynamic> diag(fulldiag, firstCol+shift, length); - - const RealScalar considerZero = (std::numeric_limits<RealScalar>::min)(); - RealScalar maxDiag = diag.tail((std::max)(Index(1),length-1)).cwiseAbs().maxCoeff(); - RealScalar epsilon_strict = numext::maxi<RealScalar>(considerZero,NumTraits<RealScalar>::epsilon() * maxDiag); - RealScalar epsilon_coarse = Literal(8) * NumTraits<RealScalar>::epsilon() * numext::maxi<RealScalar>(col0.cwiseAbs().maxCoeff(), maxDiag); - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(m_naiveU.allFinite()); - assert(m_naiveV.allFinite()); - assert(m_computed.allFinite()); -#endif - -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "\ndeflate:" << diag.head(k+1).transpose() << " | " << diag.segment(k+1,length-k-1).transpose() << "\n"; -#endif - - //condition 4.1 - if (diag(0) < epsilon_coarse) - { -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "deflation 4.1, because " << diag(0) << " < " << epsilon_coarse << "\n"; -#endif - diag(0) = epsilon_coarse; - } - - //condition 4.2 - for (Index i=1;i<length;++i) - if (abs(col0(i)) < epsilon_strict) - { -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "deflation 4.2, set z(" << i << ") to zero because " << abs(col0(i)) << " < " << epsilon_strict << " (diag(" << i << ")=" << diag(i) << ")\n"; -#endif - col0(i) = Literal(0); - } - - //condition 4.3 - for (Index i=1;i<length; i++) - if (diag(i) < epsilon_coarse) - { -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "deflation 4.3, cancel z(" << i << ")=" << col0(i) << " because diag(" << i << ")=" << diag(i) << " < " << epsilon_coarse << "\n"; -#endif - deflation43(firstCol, shift, i, length); - } - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(m_naiveU.allFinite()); - assert(m_naiveV.allFinite()); - assert(m_computed.allFinite()); -#endif -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "to be sorted: " << diag.transpose() << "\n\n"; -#endif - { - // Check for total deflation - // If we have a total deflation, then we have to consider col0(0)==diag(0) as a singular value during sorting - bool total_deflation = (col0.tail(length-1).array()<considerZero).all(); - - // Sort the diagonal entries, since diag(1:k-1) and diag(k:length) are already sorted, let's do a sorted merge. - // First, compute the respective permutation. - Index *permutation = m_workspaceI.data(); - { - permutation[0] = 0; - Index p = 1; - - // Move deflated diagonal entries at the end. - for(Index i=1; i<length; ++i) - if(abs(diag(i))<considerZero) - permutation[p++] = i; - - Index i=1, j=k+1; - for( ; p < length; ++p) - { - if (i > k) permutation[p] = j++; - else if (j >= length) permutation[p] = i++; - else if (diag(i) < diag(j)) permutation[p] = j++; - else permutation[p] = i++; - } - } - - // If we have a total deflation, then we have to insert diag(0) at the right place - if(total_deflation) - { - for(Index i=1; i<length; ++i) - { - Index pi = permutation[i]; - if(abs(diag(pi))<considerZero || diag(0)<diag(pi)) - permutation[i-1] = permutation[i]; - else - { - permutation[i-1] = 0; - break; - } - } - } - - // Current index of each col, and current column of each index - Index *realInd = m_workspaceI.data()+length; - Index *realCol = m_workspaceI.data()+2*length; - - for(int pos = 0; pos< length; pos++) - { - realCol[pos] = pos; - realInd[pos] = pos; - } - - for(Index i = total_deflation?0:1; i < length; i++) - { - const Index pi = permutation[length - (total_deflation ? i+1 : i)]; - const Index J = realCol[pi]; - - using std::swap; - // swap diagonal and first column entries: - swap(diag(i), diag(J)); - if(i!=0 && J!=0) swap(col0(i), col0(J)); - - // change columns - if (m_compU) m_naiveU.col(firstCol+i).segment(firstCol, length + 1).swap(m_naiveU.col(firstCol+J).segment(firstCol, length + 1)); - else m_naiveU.col(firstCol+i).segment(0, 2) .swap(m_naiveU.col(firstCol+J).segment(0, 2)); - if (m_compV) m_naiveV.col(firstColW + i).segment(firstRowW, length).swap(m_naiveV.col(firstColW + J).segment(firstRowW, length)); - - //update real pos - const Index realI = realInd[i]; - realCol[realI] = J; - realCol[pi] = i; - realInd[J] = realI; - realInd[i] = pi; - } - } -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "sorted: " << diag.transpose().format(bdcsvdfmt) << "\n"; - std::cout << " : " << col0.transpose() << "\n\n"; -#endif - - //condition 4.4 - { - Index i = length-1; - while(i>0 && (abs(diag(i))<considerZero || abs(col0(i))<considerZero)) --i; - for(; i>1;--i) - if( (diag(i) - diag(i-1)) < NumTraits<RealScalar>::epsilon()*maxDiag ) - { -#ifdef EIGEN_BDCSVD_DEBUG_VERBOSE - std::cout << "deflation 4.4 with i = " << i << " because " << (diag(i) - diag(i-1)) << " < " << NumTraits<RealScalar>::epsilon()*diag(i) << "\n"; -#endif - eigen_internal_assert(abs(diag(i) - diag(i-1))<epsilon_coarse && " diagonal entries are not properly sorted"); - deflation44(firstCol, firstCol + shift, firstRowW, firstColW, i-1, i, length); - } - } - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - for(Index j=2;j<length;++j) - assert(diag(j-1)<=diag(j) || abs(diag(j))<considerZero); -#endif - -#ifdef EIGEN_BDCSVD_SANITY_CHECKS - assert(m_naiveU.allFinite()); - assert(m_naiveV.allFinite()); - assert(m_computed.allFinite()); -#endif -}//end deflation - -#ifndef __CUDACC__ -/** \svd_module - * - * \return the singular value decomposition of \c *this computed by Divide & Conquer algorithm - * - * \sa class BDCSVD - */ -template<typename Derived> -BDCSVD<typename MatrixBase<Derived>::PlainObject> -MatrixBase<Derived>::bdcSvd(unsigned int computationOptions) const -{ - return BDCSVD<PlainObject>(*this, computationOptions); -} -#endif - -} // end namespace Eigen - -#endif diff --git a/eigen/Eigen/src/SVD/JacobiSVD.h b/eigen/Eigen/src/SVD/JacobiSVD.h deleted file mode 100644 index 43488b1..0000000 --- a/eigen/Eigen/src/SVD/JacobiSVD.h +++ /dev/null @@ -1,804 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com> -// Copyright (C) 2013-2014 Gael Guennebaud <gael.guennebaud@inria.fr> -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef EIGEN_JACOBISVD_H -#define EIGEN_JACOBISVD_H - -namespace Eigen { - -namespace internal { -// forward declaration (needed by ICC) -// the empty body is required by MSVC -template<typename MatrixType, int QRPreconditioner, - bool IsComplex = NumTraits<typename MatrixType::Scalar>::IsComplex> -struct svd_precondition_2x2_block_to_be_real {}; - -/*** QR preconditioners (R-SVD) - *** - *** Their role is to reduce the problem of computing the SVD to the case of a square matrix. - *** This approach, known as R-SVD, is an optimization for rectangular-enough matrices, and is a requirement for - *** JacobiSVD which by itself is only able to work on square matrices. - ***/ - -enum { PreconditionIfMoreColsThanRows, PreconditionIfMoreRowsThanCols }; - -template<typename MatrixType, int QRPreconditioner, int Case> -struct qr_preconditioner_should_do_anything -{ - enum { a = MatrixType::RowsAtCompileTime != Dynamic && - MatrixType::ColsAtCompileTime != Dynamic && - MatrixType::ColsAtCompileTime <= MatrixType::RowsAtCompileTime, - b = MatrixType::RowsAtCompileTime != Dynamic && - MatrixType::ColsAtCompileTime != Dynamic && - MatrixType::RowsAtCompileTime <= MatrixType::ColsAtCompileTime, - ret = !( (QRPreconditioner == NoQRPreconditioner) || - (Case == PreconditionIfMoreColsThanRows && bool(a)) || - (Case == PreconditionIfMoreRowsThanCols && bool(b)) ) - }; -}; - -template<typename MatrixType, int QRPreconditioner, int Case, - bool DoAnything = qr_preconditioner_should_do_anything<MatrixType, QRPreconditioner, Case>::ret -> struct qr_preconditioner_impl {}; - -template<typename MatrixType, int QRPreconditioner, int Case> -class qr_preconditioner_impl<MatrixType, QRPreconditioner, Case, false> -{ -public: - void allocate(const JacobiSVD<MatrixType, QRPreconditioner>&) {} - bool run(JacobiSVD<MatrixType, QRPreconditioner>&, const MatrixType&) - { - return false; - } -}; - -/*** preconditioner using FullPivHouseholderQR ***/ - -template<typename MatrixType> -class qr_preconditioner_impl<MatrixType, FullPivHouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true> -{ -public: - typedef typename MatrixType::Scalar Scalar; - enum - { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime - }; - typedef Matrix<Scalar, 1, RowsAtCompileTime, RowMajor, 1, MaxRowsAtCompileTime> WorkspaceType; - - void allocate(const JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd) - { - if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols()) - { - m_qr.~QRType(); - ::new (&m_qr) QRType(svd.rows(), svd.cols()); - } - if (svd.m_computeFullU) m_workspace.resize(svd.rows()); - } - - bool run(JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix) - { - if(matrix.rows() > matrix.cols()) - { - m_qr.compute(matrix); - svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>(); - if(svd.m_computeFullU) m_qr.matrixQ().evalTo(svd.m_matrixU, m_workspace); - if(svd.computeV()) svd.m_matrixV = m_qr.colsPermutation(); - return true; - } - return false; - } -private: - typedef FullPivHouseholderQR<MatrixType> QRType; - QRType m_qr; - WorkspaceType m_workspace; -}; - -template<typename MatrixType> -class qr_preconditioner_impl<MatrixType, FullPivHouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true> -{ -public: - typedef typename MatrixType::Scalar Scalar; - enum - { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - ColsAtCompileTime = MatrixType::ColsAtCompileTime, - MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, - MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, - TrOptions = RowsAtCompileTime==1 ? (MatrixType::Options & ~(RowMajor)) - : ColsAtCompileTime==1 ? (MatrixType::Options | RowMajor) - : MatrixType::Options - }; - typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, TrOptions, MaxColsAtCompileTime, MaxRowsAtCompileTime> - TransposeTypeWithSameStorageOrder; - - void allocate(const JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd) - { - if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols()) - { - m_qr.~QRType(); - ::new (&m_qr) QRType(svd.cols(), svd.rows()); - } - m_adjoint.resize(svd.cols(), svd.rows()); - if (svd.m_computeFullV) m_workspace.resize(svd.cols()); - } - - bool run(JacobiSVD<MatrixType, FullPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix) - { - if(matrix.cols() > matrix.rows()) - { - m_adjoint = matrix.adjoint(); - m_qr.compute(m_adjoint); - svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint(); - if(svd.m_computeFullV) m_qr.matrixQ().evalTo(svd.m_matrixV, m_workspace); - if(svd.computeU()) svd.m_matrixU = m_qr.colsPermutation(); - return true; - } - else return false; - } -private: - typedef FullPivHouseholderQR<TransposeTypeWithSameStorageOrder> QRType; - QRType m_qr; - TransposeTypeWithSameStorageOrder m_adjoint; - typename internal::plain_row_type<MatrixType>::type m_workspace; -}; - -/*** preconditioner using ColPivHouseholderQR ***/ - -template<typename MatrixType> -class qr_preconditioner_impl<MatrixType, ColPivHouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true> -{ -public: - void allocate(const JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd) - { - if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols()) - { - m_qr.~QRType(); - ::new (&m_qr) QRType(svd.rows(), svd.cols()); - } - if (svd.m_computeFullU) m_workspace.resize(svd.rows()); - else if (svd.m_computeThinU) m_workspace.resize(svd.cols()); - } - - bool run(JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix) - { - if(matrix.rows() > matrix.cols()) - { - m_qr.compute(matrix); - svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>(); - if(svd.m_computeFullU) m_qr.householderQ().evalTo(svd.m_matrixU, m_workspace); - else if(svd.m_computeThinU) - { - svd.m_matrixU.setIdentity(matrix.rows(), matrix.cols()); - m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixU, m_workspace); - } - if(svd.computeV()) svd.m_matrixV = m_qr.colsPermutation(); - return true; - } - return false; - } - -private: - typedef ColPivHouseholderQR<MatrixType> QRType; - QRType m_qr; - typename internal::plain_col_type<MatrixType>::type m_workspace; -}; - -template<typename MatrixType> -class qr_preconditioner_impl<MatrixType, ColPivHouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true> -{ -public: - typedef typename MatrixType::Scalar Scalar; - enum - { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - ColsAtCompileTime = MatrixType::ColsAtCompileTime, - MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, - MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, - TrOptions = RowsAtCompileTime==1 ? (MatrixType::Options & ~(RowMajor)) - : ColsAtCompileTime==1 ? (MatrixType::Options | RowMajor) - : MatrixType::Options - }; - - typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, TrOptions, MaxColsAtCompileTime, MaxRowsAtCompileTime> - TransposeTypeWithSameStorageOrder; - - void allocate(const JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd) - { - if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols()) - { - m_qr.~QRType(); - ::new (&m_qr) QRType(svd.cols(), svd.rows()); - } - if (svd.m_computeFullV) m_workspace.resize(svd.cols()); - else if (svd.m_computeThinV) m_workspace.resize(svd.rows()); - m_adjoint.resize(svd.cols(), svd.rows()); - } - - bool run(JacobiSVD<MatrixType, ColPivHouseholderQRPreconditioner>& svd, const MatrixType& matrix) - { - if(matrix.cols() > matrix.rows()) - { - m_adjoint = matrix.adjoint(); - m_qr.compute(m_adjoint); - - svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint(); - if(svd.m_computeFullV) m_qr.householderQ().evalTo(svd.m_matrixV, m_workspace); - else if(svd.m_computeThinV) - { - svd.m_matrixV.setIdentity(matrix.cols(), matrix.rows()); - m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixV, m_workspace); - } - if(svd.computeU()) svd.m_matrixU = m_qr.colsPermutation(); - return true; - } - else return false; - } - -private: - typedef ColPivHouseholderQR<TransposeTypeWithSameStorageOrder> QRType; - QRType m_qr; - TransposeTypeWithSameStorageOrder m_adjoint; - typename internal::plain_row_type<MatrixType>::type m_workspace; -}; - -/*** preconditioner using HouseholderQR ***/ - -template<typename MatrixType> -class qr_preconditioner_impl<MatrixType, HouseholderQRPreconditioner, PreconditionIfMoreRowsThanCols, true> -{ -public: - void allocate(const JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd) - { - if (svd.rows() != m_qr.rows() || svd.cols() != m_qr.cols()) - { - m_qr.~QRType(); - ::new (&m_qr) QRType(svd.rows(), svd.cols()); - } - if (svd.m_computeFullU) m_workspace.resize(svd.rows()); - else if (svd.m_computeThinU) m_workspace.resize(svd.cols()); - } - - bool run(JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd, const MatrixType& matrix) - { - if(matrix.rows() > matrix.cols()) - { - m_qr.compute(matrix); - svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.cols(),matrix.cols()).template triangularView<Upper>(); - if(svd.m_computeFullU) m_qr.householderQ().evalTo(svd.m_matrixU, m_workspace); - else if(svd.m_computeThinU) - { - svd.m_matrixU.setIdentity(matrix.rows(), matrix.cols()); - m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixU, m_workspace); - } - if(svd.computeV()) svd.m_matrixV.setIdentity(matrix.cols(), matrix.cols()); - return true; - } - return false; - } -private: - typedef HouseholderQR<MatrixType> QRType; - QRType m_qr; - typename internal::plain_col_type<MatrixType>::type m_workspace; -}; - -template<typename MatrixType> -class qr_preconditioner_impl<MatrixType, HouseholderQRPreconditioner, PreconditionIfMoreColsThanRows, true> -{ -public: - typedef typename MatrixType::Scalar Scalar; - enum - { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - ColsAtCompileTime = MatrixType::ColsAtCompileTime, - MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, - MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, - Options = MatrixType::Options - }; - - typedef Matrix<Scalar, ColsAtCompileTime, RowsAtCompileTime, Options, MaxColsAtCompileTime, MaxRowsAtCompileTime> - TransposeTypeWithSameStorageOrder; - - void allocate(const JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd) - { - if (svd.cols() != m_qr.rows() || svd.rows() != m_qr.cols()) - { - m_qr.~QRType(); - ::new (&m_qr) QRType(svd.cols(), svd.rows()); - } - if (svd.m_computeFullV) m_workspace.resize(svd.cols()); - else if (svd.m_computeThinV) m_workspace.resize(svd.rows()); - m_adjoint.resize(svd.cols(), svd.rows()); - } - - bool run(JacobiSVD<MatrixType, HouseholderQRPreconditioner>& svd, const MatrixType& matrix) - { - if(matrix.cols() > matrix.rows()) - { - m_adjoint = matrix.adjoint(); - m_qr.compute(m_adjoint); - - svd.m_workMatrix = m_qr.matrixQR().block(0,0,matrix.rows(),matrix.rows()).template triangularView<Upper>().adjoint(); - if(svd.m_computeFullV) m_qr.householderQ().evalTo(svd.m_matrixV, m_workspace); - else if(svd.m_computeThinV) - { - svd.m_matrixV.setIdentity(matrix.cols(), matrix.rows()); - m_qr.householderQ().applyThisOnTheLeft(svd.m_matrixV, m_workspace); - } - if(svd.computeU()) svd.m_matrixU.setIdentity(matrix.rows(), matrix.rows()); - return true; - } - else return false; - } - -private: - typedef HouseholderQR<TransposeTypeWithSameStorageOrder> QRType; - QRType m_qr; - TransposeTypeWithSameStorageOrder m_adjoint; - typename internal::plain_row_type<MatrixType>::type m_workspace; -}; - -/*** 2x2 SVD implementation - *** - *** JacobiSVD consists in performing a series of 2x2 SVD subproblems - ***/ - -template<typename MatrixType, int QRPreconditioner> -struct svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner, false> -{ - typedef JacobiSVD<MatrixType, QRPreconditioner> SVD; - typedef typename MatrixType::RealScalar RealScalar; - static bool run(typename SVD::WorkMatrixType&, SVD&, Index, Index, RealScalar&) { return true; } -}; - -template<typename MatrixType, int QRPreconditioner> -struct svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner, true> -{ - typedef JacobiSVD<MatrixType, QRPreconditioner> SVD; - typedef typename MatrixType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; - static bool run(typename SVD::WorkMatrixType& work_matrix, SVD& svd, Index p, Index q, RealScalar& maxDiagEntry) - { - using std::sqrt; - using std::abs; - Scalar z; - JacobiRotation<Scalar> rot; - RealScalar n = sqrt(numext::abs2(work_matrix.coeff(p,p)) + numext::abs2(work_matrix.coeff(q,p))); - - const RealScalar considerAsZero = (std::numeric_limits<RealScalar>::min)(); - const RealScalar precision = NumTraits<Scalar>::epsilon(); - - if(n==0) - { - // make sure first column is zero - work_matrix.coeffRef(p,p) = work_matrix.coeffRef(q,p) = Scalar(0); - - if(abs(numext::imag(work_matrix.coeff(p,q)))>considerAsZero) - { - // work_matrix.coeff(p,q) can be zero if work_matrix.coeff(q,p) is not zero but small enough to underflow when computing n - z = abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q); - work_matrix.row(p) *= z; - if(svd.computeU()) svd.m_matrixU.col(p) *= conj(z); - } - if(abs(numext::imag(work_matrix.coeff(q,q)))>considerAsZero) - { - z = abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q); - work_matrix.row(q) *= z; - if(svd.computeU()) svd.m_matrixU.col(q) *= conj(z); - } - // otherwise the second row is already zero, so we have nothing to do. - } - else - { - rot.c() = conj(work_matrix.coeff(p,p)) / n; - rot.s() = work_matrix.coeff(q,p) / n; - work_matrix.applyOnTheLeft(p,q,rot); - if(svd.computeU()) svd.m_matrixU.applyOnTheRight(p,q,rot.adjoint()); - if(abs(numext::imag(work_matrix.coeff(p,q)))>considerAsZero) - { - z = abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q); - work_matrix.col(q) *= z; - if(svd.computeV()) svd.m_matrixV.col(q) *= z; - } - if(abs(numext::imag(work_matrix.coeff(q,q)))>considerAsZero) - { - z = abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q); - work_matrix.row(q) *= z; - if(svd.computeU()) svd.m_matrixU.col(q) *= conj(z); - } - } - - // update largest diagonal entry - maxDiagEntry = numext::maxi<RealScalar>(maxDiagEntry,numext::maxi<RealScalar>(abs(work_matrix.coeff(p,p)), abs(work_matrix.coeff(q,q)))); - // and check whether the 2x2 block is already diagonal - RealScalar threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry); - return abs(work_matrix.coeff(p,q))>threshold || abs(work_matrix.coeff(q,p)) > threshold; - } -}; - -template<typename _MatrixType, int QRPreconditioner> -struct traits<JacobiSVD<_MatrixType,QRPreconditioner> > -{ - typedef _MatrixType MatrixType; -}; - -} // end namespace internal - -/** \ingroup SVD_Module - * - * - * \class JacobiSVD - * - * \brief Two-sided Jacobi SVD decomposition of a rectangular matrix - * - * \tparam _MatrixType the type of the matrix of which we are computing the SVD decomposition - * \tparam QRPreconditioner this optional parameter allows to specify the type of QR decomposition that will be used internally - * for the R-SVD step for non-square matrices. See discussion of possible values below. - * - * SVD decomposition consists in decomposing any n-by-p matrix \a A as a product - * \f[ A = U S V^* \f] - * where \a U is a n-by-n unitary, \a V is a p-by-p unitary, and \a S is a n-by-p real positive matrix which is zero outside of its main diagonal; - * the diagonal entries of S are known as the \em singular \em values of \a A and the columns of \a U and \a V are known as the left - * and right \em singular \em vectors of \a A respectively. - * - * Singular values are always sorted in decreasing order. - * - * This JacobiSVD decomposition computes only the singular values by default. If you want \a U or \a V, you need to ask for them explicitly. - * - * You can ask for only \em thin \a U or \a V to be computed, meaning the following. In case of a rectangular n-by-p matrix, letting \a m be the - * smaller value among \a n and \a p, there are only \a m singular vectors; the remaining columns of \a U and \a V do not correspond to actual - * singular vectors. Asking for \em thin \a U or \a V means asking for only their \a m first columns to be formed. So \a U is then a n-by-m matrix, - * and \a V is then a p-by-m matrix. Notice that thin \a U and \a V are all you need for (least squares) solving. - * - * Here's an example demonstrating basic usage: - * \include JacobiSVD_basic.cpp - * Output: \verbinclude JacobiSVD_basic.out - * - * This JacobiSVD class is a two-sided Jacobi R-SVD decomposition, ensuring optimal reliability and accuracy. The downside is that it's slower than - * bidiagonalizing SVD algorithms for large square matrices; however its complexity is still \f$ O(n^2p) \f$ where \a n is the smaller dimension and - * \a p is the greater dimension, meaning that it is still of the same order of complexity as the faster bidiagonalizing R-SVD algorithms. - * In particular, like any R-SVD, it takes advantage of non-squareness in that its complexity is only linear in the greater dimension. - * - * If the input matrix has inf or nan coefficients, the result of the computation is undefined, but the computation is guaranteed to - * terminate in finite (and reasonable) time. - * - * The possible values for QRPreconditioner are: - * \li ColPivHouseholderQRPreconditioner is the default. In practice it's very safe. It uses column-pivoting QR. - * \li FullPivHouseholderQRPreconditioner, is the safest and slowest. It uses full-pivoting QR. - * Contrary to other QRs, it doesn't allow computing thin unitaries. - * \li HouseholderQRPreconditioner is the fastest, and less safe and accurate than the pivoting variants. It uses non-pivoting QR. - * This is very similar in safety and accuracy to the bidiagonalization process used by bidiagonalizing SVD algorithms (since bidiagonalization - * is inherently non-pivoting). However the resulting SVD is still more reliable than bidiagonalizing SVDs because the Jacobi-based iterarive - * process is more reliable than the optimized bidiagonal SVD iterations. - * \li NoQRPreconditioner allows not to use a QR preconditioner at all. This is useful if you know that you will only be computing - * JacobiSVD decompositions of square matrices. Non-square matrices require a QR preconditioner. Using this option will result in - * faster compilation and smaller executable code. It won't significantly speed up computation, since JacobiSVD is always checking - * if QR preconditioning is needed before applying it anyway. - * - * \sa MatrixBase::jacobiSvd() - */ -template<typename _MatrixType, int QRPreconditioner> class JacobiSVD - : public SVDBase<JacobiSVD<_MatrixType,QRPreconditioner> > -{ - typedef SVDBase<JacobiSVD> Base; - public: - - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; - enum { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - ColsAtCompileTime = MatrixType::ColsAtCompileTime, - DiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(RowsAtCompileTime,ColsAtCompileTime), - MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, - MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, - MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_FIXED(MaxRowsAtCompileTime,MaxColsAtCompileTime), - MatrixOptions = MatrixType::Options - }; - - typedef typename Base::MatrixUType MatrixUType; - typedef typename Base::MatrixVType MatrixVType; - typedef typename Base::SingularValuesType SingularValuesType; - - typedef typename internal::plain_row_type<MatrixType>::type RowType; - typedef typename internal::plain_col_type<MatrixType>::type ColType; - typedef Matrix<Scalar, DiagSizeAtCompileTime, DiagSizeAtCompileTime, - MatrixOptions, MaxDiagSizeAtCompileTime, MaxDiagSizeAtCompileTime> - WorkMatrixType; - - /** \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via JacobiSVD::compute(const MatrixType&). - */ - JacobiSVD() - {} - - - /** \brief Default Constructor with memory preallocation - * - * Like the default constructor but with preallocation of the internal data - * according to the specified problem size. - * \sa JacobiSVD() - */ - JacobiSVD(Index rows, Index cols, unsigned int computationOptions = 0) - { - allocate(rows, cols, computationOptions); - } - - /** \brief Constructor performing the decomposition of given matrix. - * - * \param matrix the matrix to decompose - * \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed. - * By default, none is computed. This is a bit-field, the possible bits are #ComputeFullU, #ComputeThinU, - * #ComputeFullV, #ComputeThinV. - * - * Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not - * available with the (non-default) FullPivHouseholderQR preconditioner. - */ - explicit JacobiSVD(const MatrixType& matrix, unsigned int computationOptions = 0) - { - compute(matrix, computationOptions); - } - - /** \brief Method performing the decomposition of given matrix using custom options. - * - * \param matrix the matrix to decompose - * \param computationOptions optional parameter allowing to specify if you want full or thin U or V unitaries to be computed. - * By default, none is computed. This is a bit-field, the possible bits are #ComputeFullU, #ComputeThinU, - * #ComputeFullV, #ComputeThinV. - * - * Thin unitaries are only available if your matrix type has a Dynamic number of columns (for example MatrixXf). They also are not - * available with the (non-default) FullPivHouseholderQR preconditioner. - */ - JacobiSVD& compute(const MatrixType& matrix, unsigned int computationOptions); - - /** \brief Method performing the decomposition of given matrix using current options. - * - * \param matrix the matrix to decompose - * - * This method uses the current \a computationOptions, as already passed to the constructor or to compute(const MatrixType&, unsigned int). - */ - JacobiSVD& compute(const MatrixType& matrix) - { - return compute(matrix, m_computationOptions); - } - - using Base::computeU; - using Base::computeV; - using Base::rows; - using Base::cols; - using Base::rank; - - private: - void allocate(Index rows, Index cols, unsigned int computationOptions); - - protected: - using Base::m_matrixU; - using Base::m_matrixV; - using Base::m_singularValues; - using Base::m_isInitialized; - using Base::m_isAllocated; - using Base::m_usePrescribedThreshold; - using Base::m_computeFullU; - using Base::m_computeThinU; - using Base::m_computeFullV; - using Base::m_computeThinV; - using Base::m_computationOptions; - using Base::m_nonzeroSingularValues; - using Base::m_rows; - using Base::m_cols; - using Base::m_diagSize; - using Base::m_prescribedThreshold; - WorkMatrixType m_workMatrix; - - template<typename __MatrixType, int _QRPreconditioner, bool _IsComplex> - friend struct internal::svd_precondition_2x2_block_to_be_real; - template<typename __MatrixType, int _QRPreconditioner, int _Case, bool _DoAnything> - friend struct internal::qr_preconditioner_impl; - - internal::qr_preconditioner_impl<MatrixType, QRPreconditioner, internal::PreconditionIfMoreColsThanRows> m_qr_precond_morecols; - internal::qr_preconditioner_impl<MatrixType, QRPreconditioner, internal::PreconditionIfMoreRowsThanCols> m_qr_precond_morerows; - MatrixType m_scaledMatrix; -}; - -template<typename MatrixType, int QRPreconditioner> -void JacobiSVD<MatrixType, QRPreconditioner>::allocate(Index rows, Index cols, unsigned int computationOptions) -{ - eigen_assert(rows >= 0 && cols >= 0); - - if (m_isAllocated && - rows == m_rows && - cols == m_cols && - computationOptions == m_computationOptions) - { - return; - } - - m_rows = rows; - m_cols = cols; - m_isInitialized = false; - m_isAllocated = true; - m_computationOptions = computationOptions; - m_computeFullU = (computationOptions & ComputeFullU) != 0; - m_computeThinU = (computationOptions & ComputeThinU) != 0; - m_computeFullV = (computationOptions & ComputeFullV) != 0; - m_computeThinV = (computationOptions & ComputeThinV) != 0; - eigen_assert(!(m_computeFullU && m_computeThinU) && "JacobiSVD: you can't ask for both full and thin U"); - eigen_assert(!(m_computeFullV && m_computeThinV) && "JacobiSVD: you can't ask for both full and thin V"); - eigen_assert(EIGEN_IMPLIES(m_computeThinU || m_computeThinV, MatrixType::ColsAtCompileTime==Dynamic) && - "JacobiSVD: thin U and V are only available when your matrix has a dynamic number of columns."); - if (QRPreconditioner == FullPivHouseholderQRPreconditioner) - { - eigen_assert(!(m_computeThinU || m_computeThinV) && - "JacobiSVD: can't compute thin U or thin V with the FullPivHouseholderQR preconditioner. " - "Use the ColPivHouseholderQR preconditioner instead."); - } - m_diagSize = (std::min)(m_rows, m_cols); - m_singularValues.resize(m_diagSize); - if(RowsAtCompileTime==Dynamic) - m_matrixU.resize(m_rows, m_computeFullU ? m_rows - : m_computeThinU ? m_diagSize - : 0); - if(ColsAtCompileTime==Dynamic) - m_matrixV.resize(m_cols, m_computeFullV ? m_cols - : m_computeThinV ? m_diagSize - : 0); - m_workMatrix.resize(m_diagSize, m_diagSize); - - if(m_cols>m_rows) m_qr_precond_morecols.allocate(*this); - if(m_rows>m_cols) m_qr_precond_morerows.allocate(*this); - if(m_rows!=m_cols) m_scaledMatrix.resize(rows,cols); -} - -template<typename MatrixType, int QRPreconditioner> -JacobiSVD<MatrixType, QRPreconditioner>& -JacobiSVD<MatrixType, QRPreconditioner>::compute(const MatrixType& matrix, unsigned int computationOptions) -{ - using std::abs; - allocate(matrix.rows(), matrix.cols(), computationOptions); - - // currently we stop when we reach precision 2*epsilon as the last bit of precision can require an unreasonable number of iterations, - // only worsening the precision of U and V as we accumulate more rotations - const RealScalar precision = RealScalar(2) * NumTraits<Scalar>::epsilon(); - - // limit for denormal numbers to be considered zero in order to avoid infinite loops (see bug 286) - const RealScalar considerAsZero = (std::numeric_limits<RealScalar>::min)(); - - // Scaling factor to reduce over/under-flows - RealScalar scale = matrix.cwiseAbs().maxCoeff(); - if(scale==RealScalar(0)) scale = RealScalar(1); - - /*** step 1. The R-SVD step: we use a QR decomposition to reduce to the case of a square matrix */ - - if(m_rows!=m_cols) - { - m_scaledMatrix = matrix / scale; - m_qr_precond_morecols.run(*this, m_scaledMatrix); - m_qr_precond_morerows.run(*this, m_scaledMatrix); - } - else - { - m_workMatrix = matrix.block(0,0,m_diagSize,m_diagSize) / scale; - if(m_computeFullU) m_matrixU.setIdentity(m_rows,m_rows); - if(m_computeThinU) m_matrixU.setIdentity(m_rows,m_diagSize); - if(m_computeFullV) m_matrixV.setIdentity(m_cols,m_cols); - if(m_computeThinV) m_matrixV.setIdentity(m_cols, m_diagSize); - } - - /*** step 2. The main Jacobi SVD iteration. ***/ - RealScalar maxDiagEntry = m_workMatrix.cwiseAbs().diagonal().maxCoeff(); - - bool finished = false; - while(!finished) - { - finished = true; - - // do a sweep: for all index pairs (p,q), perform SVD of the corresponding 2x2 sub-matrix - - for(Index p = 1; p < m_diagSize; ++p) - { - for(Index q = 0; q < p; ++q) - { - // if this 2x2 sub-matrix is not diagonal already... - // notice that this comparison will evaluate to false if any NaN is involved, ensuring that NaN's don't - // keep us iterating forever. Similarly, small denormal numbers are considered zero. - RealScalar threshold = numext::maxi<RealScalar>(considerAsZero, precision * maxDiagEntry); - if(abs(m_workMatrix.coeff(p,q))>threshold || abs(m_workMatrix.coeff(q,p)) > threshold) - { - finished = false; - // perform SVD decomposition of 2x2 sub-matrix corresponding to indices p,q to make it diagonal - // the complex to real operation returns true if the updated 2x2 block is not already diagonal - if(internal::svd_precondition_2x2_block_to_be_real<MatrixType, QRPreconditioner>::run(m_workMatrix, *this, p, q, maxDiagEntry)) - { - JacobiRotation<RealScalar> j_left, j_right; - internal::real_2x2_jacobi_svd(m_workMatrix, p, q, &j_left, &j_right); - - // accumulate resulting Jacobi rotations - m_workMatrix.applyOnTheLeft(p,q,j_left); - if(computeU()) m_matrixU.applyOnTheRight(p,q,j_left.transpose()); - - m_workMatrix.applyOnTheRight(p,q,j_right); - if(computeV()) m_matrixV.applyOnTheRight(p,q,j_right); - - // keep track of the largest diagonal coefficient - maxDiagEntry = numext::maxi<RealScalar>(maxDiagEntry,numext::maxi<RealScalar>(abs(m_workMatrix.coeff(p,p)), abs(m_workMatrix.coeff(q,q)))); - } - } - } - } - } - - /*** step 3. The work matrix is now diagonal, so ensure it's positive so its diagonal entries are the singular values ***/ - - for(Index i = 0; i < m_diagSize; ++i) - { - // For a complex matrix, some diagonal coefficients might note have been - // treated by svd_precondition_2x2_block_to_be_real, and the imaginary part - // of some diagonal entry might not be null. - if(NumTraits<Scalar>::IsComplex && abs(numext::imag(m_workMatrix.coeff(i,i)))>considerAsZero) - { - RealScalar a = abs(m_workMatrix.coeff(i,i)); - m_singularValues.coeffRef(i) = abs(a); - if(computeU()) m_matrixU.col(i) *= m_workMatrix.coeff(i,i)/a; - } - else - { - // m_workMatrix.coeff(i,i) is already real, no difficulty: - RealScalar a = numext::real(m_workMatrix.coeff(i,i)); - m_singularValues.coeffRef(i) = abs(a); - if(computeU() && (a<RealScalar(0))) m_matrixU.col(i) = -m_matrixU.col(i); - } - } - - m_singularValues *= scale; - - /*** step 4. Sort singular values in descending order and compute the number of nonzero singular values ***/ - - m_nonzeroSingularValues = m_diagSize; - for(Index i = 0; i < m_diagSize; i++) - { - Index pos; - RealScalar maxRemainingSingularValue = m_singularValues.tail(m_diagSize-i).maxCoeff(&pos); - if(maxRemainingSingularValue == RealScalar(0)) - { - m_nonzeroSingularValues = i; - break; - } - 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; -} - -/** \svd_module - * - * \return the singular value decomposition of \c *this computed by two-sided - * Jacobi transformations. - * - * \sa class JacobiSVD - */ -template<typename Derived> -JacobiSVD<typename MatrixBase<Derived>::PlainObject> -MatrixBase<Derived>::jacobiSvd(unsigned int computationOptions) const -{ - return JacobiSVD<PlainObject>(*this, computationOptions); -} - -} // end namespace Eigen - -#endif // EIGEN_JACOBISVD_H diff --git a/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h b/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h deleted file mode 100644 index ff0516f..0000000 --- a/eigen/Eigen/src/SVD/JacobiSVD_LAPACKE.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - Copyright (c) 2011, Intel Corporation. All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of Intel Corporation nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - ******************************************************************************** - * Content : Eigen bindings to LAPACKe - * Singular Value Decomposition - SVD. - ******************************************************************************** -*/ - -#ifndef EIGEN_JACOBISVD_LAPACKE_H -#define EIGEN_JACOBISVD_LAPACKE_H - -namespace Eigen { - -/** \internal Specialization for the data types supported by LAPACKe */ - -#define EIGEN_LAPACKE_SVD(EIGTYPE, LAPACKE_TYPE, LAPACKE_RTYPE, LAPACKE_PREFIX, EIGCOLROW, LAPACKE_COLROW) \ -template<> inline \ -JacobiSVD<Matrix<EIGTYPE, Dynamic, Dynamic, EIGCOLROW, Dynamic, Dynamic>, ColPivHouseholderQRPreconditioner>& \ -JacobiSVD<Matrix<EIGTYPE, Dynamic, Dynamic, EIGCOLROW, Dynamic, Dynamic>, ColPivHouseholderQRPreconditioner>::compute(const Matrix<EIGTYPE, Dynamic, Dynamic, EIGCOLROW, Dynamic, Dynamic>& matrix, unsigned int computationOptions) \ -{ \ - typedef Matrix<EIGTYPE, Dynamic, Dynamic, EIGCOLROW, Dynamic, Dynamic> MatrixType; \ - /*typedef MatrixType::Scalar Scalar;*/ \ - /*typedef MatrixType::RealScalar RealScalar;*/ \ - allocate(matrix.rows(), matrix.cols(), computationOptions); \ -\ - /*const RealScalar precision = RealScalar(2) * NumTraits<Scalar>::epsilon();*/ \ - m_nonzeroSingularValues = m_diagSize; \ -\ - lapack_int lda = internal::convert_index<lapack_int>(matrix.outerStride()), ldu, ldvt; \ - lapack_int matrix_order = LAPACKE_COLROW; \ - char jobu, jobvt; \ - LAPACKE_TYPE *u, *vt, dummy; \ - jobu = (m_computeFullU) ? 'A' : (m_computeThinU) ? 'S' : 'N'; \ - jobvt = (m_computeFullV) ? 'A' : (m_computeThinV) ? 'S' : 'N'; \ - if (computeU()) { \ - ldu = internal::convert_index<lapack_int>(m_matrixU.outerStride()); \ - u = (LAPACKE_TYPE*)m_matrixU.data(); \ - } else { ldu=1; u=&dummy; }\ - MatrixType localV; \ - lapack_int vt_rows = (m_computeFullV) ? internal::convert_index<lapack_int>(m_cols) : (m_computeThinV) ? internal::convert_index<lapack_int>(m_diagSize) : 1; \ - if (computeV()) { \ - localV.resize(vt_rows, m_cols); \ - ldvt = internal::convert_index<lapack_int>(localV.outerStride()); \ - vt = (LAPACKE_TYPE*)localV.data(); \ - } else { ldvt=1; vt=&dummy; }\ - Matrix<LAPACKE_RTYPE, Dynamic, Dynamic> superb; superb.resize(m_diagSize, 1); \ - MatrixType m_temp; m_temp = matrix; \ - LAPACKE_##LAPACKE_PREFIX##gesvd( matrix_order, jobu, jobvt, internal::convert_index<lapack_int>(m_rows), internal::convert_index<lapack_int>(m_cols), (LAPACKE_TYPE*)m_temp.data(), lda, (LAPACKE_RTYPE*)m_singularValues.data(), u, ldu, vt, ldvt, superb.data()); \ - if (computeV()) m_matrixV = localV.adjoint(); \ - /* for(int i=0;i<m_diagSize;i++) if (m_singularValues.coeffRef(i) < precision) { m_nonzeroSingularValues--; m_singularValues.coeffRef(i)=RealScalar(0);}*/ \ - m_isInitialized = true; \ - return *this; \ -} - -EIGEN_LAPACKE_SVD(double, double, double, d, ColMajor, LAPACK_COL_MAJOR) -EIGEN_LAPACKE_SVD(float, float, float , s, ColMajor, LAPACK_COL_MAJOR) -EIGEN_LAPACKE_SVD(dcomplex, lapack_complex_double, double, z, ColMajor, LAPACK_COL_MAJOR) -EIGEN_LAPACKE_SVD(scomplex, lapack_complex_float, float , c, ColMajor, LAPACK_COL_MAJOR) - -EIGEN_LAPACKE_SVD(double, double, double, d, RowMajor, LAPACK_ROW_MAJOR) -EIGEN_LAPACKE_SVD(float, float, float , s, RowMajor, LAPACK_ROW_MAJOR) -EIGEN_LAPACKE_SVD(dcomplex, lapack_complex_double, double, z, RowMajor, LAPACK_ROW_MAJOR) -EIGEN_LAPACKE_SVD(scomplex, lapack_complex_float, float , c, RowMajor, LAPACK_ROW_MAJOR) - -} // end namespace Eigen - -#endif // EIGEN_JACOBISVD_LAPACKE_H diff --git a/eigen/Eigen/src/SVD/SVDBase.h b/eigen/Eigen/src/SVD/SVDBase.h deleted file mode 100644 index 3d1ef37..0000000 --- a/eigen/Eigen/src/SVD/SVDBase.h +++ /dev/null @@ -1,315 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2009-2010 Benoit Jacob <jacob.benoit.1@gmail.com> -// Copyright (C) 2014 Gael Guennebaud <gael.guennebaud@inria.fr> -// -// Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com> -// Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr> -// Copyright (C) 2013 Jean Ceccato <jean.ceccato@ensimag.fr> -// Copyright (C) 2013 Pierre Zoppitelli <pierre.zoppitelli@ensimag.fr> -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef EIGEN_SVDBASE_H -#define EIGEN_SVDBASE_H - -namespace Eigen { -/** \ingroup SVD_Module - * - * - * \class SVDBase - * - * \brief Base class of SVD algorithms - * - * \tparam Derived the type of the actual SVD decomposition - * - * SVD decomposition consists in decomposing any n-by-p matrix \a A as a product - * \f[ A = U S V^* \f] - * where \a U is a n-by-n unitary, \a V is a p-by-p unitary, and \a S is a n-by-p real positive matrix which is zero outside of its main diagonal; - * the diagonal entries of S are known as the \em singular \em values of \a A and the columns of \a U and \a V are known as the left - * and right \em singular \em vectors of \a A respectively. - * - * Singular values are always sorted in decreasing order. - * - * - * You can ask for only \em thin \a U or \a V to be computed, meaning the following. In case of a rectangular n-by-p matrix, letting \a m be the - * smaller value among \a n and \a p, there are only \a m singular vectors; the remaining columns of \a U and \a V do not correspond to actual - * singular vectors. Asking for \em thin \a U or \a V means asking for only their \a m first columns to be formed. So \a U is then a n-by-m matrix, - * and \a V is then a p-by-m matrix. Notice that thin \a U and \a V are all you need for (least squares) solving. - * - * If the input matrix has inf or nan coefficients, the result of the computation is undefined, but the computation is guaranteed to - * terminate in finite (and reasonable) time. - * \sa class BDCSVD, class JacobiSVD - */ -template<typename Derived> -class SVDBase -{ - -public: - typedef typename internal::traits<Derived>::MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits<typename MatrixType::Scalar>::Real RealScalar; - typedef typename MatrixType::StorageIndex StorageIndex; - typedef Eigen::Index Index; ///< \deprecated since Eigen 3.3 - enum { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - ColsAtCompileTime = MatrixType::ColsAtCompileTime, - DiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_DYNAMIC(RowsAtCompileTime,ColsAtCompileTime), - MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, - MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, - MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN_PREFER_FIXED(MaxRowsAtCompileTime,MaxColsAtCompileTime), - MatrixOptions = MatrixType::Options - }; - - typedef Matrix<Scalar, RowsAtCompileTime, RowsAtCompileTime, MatrixOptions, MaxRowsAtCompileTime, MaxRowsAtCompileTime> MatrixUType; - typedef Matrix<Scalar, ColsAtCompileTime, ColsAtCompileTime, MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime> MatrixVType; - typedef typename internal::plain_diag_type<MatrixType, RealScalar>::type SingularValuesType; - - Derived& derived() { return *static_cast<Derived*>(this); } - const Derived& derived() const { return *static_cast<const Derived*>(this); } - - /** \returns the \a U matrix. - * - * For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p, - * the U matrix is n-by-n if you asked for \link Eigen::ComputeFullU ComputeFullU \endlink, and is n-by-m if you asked for \link Eigen::ComputeThinU ComputeThinU \endlink. - * - * The \a m first columns of \a U are the left singular vectors of the matrix being decomposed. - * - * This method asserts that you asked for \a U to be computed. - */ - const MatrixUType& matrixU() const - { - eigen_assert(m_isInitialized && "SVD is not initialized."); - eigen_assert(computeU() && "This SVD decomposition didn't compute U. Did you ask for it?"); - return m_matrixU; - } - - /** \returns the \a V matrix. - * - * For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p, - * the V matrix is p-by-p if you asked for \link Eigen::ComputeFullV ComputeFullV \endlink, and is p-by-m if you asked for \link Eigen::ComputeThinV ComputeThinV \endlink. - * - * The \a m first columns of \a V are the right singular vectors of the matrix being decomposed. - * - * This method asserts that you asked for \a V to be computed. - */ - const MatrixVType& matrixV() const - { - eigen_assert(m_isInitialized && "SVD is not initialized."); - eigen_assert(computeV() && "This SVD decomposition didn't compute V. Did you ask for it?"); - return m_matrixV; - } - - /** \returns the vector of singular values. - * - * For the SVD decomposition of a n-by-p matrix, letting \a m be the minimum of \a n and \a p, the - * returned vector has size \a m. Singular values are always sorted in decreasing order. - */ - const SingularValuesType& singularValues() const - { - eigen_assert(m_isInitialized && "SVD is not initialized."); - return m_singularValues; - } - - /** \returns the number of singular values that are not exactly 0 */ - Index nonzeroSingularValues() const - { - eigen_assert(m_isInitialized && "SVD is not initialized."); - return m_nonzeroSingularValues; - } - - /** \returns the rank of the matrix of which \c *this is the SVD. - * - * \note This method has to determine which singular values should be considered nonzero. - * For that, it uses the threshold value that you can control by calling - * setThreshold(const RealScalar&). - */ - inline Index rank() const - { - using std::abs; - eigen_assert(m_isInitialized && "JacobiSVD is not initialized."); - if(m_singularValues.size()==0) return 0; - RealScalar premultiplied_threshold = numext::maxi<RealScalar>(m_singularValues.coeff(0) * threshold(), (std::numeric_limits<RealScalar>::min)()); - Index i = m_nonzeroSingularValues-1; - while(i>=0 && m_singularValues.coeff(i) < premultiplied_threshold) --i; - return i+1; - } - - /** Allows to prescribe a threshold to be used by certain methods, such as rank() and solve(), - * which need to determine when singular values are to be considered nonzero. - * This is not used for the SVD decomposition itself. - * - * When it needs to get the threshold value, Eigen calls threshold(). - * The default is \c NumTraits<Scalar>::epsilon() - * - * \param threshold The new value to use as the threshold. - * - * A singular value will be considered nonzero if its value is strictly greater than - * \f$ \vert singular value \vert \leqslant threshold \times \vert max singular value \vert \f$. - * - * If you want to come back to the default behavior, call setThreshold(Default_t) - */ - Derived& setThreshold(const RealScalar& threshold) - { - m_usePrescribedThreshold = true; - m_prescribedThreshold = threshold; - return derived(); - } - - /** Allows to come back to the default behavior, letting Eigen use its default formula for - * determining the threshold. - * - * You should pass the special object Eigen::Default as parameter here. - * \code svd.setThreshold(Eigen::Default); \endcode - * - * See the documentation of setThreshold(const RealScalar&). - */ - Derived& setThreshold(Default_t) - { - m_usePrescribedThreshold = false; - return derived(); - } - - /** Returns the threshold that will be used by certain methods such as rank(). - * - * See the documentation of setThreshold(const RealScalar&). - */ - RealScalar threshold() const - { - eigen_assert(m_isInitialized || m_usePrescribedThreshold); - // this temporary is needed to workaround a MSVC issue - Index diagSize = (std::max<Index>)(1,m_diagSize); - return m_usePrescribedThreshold ? m_prescribedThreshold - : diagSize*NumTraits<Scalar>::epsilon(); - } - - /** \returns true if \a U (full or thin) is asked for in this SVD decomposition */ - inline bool computeU() const { return m_computeFullU || m_computeThinU; } - /** \returns true if \a V (full or thin) is asked for in this SVD decomposition */ - inline bool computeV() const { return m_computeFullV || m_computeThinV; } - - inline Index rows() const { return m_rows; } - inline Index cols() const { return m_cols; } - - /** \returns a (least squares) solution of \f$ A x = b \f$ using the current SVD decomposition of A. - * - * \param b the right-hand-side of the equation to solve. - * - * \note Solving requires both U and V to be computed. Thin U and V are enough, there is no need for full U or V. - * - * \note SVD solving is implicitly least-squares. Thus, this method serves both purposes of exact solving and least-squares solving. - * In other words, the returned solution is guaranteed to minimize the Euclidean norm \f$ \Vert A x - b \Vert \f$. - */ - template<typename Rhs> - inline const Solve<Derived, Rhs> - solve(const MatrixBase<Rhs>& b) const - { - eigen_assert(m_isInitialized && "SVD is not initialized."); - eigen_assert(computeU() && computeV() && "SVD::solve() requires both unitaries U and V to be computed (thin unitaries suffice)."); - return Solve<Derived, Rhs>(derived(), b.derived()); - } - - #ifndef EIGEN_PARSED_BY_DOXYGEN - template<typename RhsType, typename DstType> - EIGEN_DEVICE_FUNC - void _solve_impl(const RhsType &rhs, DstType &dst) const; - #endif - -protected: - - static void check_template_parameters() - { - EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar); - } - - // return true if already allocated - bool allocate(Index rows, Index cols, unsigned int computationOptions) ; - - MatrixUType m_matrixU; - MatrixVType m_matrixV; - SingularValuesType m_singularValues; - bool m_isInitialized, m_isAllocated, m_usePrescribedThreshold; - bool m_computeFullU, m_computeThinU; - bool m_computeFullV, m_computeThinV; - unsigned int m_computationOptions; - Index m_nonzeroSingularValues, m_rows, m_cols, m_diagSize; - RealScalar m_prescribedThreshold; - - /** \brief Default Constructor. - * - * Default constructor of SVDBase - */ - SVDBase() - : m_isInitialized(false), - m_isAllocated(false), - m_usePrescribedThreshold(false), - m_computationOptions(0), - m_rows(-1), m_cols(-1), m_diagSize(0) - { - check_template_parameters(); - } - - -}; - -#ifndef EIGEN_PARSED_BY_DOXYGEN -template<typename Derived> -template<typename RhsType, typename DstType> -void SVDBase<Derived>::_solve_impl(const RhsType &rhs, DstType &dst) const -{ - eigen_assert(rhs.rows() == rows()); - - // A = U S V^* - // So A^{-1} = V S^{-1} U^* - - Matrix<Scalar, Dynamic, RhsType::ColsAtCompileTime, 0, MatrixType::MaxRowsAtCompileTime, RhsType::MaxColsAtCompileTime> tmp; - Index l_rank = rank(); - tmp.noalias() = m_matrixU.leftCols(l_rank).adjoint() * rhs; - tmp = m_singularValues.head(l_rank).asDiagonal().inverse() * tmp; - dst = m_matrixV.leftCols(l_rank) * tmp; -} -#endif - -template<typename MatrixType> -bool SVDBase<MatrixType>::allocate(Index rows, Index cols, unsigned int computationOptions) -{ - eigen_assert(rows >= 0 && cols >= 0); - - if (m_isAllocated && - rows == m_rows && - cols == m_cols && - computationOptions == m_computationOptions) - { - return true; - } - - m_rows = rows; - m_cols = cols; - m_isInitialized = false; - m_isAllocated = true; - m_computationOptions = computationOptions; - m_computeFullU = (computationOptions & ComputeFullU) != 0; - m_computeThinU = (computationOptions & ComputeThinU) != 0; - m_computeFullV = (computationOptions & ComputeFullV) != 0; - m_computeThinV = (computationOptions & ComputeThinV) != 0; - eigen_assert(!(m_computeFullU && m_computeThinU) && "SVDBase: you can't ask for both full and thin U"); - eigen_assert(!(m_computeFullV && m_computeThinV) && "SVDBase: you can't ask for both full and thin V"); - eigen_assert(EIGEN_IMPLIES(m_computeThinU || m_computeThinV, MatrixType::ColsAtCompileTime==Dynamic) && - "SVDBase: thin U and V are only available when your matrix has a dynamic number of columns."); - - m_diagSize = (std::min)(m_rows, m_cols); - m_singularValues.resize(m_diagSize); - if(RowsAtCompileTime==Dynamic) - m_matrixU.resize(m_rows, m_computeFullU ? m_rows : m_computeThinU ? m_diagSize : 0); - if(ColsAtCompileTime==Dynamic) - m_matrixV.resize(m_cols, m_computeFullV ? m_cols : m_computeThinV ? m_diagSize : 0); - - return false; -} - -}// end namespace - -#endif // EIGEN_SVDBASE_H diff --git a/eigen/Eigen/src/SVD/UpperBidiagonalization.h b/eigen/Eigen/src/SVD/UpperBidiagonalization.h deleted file mode 100644 index 11ac847..0000000 --- a/eigen/Eigen/src/SVD/UpperBidiagonalization.h +++ /dev/null @@ -1,414 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com> -// Copyright (C) 2013-2014 Gael Guennebaud <gael.guennebaud@inria.fr> -// -// This Source Code Form is subject to the terms of the Mozilla -// Public License v. 2.0. If a copy of the MPL was not distributed -// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef EIGEN_BIDIAGONALIZATION_H -#define EIGEN_BIDIAGONALIZATION_H - -namespace Eigen { - -namespace internal { -// UpperBidiagonalization will probably be replaced by a Bidiagonalization class, don't want to make it stable API. -// At the same time, it's useful to keep for now as it's about the only thing that is testing the BandMatrix class. - -template<typename _MatrixType> class UpperBidiagonalization -{ - public: - - typedef _MatrixType MatrixType; - enum { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - ColsAtCompileTime = MatrixType::ColsAtCompileTime, - ColsAtCompileTimeMinusOne = internal::decrement_size<ColsAtCompileTime>::ret - }; - typedef typename MatrixType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; - typedef Eigen::Index Index; ///< \deprecated since Eigen 3.3 - typedef Matrix<Scalar, 1, ColsAtCompileTime> RowVectorType; - typedef Matrix<Scalar, RowsAtCompileTime, 1> ColVectorType; - typedef BandMatrix<RealScalar, ColsAtCompileTime, ColsAtCompileTime, 1, 0, RowMajor> BidiagonalType; - typedef Matrix<Scalar, ColsAtCompileTime, 1> DiagVectorType; - typedef Matrix<Scalar, ColsAtCompileTimeMinusOne, 1> SuperDiagVectorType; - typedef HouseholderSequence< - const MatrixType, - const typename internal::remove_all<typename Diagonal<const MatrixType,0>::ConjugateReturnType>::type - > HouseholderUSequenceType; - typedef HouseholderSequence< - const typename internal::remove_all<typename MatrixType::ConjugateReturnType>::type, - Diagonal<const MatrixType,1>, - OnTheRight - > HouseholderVSequenceType; - - /** - * \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via Bidiagonalization::compute(const MatrixType&). - */ - UpperBidiagonalization() : m_householder(), m_bidiagonal(), m_isInitialized(false) {} - - explicit UpperBidiagonalization(const MatrixType& matrix) - : m_householder(matrix.rows(), matrix.cols()), - m_bidiagonal(matrix.cols(), matrix.cols()), - m_isInitialized(false) - { - compute(matrix); - } - - UpperBidiagonalization& compute(const MatrixType& matrix); - UpperBidiagonalization& computeUnblocked(const MatrixType& matrix); - - const MatrixType& householder() const { return m_householder; } - const BidiagonalType& bidiagonal() const { return m_bidiagonal; } - - const HouseholderUSequenceType householderU() const - { - eigen_assert(m_isInitialized && "UpperBidiagonalization is not initialized."); - return HouseholderUSequenceType(m_householder, m_householder.diagonal().conjugate()); - } - - const HouseholderVSequenceType householderV() // const here gives nasty errors and i'm lazy - { - eigen_assert(m_isInitialized && "UpperBidiagonalization is not initialized."); - return HouseholderVSequenceType(m_householder.conjugate(), m_householder.const_derived().template diagonal<1>()) - .setLength(m_householder.cols()-1) - .setShift(1); - } - - protected: - MatrixType m_householder; - BidiagonalType m_bidiagonal; - bool m_isInitialized; -}; - -// Standard upper bidiagonalization without fancy optimizations -// This version should be faster for small matrix size -template<typename MatrixType> -void upperbidiagonalization_inplace_unblocked(MatrixType& mat, - typename MatrixType::RealScalar *diagonal, - typename MatrixType::RealScalar *upper_diagonal, - typename MatrixType::Scalar* tempData = 0) -{ - typedef typename MatrixType::Scalar Scalar; - - Index rows = mat.rows(); - Index cols = mat.cols(); - - typedef Matrix<Scalar,Dynamic,1,ColMajor,MatrixType::MaxRowsAtCompileTime,1> TempType; - TempType tempVector; - if(tempData==0) - { - tempVector.resize(rows); - tempData = tempVector.data(); - } - - for (Index k = 0; /* breaks at k==cols-1 below */ ; ++k) - { - Index remainingRows = rows - k; - Index remainingCols = cols - k - 1; - - // construct left householder transform in-place in A - mat.col(k).tail(remainingRows) - .makeHouseholderInPlace(mat.coeffRef(k,k), diagonal[k]); - // apply householder transform to remaining part of A on the left - mat.bottomRightCorner(remainingRows, remainingCols) - .applyHouseholderOnTheLeft(mat.col(k).tail(remainingRows-1), mat.coeff(k,k), tempData); - - if(k == cols-1) break; - - // construct right householder transform in-place in mat - mat.row(k).tail(remainingCols) - .makeHouseholderInPlace(mat.coeffRef(k,k+1), upper_diagonal[k]); - // apply householder transform to remaining part of mat on the left - mat.bottomRightCorner(remainingRows-1, remainingCols) - .applyHouseholderOnTheRight(mat.row(k).tail(remainingCols-1).transpose(), mat.coeff(k,k+1), tempData); - } -} - -/** \internal - * Helper routine for the block reduction to upper bidiagonal form. - * - * Let's partition the matrix A: - * - * | A00 A01 | - * A = | | - * | A10 A11 | - * - * This function reduces to bidiagonal form the left \c rows x \a blockSize vertical panel [A00/A10] - * and the \a blockSize x \c cols horizontal panel [A00 A01] of the matrix \a A. The bottom-right block A11 - * is updated using matrix-matrix products: - * A22 -= V * Y^T - X * U^T - * where V and U contains the left and right Householder vectors. U and V are stored in A10, and A01 - * respectively, and the update matrices X and Y are computed during the reduction. - * - */ -template<typename MatrixType> -void upperbidiagonalization_blocked_helper(MatrixType& A, - typename MatrixType::RealScalar *diagonal, - typename MatrixType::RealScalar *upper_diagonal, - Index bs, - Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic, - traits<MatrixType>::Flags & RowMajorBit> > X, - Ref<Matrix<typename MatrixType::Scalar, Dynamic, Dynamic, - traits<MatrixType>::Flags & RowMajorBit> > Y) -{ - typedef typename MatrixType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; - typedef typename NumTraits<RealScalar>::Literal Literal; - enum { StorageOrder = traits<MatrixType>::Flags & RowMajorBit }; - typedef InnerStride<int(StorageOrder) == int(ColMajor) ? 1 : Dynamic> ColInnerStride; - typedef InnerStride<int(StorageOrder) == int(ColMajor) ? Dynamic : 1> RowInnerStride; - typedef Ref<Matrix<Scalar, Dynamic, 1>, 0, ColInnerStride> SubColumnType; - typedef Ref<Matrix<Scalar, 1, Dynamic>, 0, RowInnerStride> SubRowType; - typedef Ref<Matrix<Scalar, Dynamic, Dynamic, StorageOrder > > SubMatType; - - Index brows = A.rows(); - Index bcols = A.cols(); - - Scalar tau_u, tau_u_prev(0), tau_v; - - for(Index k = 0; k < bs; ++k) - { - Index remainingRows = brows - k; - Index remainingCols = bcols - k - 1; - - SubMatType X_k1( X.block(k,0, remainingRows,k) ); - SubMatType V_k1( A.block(k,0, remainingRows,k) ); - - // 1 - update the k-th column of A - SubColumnType v_k = A.col(k).tail(remainingRows); - v_k -= V_k1 * Y.row(k).head(k).adjoint(); - if(k) v_k -= X_k1 * A.col(k).head(k); - - // 2 - construct left Householder transform in-place - v_k.makeHouseholderInPlace(tau_v, diagonal[k]); - - if(k+1<bcols) - { - SubMatType Y_k ( Y.block(k+1,0, remainingCols, k+1) ); - SubMatType U_k1 ( A.block(0,k+1, k,remainingCols) ); - - // this eases the application of Householder transforAions - // A(k,k) will store tau_v later - A(k,k) = Scalar(1); - - // 3 - Compute y_k^T = tau_v * ( A^T*v_k - Y_k-1*V_k-1^T*v_k - U_k-1*X_k-1^T*v_k ) - { - SubColumnType y_k( Y.col(k).tail(remainingCols) ); - - // let's use the begining of column k of Y as a temporary vector - SubColumnType tmp( Y.col(k).head(k) ); - y_k.noalias() = A.block(k,k+1, remainingRows,remainingCols).adjoint() * v_k; // bottleneck - tmp.noalias() = V_k1.adjoint() * v_k; - y_k.noalias() -= Y_k.leftCols(k) * tmp; - tmp.noalias() = X_k1.adjoint() * v_k; - y_k.noalias() -= U_k1.adjoint() * tmp; - y_k *= numext::conj(tau_v); - } - - // 4 - update k-th row of A (it will become u_k) - SubRowType u_k( A.row(k).tail(remainingCols) ); - u_k = u_k.conjugate(); - { - u_k -= Y_k * A.row(k).head(k+1).adjoint(); - if(k) u_k -= U_k1.adjoint() * X.row(k).head(k).adjoint(); - } - - // 5 - construct right Householder transform in-place - u_k.makeHouseholderInPlace(tau_u, upper_diagonal[k]); - - // this eases the application of Householder transformations - // A(k,k+1) will store tau_u later - A(k,k+1) = Scalar(1); - - // 6 - Compute x_k = tau_u * ( A*u_k - X_k-1*U_k-1^T*u_k - V_k*Y_k^T*u_k ) - { - SubColumnType x_k ( X.col(k).tail(remainingRows-1) ); - - // let's use the begining of column k of X as a temporary vectors - // note that tmp0 and tmp1 overlaps - SubColumnType tmp0 ( X.col(k).head(k) ), - tmp1 ( X.col(k).head(k+1) ); - - x_k.noalias() = A.block(k+1,k+1, remainingRows-1,remainingCols) * u_k.transpose(); // bottleneck - tmp0.noalias() = U_k1 * u_k.transpose(); - x_k.noalias() -= X_k1.bottomRows(remainingRows-1) * tmp0; - tmp1.noalias() = Y_k.adjoint() * u_k.transpose(); - x_k.noalias() -= A.block(k+1,0, remainingRows-1,k+1) * tmp1; - x_k *= numext::conj(tau_u); - tau_u = numext::conj(tau_u); - u_k = u_k.conjugate(); - } - - if(k>0) A.coeffRef(k-1,k) = tau_u_prev; - tau_u_prev = tau_u; - } - else - A.coeffRef(k-1,k) = tau_u_prev; - - A.coeffRef(k,k) = tau_v; - } - - if(bs<bcols) - A.coeffRef(bs-1,bs) = tau_u_prev; - - // update A22 - if(bcols>bs && brows>bs) - { - SubMatType A11( A.bottomRightCorner(brows-bs,bcols-bs) ); - SubMatType A10( A.block(bs,0, brows-bs,bs) ); - SubMatType A01( A.block(0,bs, bs,bcols-bs) ); - Scalar tmp = A01(bs-1,0); - A01(bs-1,0) = Literal(1); - A11.noalias() -= A10 * Y.topLeftCorner(bcols,bs).bottomRows(bcols-bs).adjoint(); - A11.noalias() -= X.topLeftCorner(brows,bs).bottomRows(brows-bs) * A01; - A01(bs-1,0) = tmp; - } -} - -/** \internal - * - * Implementation of a block-bidiagonal reduction. - * It is based on the following paper: - * The Design of a Parallel Dense Linear Algebra Software Library: Reduction to Hessenberg, Tridiagonal, and Bidiagonal Form. - * by Jaeyoung Choi, Jack J. Dongarra, David W. Walker. (1995) - * section 3.3 - */ -template<typename MatrixType, typename BidiagType> -void upperbidiagonalization_inplace_blocked(MatrixType& A, BidiagType& bidiagonal, - Index maxBlockSize=32, - typename MatrixType::Scalar* /*tempData*/ = 0) -{ - typedef typename MatrixType::Scalar Scalar; - typedef Block<MatrixType,Dynamic,Dynamic> BlockType; - - Index rows = A.rows(); - Index cols = A.cols(); - Index size = (std::min)(rows, cols); - - // X and Y are work space - enum { StorageOrder = traits<MatrixType>::Flags & RowMajorBit }; - Matrix<Scalar, - MatrixType::RowsAtCompileTime, - Dynamic, - StorageOrder, - MatrixType::MaxRowsAtCompileTime> X(rows,maxBlockSize); - Matrix<Scalar, - MatrixType::ColsAtCompileTime, - Dynamic, - StorageOrder, - MatrixType::MaxColsAtCompileTime> Y(cols,maxBlockSize); - Index blockSize = (std::min)(maxBlockSize,size); - - Index k = 0; - for(k = 0; k < size; k += blockSize) - { - Index bs = (std::min)(size-k,blockSize); // actual size of the block - Index brows = rows - k; // rows of the block - Index bcols = cols - k; // columns of the block - - // partition the matrix A: - // - // | A00 A01 A02 | - // | | - // A = | A10 A11 A12 | - // | | - // | A20 A21 A22 | - // - // where A11 is a bs x bs diagonal block, - // and let: - // | A11 A12 | - // B = | | - // | A21 A22 | - - BlockType B = A.block(k,k,brows,bcols); - - // This stage performs the bidiagonalization of A11, A21, A12, and updating of A22. - // Finally, the algorithm continue on the updated A22. - // - // However, if B is too small, or A22 empty, then let's use an unblocked strategy - if(k+bs==cols || bcols<48) // somewhat arbitrary threshold - { - upperbidiagonalization_inplace_unblocked(B, - &(bidiagonal.template diagonal<0>().coeffRef(k)), - &(bidiagonal.template diagonal<1>().coeffRef(k)), - X.data() - ); - break; // We're done - } - else - { - upperbidiagonalization_blocked_helper<BlockType>( B, - &(bidiagonal.template diagonal<0>().coeffRef(k)), - &(bidiagonal.template diagonal<1>().coeffRef(k)), - bs, - X.topLeftCorner(brows,bs), - Y.topLeftCorner(bcols,bs) - ); - } - } -} - -template<typename _MatrixType> -UpperBidiagonalization<_MatrixType>& UpperBidiagonalization<_MatrixType>::computeUnblocked(const _MatrixType& matrix) -{ - Index rows = matrix.rows(); - Index cols = matrix.cols(); - EIGEN_ONLY_USED_FOR_DEBUG(cols); - - eigen_assert(rows >= cols && "UpperBidiagonalization is only for Arices satisfying rows>=cols."); - - m_householder = matrix; - - ColVectorType temp(rows); - - upperbidiagonalization_inplace_unblocked(m_householder, - &(m_bidiagonal.template diagonal<0>().coeffRef(0)), - &(m_bidiagonal.template diagonal<1>().coeffRef(0)), - temp.data()); - - m_isInitialized = true; - return *this; -} - -template<typename _MatrixType> -UpperBidiagonalization<_MatrixType>& UpperBidiagonalization<_MatrixType>::compute(const _MatrixType& matrix) -{ - Index rows = matrix.rows(); - Index cols = matrix.cols(); - EIGEN_ONLY_USED_FOR_DEBUG(rows); - EIGEN_ONLY_USED_FOR_DEBUG(cols); - - eigen_assert(rows >= cols && "UpperBidiagonalization is only for Arices satisfying rows>=cols."); - - m_householder = matrix; - upperbidiagonalization_inplace_blocked(m_householder, m_bidiagonal); - - m_isInitialized = true; - return *this; -} - -#if 0 -/** \return the Householder QR decomposition of \c *this. - * - * \sa class Bidiagonalization - */ -template<typename Derived> -const UpperBidiagonalization<typename MatrixBase<Derived>::PlainObject> -MatrixBase<Derived>::bidiagonalization() const -{ - return UpperBidiagonalization<PlainObject>(eval()); -} -#endif - -} // end namespace internal - -} // end namespace Eigen - -#endif // EIGEN_BIDIAGONALIZATION_H |
