Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame^] | 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 |