394 eigen_assert(m_analysisIsok &&
"analyzePattern() should be called before this step");
401 StorageIndex m = StorageIndex(mat.rows());
402 StorageIndex n = StorageIndex(mat.cols());
403 StorageIndex diagSize = (std::min)(m, n);
404 IndexVector mark((std::max)(m, n));
406 IndexVector Ridx(n), Qidx(m);
407 Index nzcolR, nzcolQ;
408 ScalarVector tval(m);
409 ScalarVector tvalLookAhead(m);
414 m_lastPivotLookAheadSkipped =
false;
417 m_outputPerm_c = m_perm_c.inverse();
418 internal::coletree(m_pmat, m_etree, m_firstRowElt, m_outputPerm_c.indices().data());
430 IndexVector originalOuterIndicesCpy;
431 const bool useInputOuterIndices = !MatrixType::IsRowMajor && mat.isCompressed();
432 const StorageIndex* originalOuterIndices = useInputOuterIndices ? mat.outerIndexPtr() :
nullptr;
433 if (!useInputOuterIndices) {
434 originalOuterIndicesCpy = IndexVector::Map(m_pmat.outerIndexPtr(), n + 1);
435 originalOuterIndices = originalOuterIndicesCpy.
data();
438 for (
int i = 0; i < n; i++) {
439 Index p = m_perm_c.size() ? m_perm_c.indices()(i) : i;
440 m_pmat.outerIndexPtr()[p] = originalOuterIndices[i];
441 m_pmat.innerNonZeroPtr()[p] = originalOuterIndices[i + 1] - originalOuterIndices[i];
449 RealScalar pivotThreshold;
450 RealScalar max2Norm = RealScalar(0.0);
451 if (m_useDefaultThreshold) {
456 using ThresholdReal = std::conditional_t<(
sizeof(RealScalar) <
sizeof(float)),
float, RealScalar>;
457 if (EIGEN_CONST_CONDITIONAL((std::is_same<ThresholdReal, RealScalar>::value))) {
459 for (
int j = 0; j < n; j++) max2Norm = numext::maxi(max2Norm, m_pmat.col(j).norm());
460 if (max2Norm == RealScalar(0)) max2Norm = RealScalar(1);
461 pivotThreshold = RealScalar(20 * (m + n)) * max2Norm * NumTraits<RealScalar>::epsilon();
463 ThresholdReal maxColNorm = ThresholdReal(0);
464 for (
int j = 0; j < n; j++) {
465 ThresholdReal colSquaredNorm = ThresholdReal(0);
466 for (
typename QRMatrixType::InnerIterator it(m_pmat, j); it; ++it)
467 colSquaredNorm += numext::abs2(ThresholdReal(numext::real(it.value()))) +
468 numext::abs2(ThresholdReal(numext::imag(it.value())));
469 maxColNorm = numext::maxi(maxColNorm, numext::sqrt(colSquaredNorm));
471 if (maxColNorm == ThresholdReal(0)) maxColNorm = ThresholdReal(1);
473 pivotThreshold = RealScalar(ThresholdReal(20 * (m + n)) * maxColNorm * NumTraits<ThresholdReal>::epsilon());
474 max2Norm = RealScalar(maxColNorm);
477 pivotThreshold = m_threshold;
481 m_pivotperm.setIdentity(n);
483 StorageIndex nonzeroCol = 0;
487 for (StorageIndex col = 0; col < n; ++col) {
490 mark(nonzeroCol) = col;
491 Qidx(0) = nonzeroCol;
494 bool found_diag = nonzeroCol >= m;
501 for (
typename QRMatrixType::InnerIterator itp(m_pmat, col); itp || !found_diag; ++itp) {
502 StorageIndex curIdx = nonzeroCol;
503 if (itp) curIdx = StorageIndex(itp.row());
504 if (curIdx == nonzeroCol) found_diag =
true;
507 StorageIndex st = m_firstRowElt(curIdx);
509 m_lastError =
"Empty row found during numerical factorization";
516 for (; mark(st) != col; st = m_etree(st)) {
523 Index nt = nzcolR - bi;
524 for (Index i = 0; i < nt / 2; i++) std::swap(Ridx(bi + i), Ridx(nzcolR - i - 1));
528 tval(curIdx) = itp.value();
530 tval(curIdx) = Scalar(0);
533 if (curIdx > nonzeroCol && mark(curIdx) != col) {
534 Qidx(nzcolQ) = curIdx;
541 for (Index i = nzcolR - 1; i >= 0; i--) {
542 Index curIdx = Ridx(i);
548 tdot = m_Q.col(curIdx).dot(tval);
550 tdot *= m_hcoeffs(curIdx);
553 tval -= tdot * m_Q.col(curIdx);
556 if (m_etree(Ridx(i)) == nonzeroCol) {
557 for (
typename QRMatrixType::InnerIterator itq(m_Q, curIdx); itq; ++itq) {
558 StorageIndex iQ = StorageIndex(itq.row());
559 if (mark(iQ) != col) {
567 Scalar tau = RealScalar(0);
568 RealScalar beta = RealScalar(0);
570 if (nonzeroCol < diagSize) {
573 Scalar c0 = nzcolQ ? tval(Qidx(0)) : Scalar(0);
576 RealScalar sqrNorm = RealScalar(0.);
577 for (Index itq = 1; itq < nzcolQ; ++itq) sqrNorm += numext::abs2(tval(Qidx(itq)));
578 if (sqrNorm == RealScalar(0) && numext::imag(c0) == RealScalar(0)) {
579 beta = numext::real(c0);
580 tval(Qidx(0)) = Scalar(1);
583 beta = sqrt(numext::abs2(c0) + sqrNorm);
584 if (numext::real(c0) >= RealScalar(0)) beta = -beta;
585 tval(Qidx(0)) = Scalar(1);
586 for (Index itq = 1; itq < nzcolQ; ++itq) tval(Qidx(itq)) /= (c0 - beta);
587 tau = numext::conj((beta - c0) / beta);
592 for (Index i = nzcolR - 1; i >= 0; i--) {
593 Index curIdx = Ridx(i);
594 if (curIdx < nonzeroCol) {
595 m_R.insertBackByOuterInnerUnordered(col, curIdx) = tval(curIdx);
596 tval(curIdx) = Scalar(0.);
600 const RealScalar absBeta = abs(beta);
601 bool hasReplacement =
false;
602 const bool canRejectColumn = nonzeroCol + (n - col - 1) >= diagSize;
613 const RealScalar weakPivotTolerance = sqrt(NumTraits<RealScalar>::epsilon());
614 const RealScalar maxReplaceablePivotThreshold = max2Norm * weakPivotTolerance;
615 if (nonzeroCol < diagSize && canRejectColumn && m_useDefaultThreshold && absBeta >= pivotThreshold &&
616 absBeta < maxReplaceablePivotThreshold) {
617 const RealScalar colNorm = m_pmat.col(col).norm();
619 const RealScalar replaceablePivotThreshold = colNorm * weakPivotTolerance;
620 if (absBeta < replaceablePivotThreshold) {
621 const StorageIndex requiredReplacementCount = diagSize - nonzeroCol;
622 const StorageIndex activeRows = m - nonzeroCol;
623 const Index maxReplacementBasisEntries = Index(PivotLookAheadMaxBasisBytes) / Index(
sizeof(Scalar));
624 const bool canStoreReplacementBasis =
625 Index(activeRows) <= maxReplacementBasisEntries / Index(requiredReplacementCount);
626 if (canStoreReplacementBasis) {
627 replacementBasis.
resize(activeRows, requiredReplacementCount);
628 StorageIndex replacementCount = 0;
629 const StorageIndex maxLookAheadCandidateColumns =
630 (std::min)(n - col - 1, requiredReplacementCount + StorageIndex(PivotLookAheadMaxExtraCandidateColumns));
631 StorageIndex inspectedCandidateCount = 0;
632 StorageIndex candidateCol = col + 1;
638 for (; candidateCol < n && !hasReplacement && inspectedCandidateCount < maxLookAheadCandidateColumns;
639 ++candidateCol, ++inspectedCandidateCount) {
640 if (m_pmat.col(candidateCol).norm() < replaceablePivotThreshold)
continue;
642 for (
typename QRMatrixType::InnerIterator itp(m_pmat, candidateCol); itp; ++itp) {
643 tvalLookAhead(itp.row()) = itp.value();
645 for (StorageIndex previousCol = 0; previousCol < nonzeroCol; ++previousCol) {
646 Scalar tdot = m_Q.col(previousCol).dot(tvalLookAhead);
647 tdot *= m_hcoeffs(previousCol);
648 tvalLookAhead -= tdot * m_Q.col(previousCol);
651 typename ScalarVector::SegmentReturnType candidateTail = tvalLookAhead.segment(nonzeroCol, activeRows);
652 for (StorageIndex replacement = 0; replacement < replacementCount; ++replacement) {
653 candidateTail -= replacementBasis.col(replacement).dot(candidateTail) * replacementBasis.col(replacement);
656 const RealScalar candidateNorm = candidateTail.norm();
657 if (candidateNorm >= replaceablePivotThreshold) {
658 replacementBasis.col(replacementCount) = candidateTail * (RealScalar(1) / candidateNorm);
660 hasReplacement = replacementCount >= requiredReplacementCount;
663 if (!hasReplacement && candidateCol < n) m_lastPivotLookAheadSkipped =
true;
667 m_lastPivotLookAheadSkipped =
true;
673 if (nonzeroCol < diagSize && absBeta >= pivotThreshold && !hasReplacement) {
674 m_R.insertBackByOuterInner(col, nonzeroCol) = beta;
676 m_hcoeffs(nonzeroCol) = tau;
678 for (Index itq = 0; itq < nzcolQ; ++itq) {
679 Index iQ = Qidx(itq);
680 m_Q.insertBackByOuterInnerUnordered(nonzeroCol, iQ) = tval(iQ);
681 tval(iQ) = Scalar(0.);
684 if (nonzeroCol < diagSize) m_Q.startVec(nonzeroCol);
687 for (Index j = nonzeroCol; j < n - 1; j++) std::swap(m_pivotperm.indices()(j), m_pivotperm.indices()[j + 1]);
690 internal::coletree(m_pmat, m_etree, m_firstRowElt, m_pivotperm.indices().data());
695 m_hcoeffs.tail(diagSize - nonzeroCol).setZero();
699 m_Q.makeCompressed();
701 m_R.makeCompressed();
704 m_nonzeropivots = nonzeroCol;
706 if (nonzeroCol < n) {
708 QRMatrixType tempR(m_R);
709 m_R = tempR * m_pivotperm;
712 m_outputPerm_c = m_outputPerm_c * m_pivotperm;
715 m_isInitialized =
true;
716 m_factorizationIsok =
true;