File indexing completed on 2025-04-19 09:06:44
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef EIGEN_SPARSE_QR_H
0012 #define EIGEN_SPARSE_QR_H
0013
0014 namespace RivetEigen {
0015
0016 template<typename MatrixType, typename OrderingType> class SparseQR;
0017 template<typename SparseQRType> struct SparseQRMatrixQReturnType;
0018 template<typename SparseQRType> struct SparseQRMatrixQTransposeReturnType;
0019 template<typename SparseQRType, typename Derived> struct SparseQR_QProduct;
0020 namespace internal {
0021 template <typename SparseQRType> struct traits<SparseQRMatrixQReturnType<SparseQRType> >
0022 {
0023 typedef typename SparseQRType::MatrixType ReturnType;
0024 typedef typename ReturnType::StorageIndex StorageIndex;
0025 typedef typename ReturnType::StorageKind StorageKind;
0026 enum {
0027 RowsAtCompileTime = Dynamic,
0028 ColsAtCompileTime = Dynamic
0029 };
0030 };
0031 template <typename SparseQRType> struct traits<SparseQRMatrixQTransposeReturnType<SparseQRType> >
0032 {
0033 typedef typename SparseQRType::MatrixType ReturnType;
0034 };
0035 template <typename SparseQRType, typename Derived> struct traits<SparseQR_QProduct<SparseQRType, Derived> >
0036 {
0037 typedef typename Derived::PlainObject ReturnType;
0038 };
0039 }
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083 template<typename _MatrixType, typename _OrderingType>
0084 class SparseQR : public SparseSolverBase<SparseQR<_MatrixType,_OrderingType> >
0085 {
0086 protected:
0087 typedef SparseSolverBase<SparseQR<_MatrixType,_OrderingType> > Base;
0088 using Base::m_isInitialized;
0089 public:
0090 using Base::_solve_impl;
0091 typedef _MatrixType MatrixType;
0092 typedef _OrderingType OrderingType;
0093 typedef typename MatrixType::Scalar Scalar;
0094 typedef typename MatrixType::RealScalar RealScalar;
0095 typedef typename MatrixType::StorageIndex StorageIndex;
0096 typedef SparseMatrix<Scalar,ColMajor,StorageIndex> QRMatrixType;
0097 typedef Matrix<StorageIndex, Dynamic, 1> IndexVector;
0098 typedef Matrix<Scalar, Dynamic, 1> ScalarVector;
0099 typedef PermutationMatrix<Dynamic, Dynamic, StorageIndex> PermutationType;
0100
0101 enum {
0102 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
0103 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
0104 };
0105
0106 public:
0107 SparseQR () : m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false),m_isEtreeOk(false)
0108 { }
0109
0110
0111
0112
0113
0114
0115
0116 explicit SparseQR(const MatrixType& mat) : m_analysisIsok(false), m_lastError(""), m_useDefaultThreshold(true),m_isQSorted(false),m_isEtreeOk(false)
0117 {
0118 compute(mat);
0119 }
0120
0121
0122
0123
0124
0125
0126
0127 void compute(const MatrixType& mat)
0128 {
0129 analyzePattern(mat);
0130 factorize(mat);
0131 }
0132 void analyzePattern(const MatrixType& mat);
0133 void factorize(const MatrixType& mat);
0134
0135
0136
0137 inline Index rows() const { return m_pmat.rows(); }
0138
0139
0140
0141 inline Index cols() const { return m_pmat.cols();}
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156 const QRMatrixType& matrixR() const { return m_R; }
0157
0158
0159
0160
0161
0162 Index rank() const
0163 {
0164 eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
0165 return m_nonzeropivots;
0166 }
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186 SparseQRMatrixQReturnType<SparseQR> matrixQ() const
0187 { return SparseQRMatrixQReturnType<SparseQR>(*this); }
0188
0189
0190
0191
0192 const PermutationType& colsPermutation() const
0193 {
0194 eigen_assert(m_isInitialized && "Decomposition is not initialized.");
0195 return m_outputPerm_c;
0196 }
0197
0198
0199
0200
0201 std::string lastErrorMessage() const { return m_lastError; }
0202
0203
0204 template<typename Rhs, typename Dest>
0205 bool _solve_impl(const MatrixBase<Rhs> &B, MatrixBase<Dest> &dest) const
0206 {
0207 eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
0208 eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix");
0209
0210 Index rank = this->rank();
0211
0212
0213 typename Dest::PlainObject y, b;
0214 y = this->matrixQ().adjoint() * B;
0215 b = y;
0216
0217
0218 y.resize((std::max<Index>)(cols(),y.rows()),y.cols());
0219 y.topRows(rank) = this->matrixR().topLeftCorner(rank, rank).template triangularView<Upper>().solve(b.topRows(rank));
0220 y.bottomRows(y.rows()-rank).setZero();
0221
0222
0223 if (m_perm_c.size()) dest = colsPermutation() * y.topRows(cols());
0224 else dest = y.topRows(cols());
0225
0226 m_info = Success;
0227 return true;
0228 }
0229
0230
0231
0232
0233
0234
0235 void setPivotThreshold(const RealScalar& threshold)
0236 {
0237 m_useDefaultThreshold = false;
0238 m_threshold = threshold;
0239 }
0240
0241
0242
0243
0244
0245 template<typename Rhs>
0246 inline const Solve<SparseQR, Rhs> solve(const MatrixBase<Rhs>& B) const
0247 {
0248 eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
0249 eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix");
0250 return Solve<SparseQR, Rhs>(*this, B.derived());
0251 }
0252 template<typename Rhs>
0253 inline const Solve<SparseQR, Rhs> solve(const SparseMatrixBase<Rhs>& B) const
0254 {
0255 eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
0256 eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix");
0257 return Solve<SparseQR, Rhs>(*this, B.derived());
0258 }
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268 ComputationInfo info() const
0269 {
0270 eigen_assert(m_isInitialized && "Decomposition is not initialized.");
0271 return m_info;
0272 }
0273
0274
0275
0276 inline void _sort_matrix_Q()
0277 {
0278 if(this->m_isQSorted) return;
0279
0280 SparseMatrix<Scalar, RowMajor, Index> mQrm(this->m_Q);
0281 this->m_Q = mQrm;
0282 this->m_isQSorted = true;
0283 }
0284
0285
0286 protected:
0287 bool m_analysisIsok;
0288 bool m_factorizationIsok;
0289 mutable ComputationInfo m_info;
0290 std::string m_lastError;
0291 QRMatrixType m_pmat;
0292 QRMatrixType m_R;
0293 QRMatrixType m_Q;
0294 ScalarVector m_hcoeffs;
0295 PermutationType m_perm_c;
0296 PermutationType m_pivotperm;
0297 PermutationType m_outputPerm_c;
0298 RealScalar m_threshold;
0299 bool m_useDefaultThreshold;
0300 Index m_nonzeropivots;
0301 IndexVector m_etree;
0302 IndexVector m_firstRowElt;
0303 bool m_isQSorted;
0304 bool m_isEtreeOk;
0305
0306 template <typename, typename > friend struct SparseQR_QProduct;
0307
0308 };
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319 template <typename MatrixType, typename OrderingType>
0320 void SparseQR<MatrixType,OrderingType>::analyzePattern(const MatrixType& mat)
0321 {
0322 eigen_assert(mat.isCompressed() && "SparseQR requires a sparse matrix in compressed mode. Call .makeCompressed() before passing it to SparseQR");
0323
0324 typename internal::conditional<MatrixType::IsRowMajor,QRMatrixType,const MatrixType&>::type matCpy(mat);
0325
0326 OrderingType ord;
0327 ord(matCpy, m_perm_c);
0328 Index n = mat.cols();
0329 Index m = mat.rows();
0330 Index diagSize = (std::min)(m,n);
0331
0332 if (!m_perm_c.size())
0333 {
0334 m_perm_c.resize(n);
0335 m_perm_c.indices().setLinSpaced(n, 0,StorageIndex(n-1));
0336 }
0337
0338
0339 m_outputPerm_c = m_perm_c.inverse();
0340 internal::coletree(matCpy, m_etree, m_firstRowElt, m_outputPerm_c.indices().data());
0341 m_isEtreeOk = true;
0342
0343 m_R.resize(m, n);
0344 m_Q.resize(m, diagSize);
0345
0346
0347 m_R.reserve(2*mat.nonZeros());
0348 m_Q.reserve(2*mat.nonZeros());
0349 m_hcoeffs.resize(diagSize);
0350 m_analysisIsok = true;
0351 }
0352
0353
0354
0355
0356
0357
0358
0359
0360 template <typename MatrixType, typename OrderingType>
0361 void SparseQR<MatrixType,OrderingType>::factorize(const MatrixType& mat)
0362 {
0363 using std::abs;
0364
0365 eigen_assert(m_analysisIsok && "analyzePattern() should be called before this step");
0366 StorageIndex m = StorageIndex(mat.rows());
0367 StorageIndex n = StorageIndex(mat.cols());
0368 StorageIndex diagSize = (std::min)(m,n);
0369 IndexVector mark((std::max)(m,n)); mark.setConstant(-1);
0370 IndexVector Ridx(n), Qidx(m);
0371 Index nzcolR, nzcolQ;
0372 ScalarVector tval(m);
0373 RealScalar pivotThreshold = m_threshold;
0374
0375 m_R.setZero();
0376 m_Q.setZero();
0377 m_pmat = mat;
0378 if(!m_isEtreeOk)
0379 {
0380 m_outputPerm_c = m_perm_c.inverse();
0381 internal::coletree(m_pmat, m_etree, m_firstRowElt, m_outputPerm_c.indices().data());
0382 m_isEtreeOk = true;
0383 }
0384
0385 m_pmat.uncompress();
0386
0387
0388 {
0389
0390
0391
0392 IndexVector originalOuterIndicesCpy;
0393 const StorageIndex *originalOuterIndices = mat.outerIndexPtr();
0394 if(MatrixType::IsRowMajor)
0395 {
0396 originalOuterIndicesCpy = IndexVector::Map(m_pmat.outerIndexPtr(),n+1);
0397 originalOuterIndices = originalOuterIndicesCpy.data();
0398 }
0399
0400 for (int i = 0; i < n; i++)
0401 {
0402 Index p = m_perm_c.size() ? m_perm_c.indices()(i) : i;
0403 m_pmat.outerIndexPtr()[p] = originalOuterIndices[i];
0404 m_pmat.innerNonZeroPtr()[p] = originalOuterIndices[i+1] - originalOuterIndices[i];
0405 }
0406 }
0407
0408
0409
0410
0411
0412 if(m_useDefaultThreshold)
0413 {
0414 RealScalar max2Norm = 0.0;
0415 for (int j = 0; j < n; j++) max2Norm = numext::maxi(max2Norm, m_pmat.col(j).norm());
0416 if(max2Norm==RealScalar(0))
0417 max2Norm = RealScalar(1);
0418 pivotThreshold = 20 * (m + n) * max2Norm * NumTraits<RealScalar>::epsilon();
0419 }
0420
0421
0422 m_pivotperm.setIdentity(n);
0423
0424 StorageIndex nonzeroCol = 0;
0425 m_Q.startVec(0);
0426
0427
0428 for (StorageIndex col = 0; col < n; ++col)
0429 {
0430 mark.setConstant(-1);
0431 m_R.startVec(col);
0432 mark(nonzeroCol) = col;
0433 Qidx(0) = nonzeroCol;
0434 nzcolR = 0; nzcolQ = 1;
0435 bool found_diag = nonzeroCol>=m;
0436 tval.setZero();
0437
0438
0439
0440
0441
0442 for (typename QRMatrixType::InnerIterator itp(m_pmat, col); itp || !found_diag; ++itp)
0443 {
0444 StorageIndex curIdx = nonzeroCol;
0445 if(itp) curIdx = StorageIndex(itp.row());
0446 if(curIdx == nonzeroCol) found_diag = true;
0447
0448
0449 StorageIndex st = m_firstRowElt(curIdx);
0450 if (st < 0 )
0451 {
0452 m_lastError = "Empty row found during numerical factorization";
0453 m_info = InvalidInput;
0454 return;
0455 }
0456
0457
0458 Index bi = nzcolR;
0459 for (; mark(st) != col; st = m_etree(st))
0460 {
0461 Ridx(nzcolR) = st;
0462 mark(st) = col;
0463 nzcolR++;
0464 }
0465
0466
0467 Index nt = nzcolR-bi;
0468 for(Index i = 0; i < nt/2; i++) std::swap(Ridx(bi+i), Ridx(nzcolR-i-1));
0469
0470
0471 if(itp) tval(curIdx) = itp.value();
0472 else tval(curIdx) = Scalar(0);
0473
0474
0475 if(curIdx > nonzeroCol && mark(curIdx) != col )
0476 {
0477 Qidx(nzcolQ) = curIdx;
0478 mark(curIdx) = col;
0479 nzcolQ++;
0480 }
0481 }
0482
0483
0484 for (Index i = nzcolR-1; i >= 0; i--)
0485 {
0486 Index curIdx = Ridx(i);
0487
0488
0489 Scalar tdot(0);
0490
0491
0492 tdot = m_Q.col(curIdx).dot(tval);
0493
0494 tdot *= m_hcoeffs(curIdx);
0495
0496
0497
0498 for (typename QRMatrixType::InnerIterator itq(m_Q, curIdx); itq; ++itq)
0499 tval(itq.row()) -= itq.value() * tdot;
0500
0501
0502 if(m_etree(Ridx(i)) == nonzeroCol)
0503 {
0504 for (typename QRMatrixType::InnerIterator itq(m_Q, curIdx); itq; ++itq)
0505 {
0506 StorageIndex iQ = StorageIndex(itq.row());
0507 if (mark(iQ) != col)
0508 {
0509 Qidx(nzcolQ++) = iQ;
0510 mark(iQ) = col;
0511 }
0512 }
0513 }
0514 }
0515
0516 Scalar tau = RealScalar(0);
0517 RealScalar beta = 0;
0518
0519 if(nonzeroCol < diagSize)
0520 {
0521
0522
0523 Scalar c0 = nzcolQ ? tval(Qidx(0)) : Scalar(0);
0524
0525
0526 RealScalar sqrNorm = 0.;
0527 for (Index itq = 1; itq < nzcolQ; ++itq) sqrNorm += numext::abs2(tval(Qidx(itq)));
0528 if(sqrNorm == RealScalar(0) && numext::imag(c0) == RealScalar(0))
0529 {
0530 beta = numext::real(c0);
0531 tval(Qidx(0)) = 1;
0532 }
0533 else
0534 {
0535 using std::sqrt;
0536 beta = sqrt(numext::abs2(c0) + sqrNorm);
0537 if(numext::real(c0) >= RealScalar(0))
0538 beta = -beta;
0539 tval(Qidx(0)) = 1;
0540 for (Index itq = 1; itq < nzcolQ; ++itq)
0541 tval(Qidx(itq)) /= (c0 - beta);
0542 tau = numext::conj((beta-c0) / beta);
0543
0544 }
0545 }
0546
0547
0548 for (Index i = nzcolR-1; i >= 0; i--)
0549 {
0550 Index curIdx = Ridx(i);
0551 if(curIdx < nonzeroCol)
0552 {
0553 m_R.insertBackByOuterInnerUnordered(col, curIdx) = tval(curIdx);
0554 tval(curIdx) = Scalar(0.);
0555 }
0556 }
0557
0558 if(nonzeroCol < diagSize && abs(beta) >= pivotThreshold)
0559 {
0560 m_R.insertBackByOuterInner(col, nonzeroCol) = beta;
0561
0562 m_hcoeffs(nonzeroCol) = tau;
0563
0564 for (Index itq = 0; itq < nzcolQ; ++itq)
0565 {
0566 Index iQ = Qidx(itq);
0567 m_Q.insertBackByOuterInnerUnordered(nonzeroCol,iQ) = tval(iQ);
0568 tval(iQ) = Scalar(0.);
0569 }
0570 nonzeroCol++;
0571 if(nonzeroCol<diagSize)
0572 m_Q.startVec(nonzeroCol);
0573 }
0574 else
0575 {
0576
0577 for (Index j = nonzeroCol; j < n-1; j++)
0578 std::swap(m_pivotperm.indices()(j), m_pivotperm.indices()[j+1]);
0579
0580
0581 internal::coletree(m_pmat, m_etree, m_firstRowElt, m_pivotperm.indices().data());
0582 m_isEtreeOk = false;
0583 }
0584 }
0585
0586 m_hcoeffs.tail(diagSize-nonzeroCol).setZero();
0587
0588
0589 m_Q.finalize();
0590 m_Q.makeCompressed();
0591 m_R.finalize();
0592 m_R.makeCompressed();
0593 m_isQSorted = false;
0594
0595 m_nonzeropivots = nonzeroCol;
0596
0597 if(nonzeroCol<n)
0598 {
0599
0600 QRMatrixType tempR(m_R);
0601 m_R = tempR * m_pivotperm;
0602
0603
0604 m_outputPerm_c = m_outputPerm_c * m_pivotperm;
0605 }
0606
0607 m_isInitialized = true;
0608 m_factorizationIsok = true;
0609 m_info = Success;
0610 }
0611
0612 template <typename SparseQRType, typename Derived>
0613 struct SparseQR_QProduct : ReturnByValue<SparseQR_QProduct<SparseQRType, Derived> >
0614 {
0615 typedef typename SparseQRType::QRMatrixType MatrixType;
0616 typedef typename SparseQRType::Scalar Scalar;
0617
0618 SparseQR_QProduct(const SparseQRType& qr, const Derived& other, bool transpose) :
0619 m_qr(qr),m_other(other),m_transpose(transpose) {}
0620 inline Index rows() const { return m_qr.matrixQ().rows(); }
0621 inline Index cols() const { return m_other.cols(); }
0622
0623
0624 template<typename DesType>
0625 void evalTo(DesType& res) const
0626 {
0627 Index m = m_qr.rows();
0628 Index n = m_qr.cols();
0629 Index diagSize = (std::min)(m,n);
0630 res = m_other;
0631 if (m_transpose)
0632 {
0633 eigen_assert(m_qr.m_Q.rows() == m_other.rows() && "Non conforming object sizes");
0634
0635 for(Index j = 0; j < res.cols(); j++){
0636 for (Index k = 0; k < diagSize; k++)
0637 {
0638 Scalar tau = Scalar(0);
0639 tau = m_qr.m_Q.col(k).dot(res.col(j));
0640 if(tau==Scalar(0)) continue;
0641 tau = tau * m_qr.m_hcoeffs(k);
0642 res.col(j) -= tau * m_qr.m_Q.col(k);
0643 }
0644 }
0645 }
0646 else
0647 {
0648 eigen_assert(m_qr.matrixQ().cols() == m_other.rows() && "Non conforming object sizes");
0649
0650 res.conservativeResize(rows(), cols());
0651
0652
0653 for(Index j = 0; j < res.cols(); j++)
0654 {
0655 Index start_k = internal::is_identity<Derived>::value ? numext::mini(j,diagSize-1) : diagSize-1;
0656 for (Index k = start_k; k >=0; k--)
0657 {
0658 Scalar tau = Scalar(0);
0659 tau = m_qr.m_Q.col(k).dot(res.col(j));
0660 if(tau==Scalar(0)) continue;
0661 tau = tau * numext::conj(m_qr.m_hcoeffs(k));
0662 res.col(j) -= tau * m_qr.m_Q.col(k);
0663 }
0664 }
0665 }
0666 }
0667
0668 const SparseQRType& m_qr;
0669 const Derived& m_other;
0670 bool m_transpose;
0671 };
0672
0673 template<typename SparseQRType>
0674 struct SparseQRMatrixQReturnType : public EigenBase<SparseQRMatrixQReturnType<SparseQRType> >
0675 {
0676 typedef typename SparseQRType::Scalar Scalar;
0677 typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix;
0678 enum {
0679 RowsAtCompileTime = Dynamic,
0680 ColsAtCompileTime = Dynamic
0681 };
0682 explicit SparseQRMatrixQReturnType(const SparseQRType& qr) : m_qr(qr) {}
0683 template<typename Derived>
0684 SparseQR_QProduct<SparseQRType, Derived> operator*(const MatrixBase<Derived>& other)
0685 {
0686 return SparseQR_QProduct<SparseQRType,Derived>(m_qr,other.derived(),false);
0687 }
0688
0689 SparseQRMatrixQTransposeReturnType<SparseQRType> adjoint() const
0690 {
0691 return SparseQRMatrixQTransposeReturnType<SparseQRType>(m_qr);
0692 }
0693 inline Index rows() const { return m_qr.rows(); }
0694 inline Index cols() const { return m_qr.rows(); }
0695
0696 SparseQRMatrixQTransposeReturnType<SparseQRType> transpose() const
0697 {
0698 return SparseQRMatrixQTransposeReturnType<SparseQRType>(m_qr);
0699 }
0700 const SparseQRType& m_qr;
0701 };
0702
0703
0704 template<typename SparseQRType>
0705 struct SparseQRMatrixQTransposeReturnType
0706 {
0707 explicit SparseQRMatrixQTransposeReturnType(const SparseQRType& qr) : m_qr(qr) {}
0708 template<typename Derived>
0709 SparseQR_QProduct<SparseQRType,Derived> operator*(const MatrixBase<Derived>& other)
0710 {
0711 return SparseQR_QProduct<SparseQRType,Derived>(m_qr,other.derived(), true);
0712 }
0713 const SparseQRType& m_qr;
0714 };
0715
0716 namespace internal {
0717
0718 template<typename SparseQRType>
0719 struct evaluator_traits<SparseQRMatrixQReturnType<SparseQRType> >
0720 {
0721 typedef typename SparseQRType::MatrixType MatrixType;
0722 typedef typename storage_kind_to_evaluator_kind<typename MatrixType::StorageKind>::Kind Kind;
0723 typedef SparseShape Shape;
0724 };
0725
0726 template< typename DstXprType, typename SparseQRType>
0727 struct Assignment<DstXprType, SparseQRMatrixQReturnType<SparseQRType>, internal::assign_op<typename DstXprType::Scalar,typename DstXprType::Scalar>, Sparse2Sparse>
0728 {
0729 typedef SparseQRMatrixQReturnType<SparseQRType> SrcXprType;
0730 typedef typename DstXprType::Scalar Scalar;
0731 typedef typename DstXprType::StorageIndex StorageIndex;
0732 static void run(DstXprType &dst, const SrcXprType &src, const internal::assign_op<Scalar,Scalar> &)
0733 {
0734 typename DstXprType::PlainObject idMat(src.rows(), src.cols());
0735 idMat.setIdentity();
0736
0737 const_cast<SparseQRType *>(&src.m_qr)->_sort_matrix_Q();
0738 dst = SparseQR_QProduct<SparseQRType, DstXprType>(src.m_qr, idMat, false);
0739 }
0740 };
0741
0742 template< typename DstXprType, typename SparseQRType>
0743 struct Assignment<DstXprType, SparseQRMatrixQReturnType<SparseQRType>, internal::assign_op<typename DstXprType::Scalar,typename DstXprType::Scalar>, Sparse2Dense>
0744 {
0745 typedef SparseQRMatrixQReturnType<SparseQRType> SrcXprType;
0746 typedef typename DstXprType::Scalar Scalar;
0747 typedef typename DstXprType::StorageIndex StorageIndex;
0748 static void run(DstXprType &dst, const SrcXprType &src, const internal::assign_op<Scalar,Scalar> &)
0749 {
0750 dst = src.m_qr.matrixQ() * DstXprType::Identity(src.m_qr.rows(), src.m_qr.rows());
0751 }
0752 };
0753
0754 }
0755
0756 }
0757
0758 #endif