1*bf2c3715SXin Li // This file is part of Eigen, a lightweight C++ template library 2*bf2c3715SXin Li // for linear algebra. 3*bf2c3715SXin Li // 4*bf2c3715SXin Li // Copyright (C) 2012-2013 Desire Nuentsa <[email protected]> 5*bf2c3715SXin Li // Copyright (C) 2012-2014 Gael Guennebaud <[email protected]> 6*bf2c3715SXin Li // 7*bf2c3715SXin Li // This Source Code Form is subject to the terms of the Mozilla 8*bf2c3715SXin Li // Public License v. 2.0. If a copy of the MPL was not distributed 9*bf2c3715SXin Li // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 10*bf2c3715SXin Li 11*bf2c3715SXin Li #ifndef EIGEN_SPARSE_QR_H 12*bf2c3715SXin Li #define EIGEN_SPARSE_QR_H 13*bf2c3715SXin Li 14*bf2c3715SXin Li namespace Eigen { 15*bf2c3715SXin Li 16*bf2c3715SXin Li template<typename MatrixType, typename OrderingType> class SparseQR; 17*bf2c3715SXin Li template<typename SparseQRType> struct SparseQRMatrixQReturnType; 18*bf2c3715SXin Li template<typename SparseQRType> struct SparseQRMatrixQTransposeReturnType; 19*bf2c3715SXin Li template<typename SparseQRType, typename Derived> struct SparseQR_QProduct; 20*bf2c3715SXin Li namespace internal { 21*bf2c3715SXin Li template <typename SparseQRType> struct traits<SparseQRMatrixQReturnType<SparseQRType> > 22*bf2c3715SXin Li { 23*bf2c3715SXin Li typedef typename SparseQRType::MatrixType ReturnType; 24*bf2c3715SXin Li typedef typename ReturnType::StorageIndex StorageIndex; 25*bf2c3715SXin Li typedef typename ReturnType::StorageKind StorageKind; 26*bf2c3715SXin Li enum { 27*bf2c3715SXin Li RowsAtCompileTime = Dynamic, 28*bf2c3715SXin Li ColsAtCompileTime = Dynamic 29*bf2c3715SXin Li }; 30*bf2c3715SXin Li }; 31*bf2c3715SXin Li template <typename SparseQRType> struct traits<SparseQRMatrixQTransposeReturnType<SparseQRType> > 32*bf2c3715SXin Li { 33*bf2c3715SXin Li typedef typename SparseQRType::MatrixType ReturnType; 34*bf2c3715SXin Li }; 35*bf2c3715SXin Li template <typename SparseQRType, typename Derived> struct traits<SparseQR_QProduct<SparseQRType, Derived> > 36*bf2c3715SXin Li { 37*bf2c3715SXin Li typedef typename Derived::PlainObject ReturnType; 38*bf2c3715SXin Li }; 39*bf2c3715SXin Li } // End namespace internal 40*bf2c3715SXin Li 41*bf2c3715SXin Li /** 42*bf2c3715SXin Li * \ingroup SparseQR_Module 43*bf2c3715SXin Li * \class SparseQR 44*bf2c3715SXin Li * \brief Sparse left-looking QR factorization with numerical column pivoting 45*bf2c3715SXin Li * 46*bf2c3715SXin Li * This class implements a left-looking QR decomposition of sparse matrices 47*bf2c3715SXin Li * with numerical column pivoting. 48*bf2c3715SXin Li * When a column has a norm less than a given tolerance 49*bf2c3715SXin Li * it is implicitly permuted to the end. The QR factorization thus obtained is 50*bf2c3715SXin Li * given by A*P = Q*R where R is upper triangular or trapezoidal. 51*bf2c3715SXin Li * 52*bf2c3715SXin Li * P is the column permutation which is the product of the fill-reducing and the 53*bf2c3715SXin Li * numerical permutations. Use colsPermutation() to get it. 54*bf2c3715SXin Li * 55*bf2c3715SXin Li * Q is the orthogonal matrix represented as products of Householder reflectors. 56*bf2c3715SXin Li * Use matrixQ() to get an expression and matrixQ().adjoint() to get the adjoint. 57*bf2c3715SXin Li * You can then apply it to a vector. 58*bf2c3715SXin Li * 59*bf2c3715SXin Li * R is the sparse triangular or trapezoidal matrix. The later occurs when A is rank-deficient. 60*bf2c3715SXin Li * matrixR().topLeftCorner(rank(), rank()) always returns a triangular factor of full rank. 61*bf2c3715SXin Li * 62*bf2c3715SXin Li * \tparam _MatrixType The type of the sparse matrix A, must be a column-major SparseMatrix<> 63*bf2c3715SXin Li * \tparam _OrderingType The fill-reducing ordering method. See the \link OrderingMethods_Module 64*bf2c3715SXin Li * OrderingMethods \endlink module for the list of built-in and external ordering methods. 65*bf2c3715SXin Li * 66*bf2c3715SXin Li * \implsparsesolverconcept 67*bf2c3715SXin Li * 68*bf2c3715SXin Li * The numerical pivoting strategy and default threshold are the same as in SuiteSparse QR, and 69*bf2c3715SXin Li * detailed in the following paper: 70*bf2c3715SXin Li * <i> 71*bf2c3715SXin Li * Tim Davis, "Algorithm 915, SuiteSparseQR: Multifrontal Multithreaded Rank-Revealing 72*bf2c3715SXin Li * Sparse QR Factorization, ACM Trans. on Math. Soft. 38(1), 2011. 73*bf2c3715SXin Li * </i> 74*bf2c3715SXin Li * Even though it is qualified as "rank-revealing", this strategy might fail for some 75*bf2c3715SXin Li * rank deficient problems. When this class is used to solve linear or least-square problems 76*bf2c3715SXin Li * it is thus strongly recommended to check the accuracy of the computed solution. If it 77*bf2c3715SXin Li * failed, it usually helps to increase the threshold with setPivotThreshold. 78*bf2c3715SXin Li * 79*bf2c3715SXin Li * \warning The input sparse matrix A must be in compressed mode (see SparseMatrix::makeCompressed()). 80*bf2c3715SXin Li * \warning For complex matrices matrixQ().transpose() will actually return the adjoint matrix. 81*bf2c3715SXin Li * 82*bf2c3715SXin Li */ 83*bf2c3715SXin Li template<typename _MatrixType, typename _OrderingType> 84*bf2c3715SXin Li class SparseQR : public SparseSolverBase<SparseQR<_MatrixType,_OrderingType> > 85*bf2c3715SXin Li { 86*bf2c3715SXin Li protected: 87*bf2c3715SXin Li typedef SparseSolverBase<SparseQR<_MatrixType,_OrderingType> > Base; 88*bf2c3715SXin Li using Base::m_isInitialized; 89*bf2c3715SXin Li public: 90*bf2c3715SXin Li using Base::_solve_impl; 91*bf2c3715SXin Li typedef _MatrixType MatrixType; 92*bf2c3715SXin Li typedef _OrderingType OrderingType; 93*bf2c3715SXin Li typedef typename MatrixType::Scalar Scalar; 94*bf2c3715SXin Li typedef typename MatrixType::RealScalar RealScalar; 95*bf2c3715SXin Li typedef typename MatrixType::StorageIndex StorageIndex; 96*bf2c3715SXin Li typedef SparseMatrix<Scalar,ColMajor,StorageIndex> QRMatrixType; 97*bf2c3715SXin Li typedef Matrix<StorageIndex, Dynamic, 1> IndexVector; 98*bf2c3715SXin Li typedef Matrix<Scalar, Dynamic, 1> ScalarVector; 99*bf2c3715SXin Li typedef PermutationMatrix<Dynamic, Dynamic, StorageIndex> PermutationType; 100*bf2c3715SXin Li 101*bf2c3715SXin Li enum { 102*bf2c3715SXin Li ColsAtCompileTime = MatrixType::ColsAtCompileTime, 103*bf2c3715SXin Li MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime 104*bf2c3715SXin Li }; 105*bf2c3715SXin Li 106*bf2c3715SXin Li public: 107*bf2c3715SXin Li SparseQR () : m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false),m_isEtreeOk(false) 108*bf2c3715SXin Li { } 109*bf2c3715SXin Li 110*bf2c3715SXin Li /** Construct a QR factorization of the matrix \a mat. 111*bf2c3715SXin Li * 112*bf2c3715SXin Li * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()). 113*bf2c3715SXin Li * 114*bf2c3715SXin Li * \sa compute() 115*bf2c3715SXin Li */ 116*bf2c3715SXin Li explicit SparseQR(const MatrixType& mat) : m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false),m_isEtreeOk(false) 117*bf2c3715SXin Li { 118*bf2c3715SXin Li compute(mat); 119*bf2c3715SXin Li } 120*bf2c3715SXin Li 121*bf2c3715SXin Li /** Computes the QR factorization of the sparse matrix \a mat. 122*bf2c3715SXin Li * 123*bf2c3715SXin Li * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()). 124*bf2c3715SXin Li * 125*bf2c3715SXin Li * \sa analyzePattern(), factorize() 126*bf2c3715SXin Li */ 127*bf2c3715SXin Li void compute(const MatrixType& mat) 128*bf2c3715SXin Li { 129*bf2c3715SXin Li analyzePattern(mat); 130*bf2c3715SXin Li factorize(mat); 131*bf2c3715SXin Li } 132*bf2c3715SXin Li void analyzePattern(const MatrixType& mat); 133*bf2c3715SXin Li void factorize(const MatrixType& mat); 134*bf2c3715SXin Li 135*bf2c3715SXin Li /** \returns the number of rows of the represented matrix. 136*bf2c3715SXin Li */ 137*bf2c3715SXin Li inline Index rows() const { return m_pmat.rows(); } 138*bf2c3715SXin Li 139*bf2c3715SXin Li /** \returns the number of columns of the represented matrix. 140*bf2c3715SXin Li */ 141*bf2c3715SXin Li inline Index cols() const { return m_pmat.cols();} 142*bf2c3715SXin Li 143*bf2c3715SXin Li /** \returns a const reference to the \b sparse upper triangular matrix R of the QR factorization. 144*bf2c3715SXin Li * \warning The entries of the returned matrix are not sorted. This means that using it in algorithms 145*bf2c3715SXin Li * expecting sorted entries will fail. This include random coefficient accesses (SpaseMatrix::coeff()), 146*bf2c3715SXin Li * and coefficient-wise operations. Matrix products and triangular solves are fine though. 147*bf2c3715SXin Li * 148*bf2c3715SXin Li * To sort the entries, you can assign it to a row-major matrix, and if a column-major matrix 149*bf2c3715SXin Li * is required, you can copy it again: 150*bf2c3715SXin Li * \code 151*bf2c3715SXin Li * SparseMatrix<double> R = qr.matrixR(); // column-major, not sorted! 152*bf2c3715SXin Li * SparseMatrix<double,RowMajor> Rr = qr.matrixR(); // row-major, sorted 153*bf2c3715SXin Li * SparseMatrix<double> Rc = Rr; // column-major, sorted 154*bf2c3715SXin Li * \endcode 155*bf2c3715SXin Li */ 156*bf2c3715SXin Li const QRMatrixType& matrixR() const { return m_R; } 157*bf2c3715SXin Li 158*bf2c3715SXin Li /** \returns the number of non linearly dependent columns as determined by the pivoting threshold. 159*bf2c3715SXin Li * 160*bf2c3715SXin Li * \sa setPivotThreshold() 161*bf2c3715SXin Li */ 162*bf2c3715SXin Li Index rank() const 163*bf2c3715SXin Li { 164*bf2c3715SXin Li eigen_assert(m_isInitialized && "The factorization should be called first, use compute()"); 165*bf2c3715SXin Li return m_nonzeropivots; 166*bf2c3715SXin Li } 167*bf2c3715SXin Li 168*bf2c3715SXin Li /** \returns an expression of the matrix Q as products of sparse Householder reflectors. 169*bf2c3715SXin Li * The common usage of this function is to apply it to a dense matrix or vector 170*bf2c3715SXin Li * \code 171*bf2c3715SXin Li * VectorXd B1, B2; 172*bf2c3715SXin Li * // Initialize B1 173*bf2c3715SXin Li * B2 = matrixQ() * B1; 174*bf2c3715SXin Li * \endcode 175*bf2c3715SXin Li * 176*bf2c3715SXin Li * To get a plain SparseMatrix representation of Q: 177*bf2c3715SXin Li * \code 178*bf2c3715SXin Li * SparseMatrix<double> Q; 179*bf2c3715SXin Li * Q = SparseQR<SparseMatrix<double> >(A).matrixQ(); 180*bf2c3715SXin Li * \endcode 181*bf2c3715SXin Li * Internally, this call simply performs a sparse product between the matrix Q 182*bf2c3715SXin Li * and a sparse identity matrix. However, due to the fact that the sparse 183*bf2c3715SXin Li * reflectors are stored unsorted, two transpositions are needed to sort 184*bf2c3715SXin Li * them before performing the product. 185*bf2c3715SXin Li */ 186*bf2c3715SXin Li SparseQRMatrixQReturnType<SparseQR> matrixQ() const 187*bf2c3715SXin Li { return SparseQRMatrixQReturnType<SparseQR>(*this); } 188*bf2c3715SXin Li 189*bf2c3715SXin Li /** \returns a const reference to the column permutation P that was applied to A such that A*P = Q*R 190*bf2c3715SXin Li * It is the combination of the fill-in reducing permutation and numerical column pivoting. 191*bf2c3715SXin Li */ 192*bf2c3715SXin Li const PermutationType& colsPermutation() const 193*bf2c3715SXin Li { 194*bf2c3715SXin Li eigen_assert(m_isInitialized && "Decomposition is not initialized."); 195*bf2c3715SXin Li return m_outputPerm_c; 196*bf2c3715SXin Li } 197*bf2c3715SXin Li 198*bf2c3715SXin Li /** \returns A string describing the type of error. 199*bf2c3715SXin Li * This method is provided to ease debugging, not to handle errors. 200*bf2c3715SXin Li */ 201*bf2c3715SXin Li std::string lastErrorMessage() const { return m_lastError; } 202*bf2c3715SXin Li 203*bf2c3715SXin Li /** \internal */ 204*bf2c3715SXin Li template<typename Rhs, typename Dest> 205*bf2c3715SXin Li bool _solve_impl(const MatrixBase<Rhs> &B, MatrixBase<Dest> &dest) const 206*bf2c3715SXin Li { 207*bf2c3715SXin Li eigen_assert(m_isInitialized && "The factorization should be called first, use compute()"); 208*bf2c3715SXin Li eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix"); 209*bf2c3715SXin Li 210*bf2c3715SXin Li Index rank = this->rank(); 211*bf2c3715SXin Li 212*bf2c3715SXin Li // Compute Q^* * b; 213*bf2c3715SXin Li typename Dest::PlainObject y, b; 214*bf2c3715SXin Li y = this->matrixQ().adjoint() * B; 215*bf2c3715SXin Li b = y; 216*bf2c3715SXin Li 217*bf2c3715SXin Li // Solve with the triangular matrix R 218*bf2c3715SXin Li y.resize((std::max<Index>)(cols(),y.rows()),y.cols()); 219*bf2c3715SXin Li y.topRows(rank) = this->matrixR().topLeftCorner(rank, rank).template triangularView<Upper>().solve(b.topRows(rank)); 220*bf2c3715SXin Li y.bottomRows(y.rows()-rank).setZero(); 221*bf2c3715SXin Li 222*bf2c3715SXin Li // Apply the column permutation 223*bf2c3715SXin Li if (m_perm_c.size()) dest = colsPermutation() * y.topRows(cols()); 224*bf2c3715SXin Li else dest = y.topRows(cols()); 225*bf2c3715SXin Li 226*bf2c3715SXin Li m_info = Success; 227*bf2c3715SXin Li return true; 228*bf2c3715SXin Li } 229*bf2c3715SXin Li 230*bf2c3715SXin Li /** Sets the threshold that is used to determine linearly dependent columns during the factorization. 231*bf2c3715SXin Li * 232*bf2c3715SXin Li * In practice, if during the factorization the norm of the column that has to be eliminated is below 233*bf2c3715SXin Li * this threshold, then the entire column is treated as zero, and it is moved at the end. 234*bf2c3715SXin Li */ 235*bf2c3715SXin Li void setPivotThreshold(const RealScalar& threshold) 236*bf2c3715SXin Li { 237*bf2c3715SXin Li m_useDefaultThreshold = false; 238*bf2c3715SXin Li m_threshold = threshold; 239*bf2c3715SXin Li } 240*bf2c3715SXin Li 241*bf2c3715SXin Li /** \returns the solution X of \f$ A X = B \f$ using the current decomposition of A. 242*bf2c3715SXin Li * 243*bf2c3715SXin Li * \sa compute() 244*bf2c3715SXin Li */ 245*bf2c3715SXin Li template<typename Rhs> 246*bf2c3715SXin Li inline const Solve<SparseQR, Rhs> solve(const MatrixBase<Rhs>& B) const 247*bf2c3715SXin Li { 248*bf2c3715SXin Li eigen_assert(m_isInitialized && "The factorization should be called first, use compute()"); 249*bf2c3715SXin Li eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix"); 250*bf2c3715SXin Li return Solve<SparseQR, Rhs>(*this, B.derived()); 251*bf2c3715SXin Li } 252*bf2c3715SXin Li template<typename Rhs> 253*bf2c3715SXin Li inline const Solve<SparseQR, Rhs> solve(const SparseMatrixBase<Rhs>& B) const 254*bf2c3715SXin Li { 255*bf2c3715SXin Li eigen_assert(m_isInitialized && "The factorization should be called first, use compute()"); 256*bf2c3715SXin Li eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix"); 257*bf2c3715SXin Li return Solve<SparseQR, Rhs>(*this, B.derived()); 258*bf2c3715SXin Li } 259*bf2c3715SXin Li 260*bf2c3715SXin Li /** \brief Reports whether previous computation was successful. 261*bf2c3715SXin Li * 262*bf2c3715SXin Li * \returns \c Success if computation was successful, 263*bf2c3715SXin Li * \c NumericalIssue if the QR factorization reports a numerical problem 264*bf2c3715SXin Li * \c InvalidInput if the input matrix is invalid 265*bf2c3715SXin Li * 266*bf2c3715SXin Li * \sa iparm() 267*bf2c3715SXin Li */ 268*bf2c3715SXin Li ComputationInfo info() const 269*bf2c3715SXin Li { 270*bf2c3715SXin Li eigen_assert(m_isInitialized && "Decomposition is not initialized."); 271*bf2c3715SXin Li return m_info; 272*bf2c3715SXin Li } 273*bf2c3715SXin Li 274*bf2c3715SXin Li 275*bf2c3715SXin Li /** \internal */ 276*bf2c3715SXin Li inline void _sort_matrix_Q() 277*bf2c3715SXin Li { 278*bf2c3715SXin Li if(this->m_isQSorted) return; 279*bf2c3715SXin Li // The matrix Q is sorted during the transposition 280*bf2c3715SXin Li SparseMatrix<Scalar, RowMajor, Index> mQrm(this->m_Q); 281*bf2c3715SXin Li this->m_Q = mQrm; 282*bf2c3715SXin Li this->m_isQSorted = true; 283*bf2c3715SXin Li } 284*bf2c3715SXin Li 285*bf2c3715SXin Li 286*bf2c3715SXin Li protected: 287*bf2c3715SXin Li bool m_analysisIsok; 288*bf2c3715SXin Li bool m_factorizationIsok; 289*bf2c3715SXin Li mutable ComputationInfo m_info; 290*bf2c3715SXin Li std::string m_lastError; 291*bf2c3715SXin Li QRMatrixType m_pmat; // Temporary matrix 292*bf2c3715SXin Li QRMatrixType m_R; // The triangular factor matrix 293*bf2c3715SXin Li QRMatrixType m_Q; // The orthogonal reflectors 294*bf2c3715SXin Li ScalarVector m_hcoeffs; // The Householder coefficients 295*bf2c3715SXin Li PermutationType m_perm_c; // Fill-reducing Column permutation 296*bf2c3715SXin Li PermutationType m_pivotperm; // The permutation for rank revealing 297*bf2c3715SXin Li PermutationType m_outputPerm_c; // The final column permutation 298*bf2c3715SXin Li RealScalar m_threshold; // Threshold to determine null Householder reflections 299*bf2c3715SXin Li bool m_useDefaultThreshold; // Use default threshold 300*bf2c3715SXin Li Index m_nonzeropivots; // Number of non zero pivots found 301*bf2c3715SXin Li IndexVector m_etree; // Column elimination tree 302*bf2c3715SXin Li IndexVector m_firstRowElt; // First element in each row 303*bf2c3715SXin Li bool m_isQSorted; // whether Q is sorted or not 304*bf2c3715SXin Li bool m_isEtreeOk; // whether the elimination tree match the initial input matrix 305*bf2c3715SXin Li 306*bf2c3715SXin Li template <typename, typename > friend struct SparseQR_QProduct; 307*bf2c3715SXin Li 308*bf2c3715SXin Li }; 309*bf2c3715SXin Li 310*bf2c3715SXin Li /** \brief Preprocessing step of a QR factorization 311*bf2c3715SXin Li * 312*bf2c3715SXin Li * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()). 313*bf2c3715SXin Li * 314*bf2c3715SXin Li * In this step, the fill-reducing permutation is computed and applied to the columns of A 315*bf2c3715SXin Li * and the column elimination tree is computed as well. Only the sparsity pattern of \a mat is exploited. 316*bf2c3715SXin Li * 317*bf2c3715SXin Li * \note In this step it is assumed that there is no empty row in the matrix \a mat. 318*bf2c3715SXin Li */ 319*bf2c3715SXin Li template <typename MatrixType, typename OrderingType> 320*bf2c3715SXin Li void SparseQR<MatrixType,OrderingType>::analyzePattern(const MatrixType& mat) 321*bf2c3715SXin Li { 322*bf2c3715SXin Li eigen_assert(mat.isCompressed() && "SparseQR requires a sparse matrix in compressed mode. Call .makeCompressed() before passing it to SparseQR"); 323*bf2c3715SXin Li // Copy to a column major matrix if the input is rowmajor 324*bf2c3715SXin Li typename internal::conditional<MatrixType::IsRowMajor,QRMatrixType,const MatrixType&>::type matCpy(mat); 325*bf2c3715SXin Li // Compute the column fill reducing ordering 326*bf2c3715SXin Li OrderingType ord; 327*bf2c3715SXin Li ord(matCpy, m_perm_c); 328*bf2c3715SXin Li Index n = mat.cols(); 329*bf2c3715SXin Li Index m = mat.rows(); 330*bf2c3715SXin Li Index diagSize = (std::min)(m,n); 331*bf2c3715SXin Li 332*bf2c3715SXin Li if (!m_perm_c.size()) 333*bf2c3715SXin Li { 334*bf2c3715SXin Li m_perm_c.resize(n); 335*bf2c3715SXin Li m_perm_c.indices().setLinSpaced(n, 0,StorageIndex(n-1)); 336*bf2c3715SXin Li } 337*bf2c3715SXin Li 338*bf2c3715SXin Li // Compute the column elimination tree of the permuted matrix 339*bf2c3715SXin Li m_outputPerm_c = m_perm_c.inverse(); 340*bf2c3715SXin Li internal::coletree(matCpy, m_etree, m_firstRowElt, m_outputPerm_c.indices().data()); 341*bf2c3715SXin Li m_isEtreeOk = true; 342*bf2c3715SXin Li 343*bf2c3715SXin Li m_R.resize(m, n); 344*bf2c3715SXin Li m_Q.resize(m, diagSize); 345*bf2c3715SXin Li 346*bf2c3715SXin Li // Allocate space for nonzero elements: rough estimation 347*bf2c3715SXin Li m_R.reserve(2*mat.nonZeros()); //FIXME Get a more accurate estimation through symbolic factorization with the etree 348*bf2c3715SXin Li m_Q.reserve(2*mat.nonZeros()); 349*bf2c3715SXin Li m_hcoeffs.resize(diagSize); 350*bf2c3715SXin Li m_analysisIsok = true; 351*bf2c3715SXin Li } 352*bf2c3715SXin Li 353*bf2c3715SXin Li /** \brief Performs the numerical QR factorization of the input matrix 354*bf2c3715SXin Li * 355*bf2c3715SXin Li * The function SparseQR::analyzePattern(const MatrixType&) must have been called beforehand with 356*bf2c3715SXin Li * a matrix having the same sparsity pattern than \a mat. 357*bf2c3715SXin Li * 358*bf2c3715SXin Li * \param mat The sparse column-major matrix 359*bf2c3715SXin Li */ 360*bf2c3715SXin Li template <typename MatrixType, typename OrderingType> 361*bf2c3715SXin Li void SparseQR<MatrixType,OrderingType>::factorize(const MatrixType& mat) 362*bf2c3715SXin Li { 363*bf2c3715SXin Li using std::abs; 364*bf2c3715SXin Li 365*bf2c3715SXin Li eigen_assert(m_analysisIsok && "analyzePattern() should be called before this step"); 366*bf2c3715SXin Li StorageIndex m = StorageIndex(mat.rows()); 367*bf2c3715SXin Li StorageIndex n = StorageIndex(mat.cols()); 368*bf2c3715SXin Li StorageIndex diagSize = (std::min)(m,n); 369*bf2c3715SXin Li IndexVector mark((std::max)(m,n)); mark.setConstant(-1); // Record the visited nodes 370*bf2c3715SXin Li IndexVector Ridx(n), Qidx(m); // Store temporarily the row indexes for the current column of R and Q 371*bf2c3715SXin Li Index nzcolR, nzcolQ; // Number of nonzero for the current column of R and Q 372*bf2c3715SXin Li ScalarVector tval(m); // The dense vector used to compute the current column 373*bf2c3715SXin Li RealScalar pivotThreshold = m_threshold; 374*bf2c3715SXin Li 375*bf2c3715SXin Li m_R.setZero(); 376*bf2c3715SXin Li m_Q.setZero(); 377*bf2c3715SXin Li m_pmat = mat; 378*bf2c3715SXin Li if(!m_isEtreeOk) 379*bf2c3715SXin Li { 380*bf2c3715SXin Li m_outputPerm_c = m_perm_c.inverse(); 381*bf2c3715SXin Li internal::coletree(m_pmat, m_etree, m_firstRowElt, m_outputPerm_c.indices().data()); 382*bf2c3715SXin Li m_isEtreeOk = true; 383*bf2c3715SXin Li } 384*bf2c3715SXin Li 385*bf2c3715SXin Li m_pmat.uncompress(); // To have the innerNonZeroPtr allocated 386*bf2c3715SXin Li 387*bf2c3715SXin Li // Apply the fill-in reducing permutation lazily: 388*bf2c3715SXin Li { 389*bf2c3715SXin Li // If the input is row major, copy the original column indices, 390*bf2c3715SXin Li // otherwise directly use the input matrix 391*bf2c3715SXin Li // 392*bf2c3715SXin Li IndexVector originalOuterIndicesCpy; 393*bf2c3715SXin Li const StorageIndex *originalOuterIndices = mat.outerIndexPtr(); 394*bf2c3715SXin Li if(MatrixType::IsRowMajor) 395*bf2c3715SXin Li { 396*bf2c3715SXin Li originalOuterIndicesCpy = IndexVector::Map(m_pmat.outerIndexPtr(),n+1); 397*bf2c3715SXin Li originalOuterIndices = originalOuterIndicesCpy.data(); 398*bf2c3715SXin Li } 399*bf2c3715SXin Li 400*bf2c3715SXin Li for (int i = 0; i < n; i++) 401*bf2c3715SXin Li { 402*bf2c3715SXin Li Index p = m_perm_c.size() ? m_perm_c.indices()(i) : i; 403*bf2c3715SXin Li m_pmat.outerIndexPtr()[p] = originalOuterIndices[i]; 404*bf2c3715SXin Li m_pmat.innerNonZeroPtr()[p] = originalOuterIndices[i+1] - originalOuterIndices[i]; 405*bf2c3715SXin Li } 406*bf2c3715SXin Li } 407*bf2c3715SXin Li 408*bf2c3715SXin Li /* Compute the default threshold as in MatLab, see: 409*bf2c3715SXin Li * Tim Davis, "Algorithm 915, SuiteSparseQR: Multifrontal Multithreaded Rank-Revealing 410*bf2c3715SXin Li * Sparse QR Factorization, ACM Trans. on Math. Soft. 38(1), 2011, Page 8:3 411*bf2c3715SXin Li */ 412*bf2c3715SXin Li if(m_useDefaultThreshold) 413*bf2c3715SXin Li { 414*bf2c3715SXin Li RealScalar max2Norm = 0.0; 415*bf2c3715SXin Li for (int j = 0; j < n; j++) max2Norm = numext::maxi(max2Norm, m_pmat.col(j).norm()); 416*bf2c3715SXin Li if(max2Norm==RealScalar(0)) 417*bf2c3715SXin Li max2Norm = RealScalar(1); 418*bf2c3715SXin Li pivotThreshold = 20 * (m + n) * max2Norm * NumTraits<RealScalar>::epsilon(); 419*bf2c3715SXin Li } 420*bf2c3715SXin Li 421*bf2c3715SXin Li // Initialize the numerical permutation 422*bf2c3715SXin Li m_pivotperm.setIdentity(n); 423*bf2c3715SXin Li 424*bf2c3715SXin Li StorageIndex nonzeroCol = 0; // Record the number of valid pivots 425*bf2c3715SXin Li m_Q.startVec(0); 426*bf2c3715SXin Li 427*bf2c3715SXin Li // Left looking rank-revealing QR factorization: compute a column of R and Q at a time 428*bf2c3715SXin Li for (StorageIndex col = 0; col < n; ++col) 429*bf2c3715SXin Li { 430*bf2c3715SXin Li mark.setConstant(-1); 431*bf2c3715SXin Li m_R.startVec(col); 432*bf2c3715SXin Li mark(nonzeroCol) = col; 433*bf2c3715SXin Li Qidx(0) = nonzeroCol; 434*bf2c3715SXin Li nzcolR = 0; nzcolQ = 1; 435*bf2c3715SXin Li bool found_diag = nonzeroCol>=m; 436*bf2c3715SXin Li tval.setZero(); 437*bf2c3715SXin Li 438*bf2c3715SXin Li // Symbolic factorization: find the nonzero locations of the column k of the factors R and Q, i.e., 439*bf2c3715SXin Li // all the nodes (with indexes lower than rank) reachable through the column elimination tree (etree) rooted at node k. 440*bf2c3715SXin Li // Note: if the diagonal entry does not exist, then its contribution must be explicitly added, 441*bf2c3715SXin Li // thus the trick with found_diag that permits to do one more iteration on the diagonal element if this one has not been found. 442*bf2c3715SXin Li for (typename QRMatrixType::InnerIterator itp(m_pmat, col); itp || !found_diag; ++itp) 443*bf2c3715SXin Li { 444*bf2c3715SXin Li StorageIndex curIdx = nonzeroCol; 445*bf2c3715SXin Li if(itp) curIdx = StorageIndex(itp.row()); 446*bf2c3715SXin Li if(curIdx == nonzeroCol) found_diag = true; 447*bf2c3715SXin Li 448*bf2c3715SXin Li // Get the nonzeros indexes of the current column of R 449*bf2c3715SXin Li StorageIndex st = m_firstRowElt(curIdx); // The traversal of the etree starts here 450*bf2c3715SXin Li if (st < 0 ) 451*bf2c3715SXin Li { 452*bf2c3715SXin Li m_lastError = "Empty row found during numerical factorization"; 453*bf2c3715SXin Li m_info = InvalidInput; 454*bf2c3715SXin Li return; 455*bf2c3715SXin Li } 456*bf2c3715SXin Li 457*bf2c3715SXin Li // Traverse the etree 458*bf2c3715SXin Li Index bi = nzcolR; 459*bf2c3715SXin Li for (; mark(st) != col; st = m_etree(st)) 460*bf2c3715SXin Li { 461*bf2c3715SXin Li Ridx(nzcolR) = st; // Add this row to the list, 462*bf2c3715SXin Li mark(st) = col; // and mark this row as visited 463*bf2c3715SXin Li nzcolR++; 464*bf2c3715SXin Li } 465*bf2c3715SXin Li 466*bf2c3715SXin Li // Reverse the list to get the topological ordering 467*bf2c3715SXin Li Index nt = nzcolR-bi; 468*bf2c3715SXin Li for(Index i = 0; i < nt/2; i++) std::swap(Ridx(bi+i), Ridx(nzcolR-i-1)); 469*bf2c3715SXin Li 470*bf2c3715SXin Li // Copy the current (curIdx,pcol) value of the input matrix 471*bf2c3715SXin Li if(itp) tval(curIdx) = itp.value(); 472*bf2c3715SXin Li else tval(curIdx) = Scalar(0); 473*bf2c3715SXin Li 474*bf2c3715SXin Li // Compute the pattern of Q(:,k) 475*bf2c3715SXin Li if(curIdx > nonzeroCol && mark(curIdx) != col ) 476*bf2c3715SXin Li { 477*bf2c3715SXin Li Qidx(nzcolQ) = curIdx; // Add this row to the pattern of Q, 478*bf2c3715SXin Li mark(curIdx) = col; // and mark it as visited 479*bf2c3715SXin Li nzcolQ++; 480*bf2c3715SXin Li } 481*bf2c3715SXin Li } 482*bf2c3715SXin Li 483*bf2c3715SXin Li // Browse all the indexes of R(:,col) in reverse order 484*bf2c3715SXin Li for (Index i = nzcolR-1; i >= 0; i--) 485*bf2c3715SXin Li { 486*bf2c3715SXin Li Index curIdx = Ridx(i); 487*bf2c3715SXin Li 488*bf2c3715SXin Li // Apply the curIdx-th householder vector to the current column (temporarily stored into tval) 489*bf2c3715SXin Li Scalar tdot(0); 490*bf2c3715SXin Li 491*bf2c3715SXin Li // First compute q' * tval 492*bf2c3715SXin Li tdot = m_Q.col(curIdx).dot(tval); 493*bf2c3715SXin Li 494*bf2c3715SXin Li tdot *= m_hcoeffs(curIdx); 495*bf2c3715SXin Li 496*bf2c3715SXin Li // Then update tval = tval - q * tau 497*bf2c3715SXin Li // FIXME: tval -= tdot * m_Q.col(curIdx) should amount to the same (need to check/add support for efficient "dense ?= sparse") 498*bf2c3715SXin Li for (typename QRMatrixType::InnerIterator itq(m_Q, curIdx); itq; ++itq) 499*bf2c3715SXin Li tval(itq.row()) -= itq.value() * tdot; 500*bf2c3715SXin Li 501*bf2c3715SXin Li // Detect fill-in for the current column of Q 502*bf2c3715SXin Li if(m_etree(Ridx(i)) == nonzeroCol) 503*bf2c3715SXin Li { 504*bf2c3715SXin Li for (typename QRMatrixType::InnerIterator itq(m_Q, curIdx); itq; ++itq) 505*bf2c3715SXin Li { 506*bf2c3715SXin Li StorageIndex iQ = StorageIndex(itq.row()); 507*bf2c3715SXin Li if (mark(iQ) != col) 508*bf2c3715SXin Li { 509*bf2c3715SXin Li Qidx(nzcolQ++) = iQ; // Add this row to the pattern of Q, 510*bf2c3715SXin Li mark(iQ) = col; // and mark it as visited 511*bf2c3715SXin Li } 512*bf2c3715SXin Li } 513*bf2c3715SXin Li } 514*bf2c3715SXin Li } // End update current column 515*bf2c3715SXin Li 516*bf2c3715SXin Li Scalar tau = RealScalar(0); 517*bf2c3715SXin Li RealScalar beta = 0; 518*bf2c3715SXin Li 519*bf2c3715SXin Li if(nonzeroCol < diagSize) 520*bf2c3715SXin Li { 521*bf2c3715SXin Li // Compute the Householder reflection that eliminate the current column 522*bf2c3715SXin Li // FIXME this step should call the Householder module. 523*bf2c3715SXin Li Scalar c0 = nzcolQ ? tval(Qidx(0)) : Scalar(0); 524*bf2c3715SXin Li 525*bf2c3715SXin Li // First, the squared norm of Q((col+1):m, col) 526*bf2c3715SXin Li RealScalar sqrNorm = 0.; 527*bf2c3715SXin Li for (Index itq = 1; itq < nzcolQ; ++itq) sqrNorm += numext::abs2(tval(Qidx(itq))); 528*bf2c3715SXin Li if(sqrNorm == RealScalar(0) && numext::imag(c0) == RealScalar(0)) 529*bf2c3715SXin Li { 530*bf2c3715SXin Li beta = numext::real(c0); 531*bf2c3715SXin Li tval(Qidx(0)) = 1; 532*bf2c3715SXin Li } 533*bf2c3715SXin Li else 534*bf2c3715SXin Li { 535*bf2c3715SXin Li using std::sqrt; 536*bf2c3715SXin Li beta = sqrt(numext::abs2(c0) + sqrNorm); 537*bf2c3715SXin Li if(numext::real(c0) >= RealScalar(0)) 538*bf2c3715SXin Li beta = -beta; 539*bf2c3715SXin Li tval(Qidx(0)) = 1; 540*bf2c3715SXin Li for (Index itq = 1; itq < nzcolQ; ++itq) 541*bf2c3715SXin Li tval(Qidx(itq)) /= (c0 - beta); 542*bf2c3715SXin Li tau = numext::conj((beta-c0) / beta); 543*bf2c3715SXin Li 544*bf2c3715SXin Li } 545*bf2c3715SXin Li } 546*bf2c3715SXin Li 547*bf2c3715SXin Li // Insert values in R 548*bf2c3715SXin Li for (Index i = nzcolR-1; i >= 0; i--) 549*bf2c3715SXin Li { 550*bf2c3715SXin Li Index curIdx = Ridx(i); 551*bf2c3715SXin Li if(curIdx < nonzeroCol) 552*bf2c3715SXin Li { 553*bf2c3715SXin Li m_R.insertBackByOuterInnerUnordered(col, curIdx) = tval(curIdx); 554*bf2c3715SXin Li tval(curIdx) = Scalar(0.); 555*bf2c3715SXin Li } 556*bf2c3715SXin Li } 557*bf2c3715SXin Li 558*bf2c3715SXin Li if(nonzeroCol < diagSize && abs(beta) >= pivotThreshold) 559*bf2c3715SXin Li { 560*bf2c3715SXin Li m_R.insertBackByOuterInner(col, nonzeroCol) = beta; 561*bf2c3715SXin Li // The householder coefficient 562*bf2c3715SXin Li m_hcoeffs(nonzeroCol) = tau; 563*bf2c3715SXin Li // Record the householder reflections 564*bf2c3715SXin Li for (Index itq = 0; itq < nzcolQ; ++itq) 565*bf2c3715SXin Li { 566*bf2c3715SXin Li Index iQ = Qidx(itq); 567*bf2c3715SXin Li m_Q.insertBackByOuterInnerUnordered(nonzeroCol,iQ) = tval(iQ); 568*bf2c3715SXin Li tval(iQ) = Scalar(0.); 569*bf2c3715SXin Li } 570*bf2c3715SXin Li nonzeroCol++; 571*bf2c3715SXin Li if(nonzeroCol<diagSize) 572*bf2c3715SXin Li m_Q.startVec(nonzeroCol); 573*bf2c3715SXin Li } 574*bf2c3715SXin Li else 575*bf2c3715SXin Li { 576*bf2c3715SXin Li // Zero pivot found: move implicitly this column to the end 577*bf2c3715SXin Li for (Index j = nonzeroCol; j < n-1; j++) 578*bf2c3715SXin Li std::swap(m_pivotperm.indices()(j), m_pivotperm.indices()[j+1]); 579*bf2c3715SXin Li 580*bf2c3715SXin Li // Recompute the column elimination tree 581*bf2c3715SXin Li internal::coletree(m_pmat, m_etree, m_firstRowElt, m_pivotperm.indices().data()); 582*bf2c3715SXin Li m_isEtreeOk = false; 583*bf2c3715SXin Li } 584*bf2c3715SXin Li } 585*bf2c3715SXin Li 586*bf2c3715SXin Li m_hcoeffs.tail(diagSize-nonzeroCol).setZero(); 587*bf2c3715SXin Li 588*bf2c3715SXin Li // Finalize the column pointers of the sparse matrices R and Q 589*bf2c3715SXin Li m_Q.finalize(); 590*bf2c3715SXin Li m_Q.makeCompressed(); 591*bf2c3715SXin Li m_R.finalize(); 592*bf2c3715SXin Li m_R.makeCompressed(); 593*bf2c3715SXin Li m_isQSorted = false; 594*bf2c3715SXin Li 595*bf2c3715SXin Li m_nonzeropivots = nonzeroCol; 596*bf2c3715SXin Li 597*bf2c3715SXin Li if(nonzeroCol<n) 598*bf2c3715SXin Li { 599*bf2c3715SXin Li // Permute the triangular factor to put the 'dead' columns to the end 600*bf2c3715SXin Li QRMatrixType tempR(m_R); 601*bf2c3715SXin Li m_R = tempR * m_pivotperm; 602*bf2c3715SXin Li 603*bf2c3715SXin Li // Update the column permutation 604*bf2c3715SXin Li m_outputPerm_c = m_outputPerm_c * m_pivotperm; 605*bf2c3715SXin Li } 606*bf2c3715SXin Li 607*bf2c3715SXin Li m_isInitialized = true; 608*bf2c3715SXin Li m_factorizationIsok = true; 609*bf2c3715SXin Li m_info = Success; 610*bf2c3715SXin Li } 611*bf2c3715SXin Li 612*bf2c3715SXin Li template <typename SparseQRType, typename Derived> 613*bf2c3715SXin Li struct SparseQR_QProduct : ReturnByValue<SparseQR_QProduct<SparseQRType, Derived> > 614*bf2c3715SXin Li { 615*bf2c3715SXin Li typedef typename SparseQRType::QRMatrixType MatrixType; 616*bf2c3715SXin Li typedef typename SparseQRType::Scalar Scalar; 617*bf2c3715SXin Li // Get the references 618*bf2c3715SXin Li SparseQR_QProduct(const SparseQRType& qr, const Derived& other, bool transpose) : 619*bf2c3715SXin Li m_qr(qr),m_other(other),m_transpose(transpose) {} 620*bf2c3715SXin Li inline Index rows() const { return m_qr.matrixQ().rows(); } 621*bf2c3715SXin Li inline Index cols() const { return m_other.cols(); } 622*bf2c3715SXin Li 623*bf2c3715SXin Li // Assign to a vector 624*bf2c3715SXin Li template<typename DesType> 625*bf2c3715SXin Li void evalTo(DesType& res) const 626*bf2c3715SXin Li { 627*bf2c3715SXin Li Index m = m_qr.rows(); 628*bf2c3715SXin Li Index n = m_qr.cols(); 629*bf2c3715SXin Li Index diagSize = (std::min)(m,n); 630*bf2c3715SXin Li res = m_other; 631*bf2c3715SXin Li if (m_transpose) 632*bf2c3715SXin Li { 633*bf2c3715SXin Li eigen_assert(m_qr.m_Q.rows() == m_other.rows() && "Non conforming object sizes"); 634*bf2c3715SXin Li //Compute res = Q' * other column by column 635*bf2c3715SXin Li for(Index j = 0; j < res.cols(); j++){ 636*bf2c3715SXin Li for (Index k = 0; k < diagSize; k++) 637*bf2c3715SXin Li { 638*bf2c3715SXin Li Scalar tau = Scalar(0); 639*bf2c3715SXin Li tau = m_qr.m_Q.col(k).dot(res.col(j)); 640*bf2c3715SXin Li if(tau==Scalar(0)) continue; 641*bf2c3715SXin Li tau = tau * m_qr.m_hcoeffs(k); 642*bf2c3715SXin Li res.col(j) -= tau * m_qr.m_Q.col(k); 643*bf2c3715SXin Li } 644*bf2c3715SXin Li } 645*bf2c3715SXin Li } 646*bf2c3715SXin Li else 647*bf2c3715SXin Li { 648*bf2c3715SXin Li eigen_assert(m_qr.matrixQ().cols() == m_other.rows() && "Non conforming object sizes"); 649*bf2c3715SXin Li 650*bf2c3715SXin Li res.conservativeResize(rows(), cols()); 651*bf2c3715SXin Li 652*bf2c3715SXin Li // Compute res = Q * other column by column 653*bf2c3715SXin Li for(Index j = 0; j < res.cols(); j++) 654*bf2c3715SXin Li { 655*bf2c3715SXin Li Index start_k = internal::is_identity<Derived>::value ? numext::mini(j,diagSize-1) : diagSize-1; 656*bf2c3715SXin Li for (Index k = start_k; k >=0; k--) 657*bf2c3715SXin Li { 658*bf2c3715SXin Li Scalar tau = Scalar(0); 659*bf2c3715SXin Li tau = m_qr.m_Q.col(k).dot(res.col(j)); 660*bf2c3715SXin Li if(tau==Scalar(0)) continue; 661*bf2c3715SXin Li tau = tau * numext::conj(m_qr.m_hcoeffs(k)); 662*bf2c3715SXin Li res.col(j) -= tau * m_qr.m_Q.col(k); 663*bf2c3715SXin Li } 664*bf2c3715SXin Li } 665*bf2c3715SXin Li } 666*bf2c3715SXin Li } 667*bf2c3715SXin Li 668*bf2c3715SXin Li const SparseQRType& m_qr; 669*bf2c3715SXin Li const Derived& m_other; 670*bf2c3715SXin Li bool m_transpose; // TODO this actually means adjoint 671*bf2c3715SXin Li }; 672*bf2c3715SXin Li 673*bf2c3715SXin Li template<typename SparseQRType> 674*bf2c3715SXin Li struct SparseQRMatrixQReturnType : public EigenBase<SparseQRMatrixQReturnType<SparseQRType> > 675*bf2c3715SXin Li { 676*bf2c3715SXin Li typedef typename SparseQRType::Scalar Scalar; 677*bf2c3715SXin Li typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; 678*bf2c3715SXin Li enum { 679*bf2c3715SXin Li RowsAtCompileTime = Dynamic, 680*bf2c3715SXin Li ColsAtCompileTime = Dynamic 681*bf2c3715SXin Li }; 682*bf2c3715SXin Li explicit SparseQRMatrixQReturnType(const SparseQRType& qr) : m_qr(qr) {} 683*bf2c3715SXin Li template<typename Derived> 684*bf2c3715SXin Li SparseQR_QProduct<SparseQRType, Derived> operator*(const MatrixBase<Derived>& other) 685*bf2c3715SXin Li { 686*bf2c3715SXin Li return SparseQR_QProduct<SparseQRType,Derived>(m_qr,other.derived(),false); 687*bf2c3715SXin Li } 688*bf2c3715SXin Li // To use for operations with the adjoint of Q 689*bf2c3715SXin Li SparseQRMatrixQTransposeReturnType<SparseQRType> adjoint() const 690*bf2c3715SXin Li { 691*bf2c3715SXin Li return SparseQRMatrixQTransposeReturnType<SparseQRType>(m_qr); 692*bf2c3715SXin Li } 693*bf2c3715SXin Li inline Index rows() const { return m_qr.rows(); } 694*bf2c3715SXin Li inline Index cols() const { return m_qr.rows(); } 695*bf2c3715SXin Li // To use for operations with the transpose of Q FIXME this is the same as adjoint at the moment 696*bf2c3715SXin Li SparseQRMatrixQTransposeReturnType<SparseQRType> transpose() const 697*bf2c3715SXin Li { 698*bf2c3715SXin Li return SparseQRMatrixQTransposeReturnType<SparseQRType>(m_qr); 699*bf2c3715SXin Li } 700*bf2c3715SXin Li const SparseQRType& m_qr; 701*bf2c3715SXin Li }; 702*bf2c3715SXin Li 703*bf2c3715SXin Li // TODO this actually represents the adjoint of Q 704*bf2c3715SXin Li template<typename SparseQRType> 705*bf2c3715SXin Li struct SparseQRMatrixQTransposeReturnType 706*bf2c3715SXin Li { 707*bf2c3715SXin Li explicit SparseQRMatrixQTransposeReturnType(const SparseQRType& qr) : m_qr(qr) {} 708*bf2c3715SXin Li template<typename Derived> 709*bf2c3715SXin Li SparseQR_QProduct<SparseQRType,Derived> operator*(const MatrixBase<Derived>& other) 710*bf2c3715SXin Li { 711*bf2c3715SXin Li return SparseQR_QProduct<SparseQRType,Derived>(m_qr,other.derived(), true); 712*bf2c3715SXin Li } 713*bf2c3715SXin Li const SparseQRType& m_qr; 714*bf2c3715SXin Li }; 715*bf2c3715SXin Li 716*bf2c3715SXin Li namespace internal { 717*bf2c3715SXin Li 718*bf2c3715SXin Li template<typename SparseQRType> 719*bf2c3715SXin Li struct evaluator_traits<SparseQRMatrixQReturnType<SparseQRType> > 720*bf2c3715SXin Li { 721*bf2c3715SXin Li typedef typename SparseQRType::MatrixType MatrixType; 722*bf2c3715SXin Li typedef typename storage_kind_to_evaluator_kind<typename MatrixType::StorageKind>::Kind Kind; 723*bf2c3715SXin Li typedef SparseShape Shape; 724*bf2c3715SXin Li }; 725*bf2c3715SXin Li 726*bf2c3715SXin Li template< typename DstXprType, typename SparseQRType> 727*bf2c3715SXin Li struct Assignment<DstXprType, SparseQRMatrixQReturnType<SparseQRType>, internal::assign_op<typename DstXprType::Scalar,typename DstXprType::Scalar>, Sparse2Sparse> 728*bf2c3715SXin Li { 729*bf2c3715SXin Li typedef SparseQRMatrixQReturnType<SparseQRType> SrcXprType; 730*bf2c3715SXin Li typedef typename DstXprType::Scalar Scalar; 731*bf2c3715SXin Li typedef typename DstXprType::StorageIndex StorageIndex; 732*bf2c3715SXin Li static void run(DstXprType &dst, const SrcXprType &src, const internal::assign_op<Scalar,Scalar> &/*func*/) 733*bf2c3715SXin Li { 734*bf2c3715SXin Li typename DstXprType::PlainObject idMat(src.rows(), src.cols()); 735*bf2c3715SXin Li idMat.setIdentity(); 736*bf2c3715SXin Li // Sort the sparse householder reflectors if needed 737*bf2c3715SXin Li const_cast<SparseQRType *>(&src.m_qr)->_sort_matrix_Q(); 738*bf2c3715SXin Li dst = SparseQR_QProduct<SparseQRType, DstXprType>(src.m_qr, idMat, false); 739*bf2c3715SXin Li } 740*bf2c3715SXin Li }; 741*bf2c3715SXin Li 742*bf2c3715SXin Li template< typename DstXprType, typename SparseQRType> 743*bf2c3715SXin Li struct Assignment<DstXprType, SparseQRMatrixQReturnType<SparseQRType>, internal::assign_op<typename DstXprType::Scalar,typename DstXprType::Scalar>, Sparse2Dense> 744*bf2c3715SXin Li { 745*bf2c3715SXin Li typedef SparseQRMatrixQReturnType<SparseQRType> SrcXprType; 746*bf2c3715SXin Li typedef typename DstXprType::Scalar Scalar; 747*bf2c3715SXin Li typedef typename DstXprType::StorageIndex StorageIndex; 748*bf2c3715SXin Li static void run(DstXprType &dst, const SrcXprType &src, const internal::assign_op<Scalar,Scalar> &/*func*/) 749*bf2c3715SXin Li { 750*bf2c3715SXin Li dst = src.m_qr.matrixQ() * DstXprType::Identity(src.m_qr.rows(), src.m_qr.rows()); 751*bf2c3715SXin Li } 752*bf2c3715SXin Li }; 753*bf2c3715SXin Li 754*bf2c3715SXin Li } // end namespace internal 755*bf2c3715SXin Li 756*bf2c3715SXin Li } // end namespace Eigen 757*bf2c3715SXin Li 758*bf2c3715SXin Li #endif 759