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