diff options
Diffstat (limited to 'eigen/Eigen/src/SparseQR/SparseQR.h')
-rw-r--r-- | eigen/Eigen/src/SparseQR/SparseQR.h | 714 |
1 files changed, 714 insertions, 0 deletions
diff --git a/eigen/Eigen/src/SparseQR/SparseQR.h b/eigen/Eigen/src/SparseQR/SparseQR.h new file mode 100644 index 0000000..a00bd5d --- /dev/null +++ b/eigen/Eigen/src/SparseQR/SparseQR.h @@ -0,0 +1,714 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2012-2013 Desire Nuentsa <desire.nuentsa_wakam@inria.fr> +// Copyright (C) 2012-2014 Gael Guennebaud <gael.guennebaud@inria.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_QR_H +#define EIGEN_SPARSE_QR_H + +namespace Eigen { + +template<typename MatrixType, typename OrderingType> class SparseQR; +template<typename SparseQRType> struct SparseQRMatrixQReturnType; +template<typename SparseQRType> struct SparseQRMatrixQTransposeReturnType; +template<typename SparseQRType, typename Derived> struct SparseQR_QProduct; +namespace internal { + template <typename SparseQRType> struct traits<SparseQRMatrixQReturnType<SparseQRType> > + { + typedef typename SparseQRType::MatrixType ReturnType; + typedef typename ReturnType::Index Index; + typedef typename ReturnType::StorageKind StorageKind; + }; + template <typename SparseQRType> struct traits<SparseQRMatrixQTransposeReturnType<SparseQRType> > + { + typedef typename SparseQRType::MatrixType ReturnType; + }; + template <typename SparseQRType, typename Derived> struct traits<SparseQR_QProduct<SparseQRType, Derived> > + { + typedef typename Derived::PlainObject ReturnType; + }; +} // End namespace internal + +/** + * \ingroup SparseQR_Module + * \class SparseQR + * \brief Sparse left-looking rank-revealing QR factorization + * + * This class implements a left-looking rank-revealing QR decomposition + * of sparse matrices. When a column has a norm less than a given tolerance + * it is implicitly permuted to the end. The QR factorization thus obtained is + * given by A*P = Q*R where R is upper triangular or trapezoidal. + * + * P is the column permutation which is the product of the fill-reducing and the + * rank-revealing permutations. Use colsPermutation() to get it. + * + * Q is the orthogonal matrix represented as products of Householder reflectors. + * Use matrixQ() to get an expression and matrixQ().transpose() to get the transpose. + * You can then apply it to a vector. + * + * R is the sparse triangular or trapezoidal matrix. The later occurs when A is rank-deficient. + * matrixR().topLeftCorner(rank(), rank()) always returns a triangular factor of full rank. + * + * \tparam _MatrixType The type of the sparse matrix A, must be a column-major SparseMatrix<> + * \tparam _OrderingType The fill-reducing ordering method. See the \link OrderingMethods_Module + * OrderingMethods \endlink module for the list of built-in and external ordering methods. + * + * \warning The input sparse matrix A must be in compressed mode (see SparseMatrix::makeCompressed()). + * + */ +template<typename _MatrixType, typename _OrderingType> +class SparseQR +{ + public: + typedef _MatrixType MatrixType; + typedef _OrderingType OrderingType; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + typedef typename MatrixType::Index Index; + typedef SparseMatrix<Scalar,ColMajor,Index> QRMatrixType; + typedef Matrix<Index, Dynamic, 1> IndexVector; + typedef Matrix<Scalar, Dynamic, 1> ScalarVector; + typedef PermutationMatrix<Dynamic, Dynamic, Index> PermutationType; + public: + SparseQR () : m_isInitialized(false), m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false),m_isEtreeOk(false) + { } + + /** Construct a QR factorization of the matrix \a mat. + * + * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()). + * + * \sa compute() + */ + SparseQR(const MatrixType& mat) : m_isInitialized(false), m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false),m_isEtreeOk(false) + { + compute(mat); + } + + /** Computes the QR factorization of the sparse matrix \a mat. + * + * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()). + * + * \sa analyzePattern(), factorize() + */ + void compute(const MatrixType& mat) + { + analyzePattern(mat); + factorize(mat); + } + void analyzePattern(const MatrixType& mat); + void factorize(const MatrixType& mat); + + /** \returns the number of rows of the represented matrix. + */ + inline Index rows() const { return m_pmat.rows(); } + + /** \returns the number of columns of the represented matrix. + */ + inline Index cols() const { return m_pmat.cols();} + + /** \returns a const reference to the \b sparse upper triangular matrix R of the QR factorization. + */ + const QRMatrixType& matrixR() const { return m_R; } + + /** \returns the number of non linearly dependent columns as determined by the pivoting threshold. + * + * \sa setPivotThreshold() + */ + Index rank() const + { + eigen_assert(m_isInitialized && "The factorization should be called first, use compute()"); + return m_nonzeropivots; + } + + /** \returns an expression of the matrix Q as products of sparse Householder reflectors. + * The common usage of this function is to apply it to a dense matrix or vector + * \code + * VectorXd B1, B2; + * // Initialize B1 + * B2 = matrixQ() * B1; + * \endcode + * + * To get a plain SparseMatrix representation of Q: + * \code + * SparseMatrix<double> Q; + * Q = SparseQR<SparseMatrix<double> >(A).matrixQ(); + * \endcode + * Internally, this call simply performs a sparse product between the matrix Q + * and a sparse identity matrix. However, due to the fact that the sparse + * reflectors are stored unsorted, two transpositions are needed to sort + * them before performing the product. + */ + SparseQRMatrixQReturnType<SparseQR> matrixQ() const + { return SparseQRMatrixQReturnType<SparseQR>(*this); } + + /** \returns a const reference to the column permutation P that was applied to A such that A*P = Q*R + * It is the combination of the fill-in reducing permutation and numerical column pivoting. + */ + const PermutationType& colsPermutation() const + { + eigen_assert(m_isInitialized && "Decomposition is not initialized."); + return m_outputPerm_c; + } + + /** \returns A string describing the type of error. + * This method is provided to ease debugging, not to handle errors. + */ + std::string lastErrorMessage() const { return m_lastError; } + + /** \internal */ + template<typename Rhs, typename Dest> + bool _solve(const MatrixBase<Rhs> &B, MatrixBase<Dest> &dest) const + { + eigen_assert(m_isInitialized && "The factorization should be called first, use compute()"); + eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix"); + + Index rank = this->rank(); + + // Compute Q^T * b; + typename Dest::PlainObject y, b; + y = this->matrixQ().transpose() * B; + b = y; + + // Solve with the triangular matrix R + y.resize((std::max)(cols(),Index(y.rows())),y.cols()); + y.topRows(rank) = this->matrixR().topLeftCorner(rank, rank).template triangularView<Upper>().solve(b.topRows(rank)); + y.bottomRows(y.rows()-rank).setZero(); + + // Apply the column permutation + if (m_perm_c.size()) dest = colsPermutation() * y.topRows(cols()); + else dest = y.topRows(cols()); + + m_info = Success; + return true; + } + + + /** Sets the threshold that is used to determine linearly dependent columns during the factorization. + * + * In practice, if during the factorization the norm of the column that has to be eliminated is below + * this threshold, then the entire column is treated as zero, and it is moved at the end. + */ + void setPivotThreshold(const RealScalar& threshold) + { + m_useDefaultThreshold = false; + m_threshold = threshold; + } + + /** \returns the solution X of \f$ A X = B \f$ using the current decomposition of A. + * + * \sa compute() + */ + template<typename Rhs> + inline const internal::solve_retval<SparseQR, Rhs> solve(const MatrixBase<Rhs>& B) const + { + eigen_assert(m_isInitialized && "The factorization should be called first, use compute()"); + eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix"); + return internal::solve_retval<SparseQR, Rhs>(*this, B.derived()); + } + template<typename Rhs> + inline const internal::sparse_solve_retval<SparseQR, Rhs> solve(const SparseMatrixBase<Rhs>& B) const + { + eigen_assert(m_isInitialized && "The factorization should be called first, use compute()"); + eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix"); + return internal::sparse_solve_retval<SparseQR, Rhs>(*this, B.derived()); + } + + /** \brief Reports whether previous computation was successful. + * + * \returns \c Success if computation was successful, + * \c NumericalIssue if the QR factorization reports a numerical problem + * \c InvalidInput if the input matrix is invalid + * + * \sa iparm() + */ + ComputationInfo info() const + { + eigen_assert(m_isInitialized && "Decomposition is not initialized."); + return m_info; + } + + protected: + inline void sort_matrix_Q() + { + if(this->m_isQSorted) return; + // The matrix Q is sorted during the transposition + SparseMatrix<Scalar, RowMajor, Index> mQrm(this->m_Q); + this->m_Q = mQrm; + this->m_isQSorted = true; + } + + + protected: + bool m_isInitialized; + bool m_analysisIsok; + bool m_factorizationIsok; + mutable ComputationInfo m_info; + std::string m_lastError; + QRMatrixType m_pmat; // Temporary matrix + QRMatrixType m_R; // The triangular factor matrix + QRMatrixType m_Q; // The orthogonal reflectors + ScalarVector m_hcoeffs; // The Householder coefficients + PermutationType m_perm_c; // Fill-reducing Column permutation + PermutationType m_pivotperm; // The permutation for rank revealing + PermutationType m_outputPerm_c; // The final column permutation + RealScalar m_threshold; // Threshold to determine null Householder reflections + bool m_useDefaultThreshold; // Use default threshold + Index m_nonzeropivots; // Number of non zero pivots found + IndexVector m_etree; // Column elimination tree + IndexVector m_firstRowElt; // First element in each row + bool m_isQSorted; // whether Q is sorted or not + bool m_isEtreeOk; // whether the elimination tree match the initial input matrix + + template <typename, typename > friend struct SparseQR_QProduct; + template <typename > friend struct SparseQRMatrixQReturnType; + +}; + +/** \brief Preprocessing step of a QR factorization + * + * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()). + * + * In this step, the fill-reducing permutation is computed and applied to the columns of A + * and the column elimination tree is computed as well. Only the sparsity pattern of \a mat is exploited. + * + * \note In this step it is assumed that there is no empty row in the matrix \a mat. + */ +template <typename MatrixType, typename OrderingType> +void SparseQR<MatrixType,OrderingType>::analyzePattern(const MatrixType& mat) +{ + eigen_assert(mat.isCompressed() && "SparseQR requires a sparse matrix in compressed mode. Call .makeCompressed() before passing it to SparseQR"); + // Copy to a column major matrix if the input is rowmajor + typename internal::conditional<MatrixType::IsRowMajor,QRMatrixType,const MatrixType&>::type matCpy(mat); + // Compute the column fill reducing ordering + OrderingType ord; + ord(matCpy, m_perm_c); + Index n = mat.cols(); + Index m = mat.rows(); + Index diagSize = (std::min)(m,n); + + if (!m_perm_c.size()) + { + m_perm_c.resize(n); + m_perm_c.indices().setLinSpaced(n, 0,n-1); + } + + // Compute the column elimination tree of the permuted matrix + m_outputPerm_c = m_perm_c.inverse(); + internal::coletree(matCpy, m_etree, m_firstRowElt, m_outputPerm_c.indices().data()); + m_isEtreeOk = true; + + m_R.resize(m, n); + m_Q.resize(m, diagSize); + + // Allocate space for nonzero elements : rough estimation + m_R.reserve(2*mat.nonZeros()); //FIXME Get a more accurate estimation through symbolic factorization with the etree + m_Q.reserve(2*mat.nonZeros()); + m_hcoeffs.resize(diagSize); + m_analysisIsok = true; +} + +/** \brief Performs the numerical QR factorization of the input matrix + * + * The function SparseQR::analyzePattern(const MatrixType&) must have been called beforehand with + * a matrix having the same sparsity pattern than \a mat. + * + * \param mat The sparse column-major matrix + */ +template <typename MatrixType, typename OrderingType> +void SparseQR<MatrixType,OrderingType>::factorize(const MatrixType& mat) +{ + using std::abs; + using std::max; + + eigen_assert(m_analysisIsok && "analyzePattern() should be called before this step"); + Index m = mat.rows(); + Index n = mat.cols(); + Index diagSize = (std::min)(m,n); + IndexVector mark((std::max)(m,n)); mark.setConstant(-1); // Record the visited nodes + IndexVector Ridx(n), Qidx(m); // Store temporarily the row indexes for the current column of R and Q + Index nzcolR, nzcolQ; // Number of nonzero for the current column of R and Q + ScalarVector tval(m); // The dense vector used to compute the current column + RealScalar pivotThreshold = m_threshold; + + m_R.setZero(); + m_Q.setZero(); + m_pmat = mat; + if(!m_isEtreeOk) + { + m_outputPerm_c = m_perm_c.inverse(); + internal::coletree(m_pmat, m_etree, m_firstRowElt, m_outputPerm_c.indices().data()); + m_isEtreeOk = true; + } + + m_pmat.uncompress(); // To have the innerNonZeroPtr allocated + + // Apply the fill-in reducing permutation lazily: + { + // If the input is row major, copy the original column indices, + // otherwise directly use the input matrix + // + IndexVector originalOuterIndicesCpy; + const Index *originalOuterIndices = mat.outerIndexPtr(); + if(MatrixType::IsRowMajor) + { + originalOuterIndicesCpy = IndexVector::Map(m_pmat.outerIndexPtr(),n+1); + originalOuterIndices = originalOuterIndicesCpy.data(); + } + + for (int i = 0; i < n; i++) + { + Index p = m_perm_c.size() ? m_perm_c.indices()(i) : i; + m_pmat.outerIndexPtr()[p] = originalOuterIndices[i]; + m_pmat.innerNonZeroPtr()[p] = originalOuterIndices[i+1] - originalOuterIndices[i]; + } + } + + /* Compute the default threshold as in MatLab, see: + * Tim Davis, "Algorithm 915, SuiteSparseQR: Multifrontal Multithreaded Rank-Revealing + * Sparse QR Factorization, ACM Trans. on Math. Soft. 38(1), 2011, Page 8:3 + */ + if(m_useDefaultThreshold) + { + RealScalar max2Norm = 0.0; + for (int j = 0; j < n; j++) max2Norm = (max)(max2Norm, m_pmat.col(j).norm()); + if(max2Norm==RealScalar(0)) + max2Norm = RealScalar(1); + pivotThreshold = 20 * (m + n) * max2Norm * NumTraits<RealScalar>::epsilon(); + } + + // Initialize the numerical permutation + m_pivotperm.setIdentity(n); + + Index nonzeroCol = 0; // Record the number of valid pivots + m_Q.startVec(0); + + // Left looking rank-revealing QR factorization: compute a column of R and Q at a time + for (Index col = 0; col < n; ++col) + { + mark.setConstant(-1); + m_R.startVec(col); + mark(nonzeroCol) = col; + Qidx(0) = nonzeroCol; + nzcolR = 0; nzcolQ = 1; + bool found_diag = nonzeroCol>=m; + tval.setZero(); + + // Symbolic factorization: find the nonzero locations of the column k of the factors R and Q, i.e., + // all the nodes (with indexes lower than rank) reachable through the column elimination tree (etree) rooted at node k. + // Note: if the diagonal entry does not exist, then its contribution must be explicitly added, + // thus the trick with found_diag that permits to do one more iteration on the diagonal element if this one has not been found. + for (typename QRMatrixType::InnerIterator itp(m_pmat, col); itp || !found_diag; ++itp) + { + Index curIdx = nonzeroCol; + if(itp) curIdx = itp.row(); + if(curIdx == nonzeroCol) found_diag = true; + + // Get the nonzeros indexes of the current column of R + Index st = m_firstRowElt(curIdx); // The traversal of the etree starts here + if (st < 0 ) + { + m_lastError = "Empty row found during numerical factorization"; + m_info = InvalidInput; + return; + } + + // Traverse the etree + Index bi = nzcolR; + for (; mark(st) != col; st = m_etree(st)) + { + Ridx(nzcolR) = st; // Add this row to the list, + mark(st) = col; // and mark this row as visited + nzcolR++; + } + + // Reverse the list to get the topological ordering + Index nt = nzcolR-bi; + for(Index i = 0; i < nt/2; i++) std::swap(Ridx(bi+i), Ridx(nzcolR-i-1)); + + // Copy the current (curIdx,pcol) value of the input matrix + if(itp) tval(curIdx) = itp.value(); + else tval(curIdx) = Scalar(0); + + // Compute the pattern of Q(:,k) + if(curIdx > nonzeroCol && mark(curIdx) != col ) + { + Qidx(nzcolQ) = curIdx; // Add this row to the pattern of Q, + mark(curIdx) = col; // and mark it as visited + nzcolQ++; + } + } + + // Browse all the indexes of R(:,col) in reverse order + for (Index i = nzcolR-1; i >= 0; i--) + { + Index curIdx = Ridx(i); + + // Apply the curIdx-th householder vector to the current column (temporarily stored into tval) + Scalar tdot(0); + + // First compute q' * tval + tdot = m_Q.col(curIdx).dot(tval); + + tdot *= m_hcoeffs(curIdx); + + // Then update tval = tval - q * tau + // FIXME: tval -= tdot * m_Q.col(curIdx) should amount to the same (need to check/add support for efficient "dense ?= sparse") + for (typename QRMatrixType::InnerIterator itq(m_Q, curIdx); itq; ++itq) + tval(itq.row()) -= itq.value() * tdot; + + // Detect fill-in for the current column of Q + if(m_etree(Ridx(i)) == nonzeroCol) + { + for (typename QRMatrixType::InnerIterator itq(m_Q, curIdx); itq; ++itq) + { + Index iQ = itq.row(); + if (mark(iQ) != col) + { + Qidx(nzcolQ++) = iQ; // Add this row to the pattern of Q, + mark(iQ) = col; // and mark it as visited + } + } + } + } // End update current column + + Scalar tau = 0; + RealScalar beta = 0; + + if(nonzeroCol < diagSize) + { + // Compute the Householder reflection that eliminate the current column + // FIXME this step should call the Householder module. + Scalar c0 = nzcolQ ? tval(Qidx(0)) : Scalar(0); + + // First, the squared norm of Q((col+1):m, col) + RealScalar sqrNorm = 0.; + for (Index itq = 1; itq < nzcolQ; ++itq) sqrNorm += numext::abs2(tval(Qidx(itq))); + if(sqrNorm == RealScalar(0) && numext::imag(c0) == RealScalar(0)) + { + beta = numext::real(c0); + tval(Qidx(0)) = 1; + } + else + { + using std::sqrt; + beta = sqrt(numext::abs2(c0) + sqrNorm); + if(numext::real(c0) >= RealScalar(0)) + beta = -beta; + tval(Qidx(0)) = 1; + for (Index itq = 1; itq < nzcolQ; ++itq) + tval(Qidx(itq)) /= (c0 - beta); + tau = numext::conj((beta-c0) / beta); + + } + } + + // Insert values in R + for (Index i = nzcolR-1; i >= 0; i--) + { + Index curIdx = Ridx(i); + if(curIdx < nonzeroCol) + { + m_R.insertBackByOuterInnerUnordered(col, curIdx) = tval(curIdx); + tval(curIdx) = Scalar(0.); + } + } + + if(nonzeroCol < diagSize && abs(beta) >= pivotThreshold) + { + m_R.insertBackByOuterInner(col, nonzeroCol) = beta; + // The householder coefficient + m_hcoeffs(nonzeroCol) = tau; + // Record the householder reflections + for (Index itq = 0; itq < nzcolQ; ++itq) + { + Index iQ = Qidx(itq); + m_Q.insertBackByOuterInnerUnordered(nonzeroCol,iQ) = tval(iQ); + tval(iQ) = Scalar(0.); + } + nonzeroCol++; + if(nonzeroCol<diagSize) + m_Q.startVec(nonzeroCol); + } + else + { + // Zero pivot found: move implicitly this column to the end + for (Index j = nonzeroCol; j < n-1; j++) + std::swap(m_pivotperm.indices()(j), m_pivotperm.indices()[j+1]); + + // Recompute the column elimination tree + internal::coletree(m_pmat, m_etree, m_firstRowElt, m_pivotperm.indices().data()); + m_isEtreeOk = false; + } + } + + m_hcoeffs.tail(diagSize-nonzeroCol).setZero(); + + // Finalize the column pointers of the sparse matrices R and Q + m_Q.finalize(); + m_Q.makeCompressed(); + m_R.finalize(); + m_R.makeCompressed(); + m_isQSorted = false; + + m_nonzeropivots = nonzeroCol; + + if(nonzeroCol<n) + { + // Permute the triangular factor to put the 'dead' columns to the end + QRMatrixType tempR(m_R); + m_R = tempR * m_pivotperm; + + // Update the column permutation + m_outputPerm_c = m_outputPerm_c * m_pivotperm; + } + + m_isInitialized = true; + m_factorizationIsok = true; + m_info = Success; +} + +namespace internal { + +template<typename _MatrixType, typename OrderingType, typename Rhs> +struct solve_retval<SparseQR<_MatrixType,OrderingType>, Rhs> + : solve_retval_base<SparseQR<_MatrixType,OrderingType>, Rhs> +{ + typedef SparseQR<_MatrixType,OrderingType> Dec; + EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs) + + template<typename Dest> void evalTo(Dest& dst) const + { + dec()._solve(rhs(),dst); + } +}; +template<typename _MatrixType, typename OrderingType, typename Rhs> +struct sparse_solve_retval<SparseQR<_MatrixType, OrderingType>, Rhs> + : sparse_solve_retval_base<SparseQR<_MatrixType, OrderingType>, Rhs> +{ + typedef SparseQR<_MatrixType, OrderingType> Dec; + EIGEN_MAKE_SPARSE_SOLVE_HELPERS(Dec, Rhs) + + template<typename Dest> void evalTo(Dest& dst) const + { + this->defaultEvalTo(dst); + } +}; +} // end namespace internal + +template <typename SparseQRType, typename Derived> +struct SparseQR_QProduct : ReturnByValue<SparseQR_QProduct<SparseQRType, Derived> > +{ + typedef typename SparseQRType::QRMatrixType MatrixType; + typedef typename SparseQRType::Scalar Scalar; + typedef typename SparseQRType::Index Index; + // Get the references + SparseQR_QProduct(const SparseQRType& qr, const Derived& other, bool transpose) : + m_qr(qr),m_other(other),m_transpose(transpose) {} + inline Index rows() const { return m_transpose ? m_qr.rows() : m_qr.cols(); } + inline Index cols() const { return m_other.cols(); } + + // Assign to a vector + template<typename DesType> + void evalTo(DesType& res) const + { + Index m = m_qr.rows(); + Index n = m_qr.cols(); + Index diagSize = (std::min)(m,n); + res = m_other; + if (m_transpose) + { + eigen_assert(m_qr.m_Q.rows() == m_other.rows() && "Non conforming object sizes"); + //Compute res = Q' * other column by column + for(Index j = 0; j < res.cols(); j++){ + for (Index k = 0; k < diagSize; k++) + { + Scalar tau = Scalar(0); + tau = m_qr.m_Q.col(k).dot(res.col(j)); + if(tau==Scalar(0)) continue; + tau = tau * m_qr.m_hcoeffs(k); + res.col(j) -= tau * m_qr.m_Q.col(k); + } + } + } + else + { + eigen_assert(m_qr.m_Q.rows() == m_other.rows() && "Non conforming object sizes"); + // Compute res = Q * other column by column + for(Index j = 0; j < res.cols(); j++) + { + for (Index k = diagSize-1; k >=0; k--) + { + Scalar tau = Scalar(0); + tau = m_qr.m_Q.col(k).dot(res.col(j)); + if(tau==Scalar(0)) continue; + tau = tau * m_qr.m_hcoeffs(k); + res.col(j) -= tau * m_qr.m_Q.col(k); + } + } + } + } + + const SparseQRType& m_qr; + const Derived& m_other; + bool m_transpose; +}; + +template<typename SparseQRType> +struct SparseQRMatrixQReturnType : public EigenBase<SparseQRMatrixQReturnType<SparseQRType> > +{ + typedef typename SparseQRType::Index Index; + typedef typename SparseQRType::Scalar Scalar; + typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; + SparseQRMatrixQReturnType(const SparseQRType& qr) : m_qr(qr) {} + template<typename Derived> + SparseQR_QProduct<SparseQRType, Derived> operator*(const MatrixBase<Derived>& other) + { + return SparseQR_QProduct<SparseQRType,Derived>(m_qr,other.derived(),false); + } + SparseQRMatrixQTransposeReturnType<SparseQRType> adjoint() const + { + return SparseQRMatrixQTransposeReturnType<SparseQRType>(m_qr); + } + inline Index rows() const { return m_qr.rows(); } + inline Index cols() const { return (std::min)(m_qr.rows(),m_qr.cols()); } + // To use for operations with the transpose of Q + SparseQRMatrixQTransposeReturnType<SparseQRType> transpose() const + { + return SparseQRMatrixQTransposeReturnType<SparseQRType>(m_qr); + } + template<typename Dest> void evalTo(MatrixBase<Dest>& dest) const + { + dest.derived() = m_qr.matrixQ() * Dest::Identity(m_qr.rows(), m_qr.rows()); + } + template<typename Dest> void evalTo(SparseMatrixBase<Dest>& dest) const + { + Dest idMat(m_qr.rows(), m_qr.rows()); + idMat.setIdentity(); + // Sort the sparse householder reflectors if needed + const_cast<SparseQRType *>(&m_qr)->sort_matrix_Q(); + dest.derived() = SparseQR_QProduct<SparseQRType, Dest>(m_qr, idMat, false); + } + + const SparseQRType& m_qr; +}; + +template<typename SparseQRType> +struct SparseQRMatrixQTransposeReturnType +{ + SparseQRMatrixQTransposeReturnType(const SparseQRType& qr) : m_qr(qr) {} + template<typename Derived> + SparseQR_QProduct<SparseQRType,Derived> operator*(const MatrixBase<Derived>& other) + { + return SparseQR_QProduct<SparseQRType,Derived>(m_qr,other.derived(), true); + } + const SparseQRType& m_qr; +}; + +} // end namespace Eigen + +#endif |