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