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