10#ifndef EIGEN_RANDCOLPIVOTINGHOUSEHOLDERQR_H
11#define EIGEN_RANDCOLPIVOTINGHOUSEHOLDERQR_H
16#include "./InternalHeaderCheck.h"
22template <
typename MatrixType_,
typename PermutationIndex_>
23struct traits<RandColPivHouseholderQR<MatrixType_, PermutationIndex_>> : traits<MatrixType_> {
24 using XprKind = MatrixXpr;
25 using StorageKind = SolverStorage;
26 using PermutationIndex = PermutationIndex_;
31template <
typename Derived,
typename Engine>
32EIGEN_STRONG_INLINE std::enable_if_t<!NumTraits<typename Derived::Scalar>::IsComplex> fill_gaussian(
33 MatrixBase<Derived>& mat, Engine& engine) {
34 using Scalar =
typename Derived::Scalar;
35 std::normal_distribution<Scalar> dist(Scalar(0), Scalar(1));
36 for (Index j = 0; j < mat.cols(); ++j)
37 for (Index i = 0; i < mat.rows(); ++i) mat.coeffRef(i, j) = dist(engine);
42template <
typename Derived,
typename Engine>
43EIGEN_STRONG_INLINE std::enable_if_t<NumTraits<typename Derived::Scalar>::IsComplex> fill_gaussian(
44 MatrixBase<Derived>& mat, Engine& engine) {
45 using Scalar =
typename Derived::Scalar;
46 using RealScalar =
typename NumTraits<Scalar>::Real;
47 std::normal_distribution<RealScalar> dist(RealScalar(0), RealScalar(1));
48 for (Index j = 0; j < mat.cols(); ++j)
49 for (Index i = 0; i < mat.rows(); ++i) mat.coeffRef(i, j) = Scalar(dist(engine), dist(engine));
57template <
typename RealScalar,
typename Scalar,
typename RecomputeFn>
58EIGEN_STRONG_INLINE
void lawn176_norm_downdate(RealScalar& norm_updated, RealScalar& norm_direct, Scalar pivot_entry,
59 RealScalar downdate_threshold, RecomputeFn&& tail_norm_fn) {
61 if (numext::is_exactly_zero(norm_updated))
return;
62 RealScalar t = abs(pivot_entry) / norm_updated;
63 t = (RealScalar(1) + t) * (RealScalar(1) - t);
64 if (t < RealScalar(0)) t = RealScalar(0);
65 RealScalar t2 = t * numext::abs2<RealScalar>(norm_updated / norm_direct);
66 if (t2 <= downdate_threshold) {
67 norm_direct = tail_norm_fn();
68 norm_updated = norm_direct;
70 norm_updated *= numext::sqrt(t);
132template <
typename MatrixType_,
typename PermutationIndex_>
134 public RankRevealingBase<RandColPivHouseholderQR<MatrixType_, PermutationIndex_>> {
136 using MatrixType = MatrixType_;
138 using RankRevealingBase_ = RankRevealingBase<RandColPivHouseholderQR>;
150 using PermutationIndex = PermutationIndex_;
154 MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
155 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
157 using HCoeffsType =
typename internal::plain_diag_type<MatrixType>::type;
159 using IntRowVectorType =
typename internal::plain_row_type<MatrixType, PermutationIndex>::type;
160 using RowVectorType =
typename internal::plain_row_type<MatrixType>::type;
161 using RealRowVectorType =
typename internal::plain_row_type<MatrixType, RealScalar>::type;
162 using HouseholderSequenceType =
164 using PlainObject =
typename MatrixType::PlainObject;
169 static constexpr Index kAutoBlockSize = 0;
173 static constexpr Index kKernelFloor = 48;
175 static constexpr Index kBlockCeiling = 1024;
185 static constexpr Index kAutoBlockedPathMinSize = 192;
190 static Index defaultL2Bytes() {
191 std::ptrdiff_t l1, l2, l3;
192 internal::manage_caching_sizes(GetAction, &l1, &l2, &l3);
217 static Index computeAutoBlockSize(Index rows, Index
size) {
222 if (rows <= 0 ||
size <= 0)
return Index(0);
223 const Index scalar_bytes = Index(
sizeof(Scalar));
224 const Index b_cache = numext::maxi(Index(1), (defaultL2Bytes() / Index(2)) / (rows * scalar_bytes));
225 const Index b_bqrrp =
size / Index(32);
227 Index b = numext::maxi(Index(kKernelFloor), b_bqrrp);
228 b = numext::mini(b, numext::maxi(Index(kKernelFloor), b_cache));
229 b = numext::mini(b, Index(kBlockCeiling));
233 void init(Index rows, Index cols) {
234 Index diag = numext::mini(rows, cols);
235 m_hCoeffs.resize(diag);
236 m_colsPermutation.resize(cols);
238 m_isInitialized =
false;
249 template <
typename InputType>
256 template <
typename InputType>
262#ifdef EIGEN_PARSED_BY_DOXYGEN
263 template <
typename Rhs>
267 HouseholderSequenceType householderQ()
const;
268 HouseholderSequenceType matrixQ()
const {
return householderQ(); }
270 const MatrixType& matrixQR()
const {
271 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
275 const MatrixType& matrixR()
const {
276 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
280 template <
typename InputType>
283 const PermutationType& colsPermutation()
const {
284 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
285 return m_colsPermutation;
288 typename MatrixType::Scalar determinant()
const;
289 typename MatrixType::RealScalar absDeterminant()
const;
290 typename MatrixType::RealScalar logAbsDeterminant()
const;
291 typename MatrixType::Scalar signDeterminant()
const;
293 RealScalar pivotCoeff(
Index i)
const {
295 return abs(m_qr.coeff(i, i));
298 inline Inverse<RandColPivHouseholderQR> inverse()
const {
299 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
300 return Inverse<RandColPivHouseholderQR>(*
this);
303 inline Index rows()
const {
return m_qr.rows(); }
304 inline Index cols()
const {
return m_qr.cols(); }
306 const HCoeffsType& hCoeffs()
const {
return m_hCoeffs; }
309 eigen_assert(m_isInitialized &&
"Decomposition is not initialized.");
325 eigen_assert(b >= 0 &&
"Block size must be non-negative.");
360#ifndef EIGEN_PARSED_BY_DOXYGEN
361 template <
typename RhsType,
typename DstType>
362 void _solve_impl(
const RhsType& rhs, DstType& dst)
const;
364 template <
bool Conjugate,
typename RhsType,
typename DstType>
365 void _solve_impl_transposed(
const RhsType& rhs, DstType& dst)
const;
369 friend class internal::CompleteOrthogonalDecompositionImpl<MatrixType, PermutationIndex,
RandColPivHouseholderQR>;
371 EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar)
373 void computeInPlace();
381 Index unblocked_pivoted_qr(Index row0, Index col0, Index ncols, RealScalar threshold_helper);
385 void finalize_rank(RealScalar threshold_helper);
388 HCoeffsType m_hCoeffs;
389 PermutationType m_colsPermutation;
390 RowVectorType m_temp;
391 Index m_blockSize = kAutoBlockSize;
393 bool m_seedSet =
false;
394 bool m_isInitialized =
false;
398template <
typename MatrixType,
typename PermutationIndex>
399typename MatrixType::Scalar RandColPivHouseholderQR<MatrixType, PermutationIndex>::determinant()
const {
400 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
401 eigen_assert(m_qr.rows() == m_qr.cols() &&
"You can't take the determinant of a non-square matrix!");
403 internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
404 return isInjective() ? (detQ * Scalar(m_det_p)) * m_qr.diagonal().prod() : Scalar(0);
407template <
typename MatrixType,
typename PermutationIndex>
408typename MatrixType::RealScalar RandColPivHouseholderQR<MatrixType, PermutationIndex>::absDeterminant()
const {
410 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
411 eigen_assert(m_qr.rows() == m_qr.cols() &&
"You can't take the determinant of a non-square matrix!");
412 return isInjective() ? abs(m_qr.diagonal().prod()) : RealScalar(0);
415template <
typename MatrixType,
typename PermutationIndex>
416typename MatrixType::RealScalar RandColPivHouseholderQR<MatrixType, PermutationIndex>::logAbsDeterminant()
const {
417 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
418 eigen_assert(m_qr.rows() == m_qr.cols() &&
"You can't take the determinant of a non-square matrix!");
419 return isInjective() ? m_qr.diagonal().cwiseAbs().array().log().sum() : -NumTraits<RealScalar>::infinity();
422template <
typename MatrixType,
typename PermutationIndex>
423typename MatrixType::Scalar RandColPivHouseholderQR<MatrixType, PermutationIndex>::signDeterminant()
const {
424 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
425 eigen_assert(m_qr.rows() == m_qr.cols() &&
"You can't take the determinant of a non-square matrix!");
427 internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
428 return isInjective() ? (detQ * Scalar(m_det_p)) * m_qr.diagonal().array().sign().prod() : Scalar(0);
431template <
typename MatrixType,
typename PermutationIndex>
432template <
typename InputType>
440template <
typename MatrixType,
typename PermutationIndex>
441Index RandColPivHouseholderQR<MatrixType, PermutationIndex>::unblocked_pivoted_qr(Index row0, Index col0, Index ncols,
442 RealScalar threshold_helper) {
444 const Index rows = m_qr.rows();
445 const Index col_end = col0 + ncols;
446 const Index sub_rows = rows - row0;
447 Index num_transpositions = 0;
451 const RealScalar downdate_threshold = numext::sqrt(NumTraits<RealScalar>::epsilon());
453 const Index size = (std::min)(sub_rows, ncols);
454 for (Index k = 0; k < size; ++k) {
456 RealScalar biggest_sq = numext::abs2(norms_updated.tail(ncols - k).maxCoeff(&biggest));
459 if (this->m_nonzero_pivots == m_qr.diagonalSize() && biggest_sq < threshold_helper * RealScalar(rows - row0 - k))
460 this->m_nonzero_pivots = col0 + k;
463 m_qr.col(col0 + k).swap(m_qr.col(col0 + biggest));
464 std::swap(norms_updated.coeffRef(k), norms_updated.coeffRef(biggest));
465 std::swap(norms_direct.coeffRef(k), norms_direct.coeffRef(biggest));
466 m_colsPermutation.applyTranspositionOnTheRight(col0 + k, col0 + biggest);
467 ++num_transpositions;
471 m_qr.col(col0 + k).tail(sub_rows - k).makeHouseholderInPlace(m_hCoeffs.coeffRef(col0 + k), beta);
472 m_qr.coeffRef(row0 + k, col0 + k) = beta;
473 if (abs(beta) > this->m_maxpivot) this->m_maxpivot = abs(beta);
479 const Index trail_cols = col_end - col0 - k - 1;
480 if (trail_cols > 0) {
481 m_qr.block(row0 + k, col0 + k + 1, sub_rows - k, trail_cols)
482 .applyHouseholderOnTheLeft(m_qr.col(col0 + k).tail(sub_rows - k - 1), m_hCoeffs.coeff(col0 + k),
483 &m_temp.coeffRef(col0 + k + 1));
486 for (Index j = k + 1; j < ncols; ++j) {
487 internal::lawn176_norm_downdate(norms_updated.coeffRef(j), norms_direct.coeffRef(j),
488 m_qr.coeff(row0 + k, col0 + j), downdate_threshold,
489 [&] { return m_qr.col(col0 + j).tail(sub_rows - k - 1).norm(); });
492 return num_transpositions;
495template <
typename MatrixType,
typename PermutationIndex>
496void RandColPivHouseholderQR<MatrixType, PermutationIndex>::finalize_rank(RealScalar threshold_helper) {
498 const Index rows = m_qr.rows();
499 const Index size = (std::min)(rows, m_qr.cols());
507 if (this->m_nonzero_pivots == size) {
508 for (Index i = 0; i < size; ++i) {
509 RealScalar a = abs(m_qr.coeff(i, i));
510 if (numext::abs2(a) < threshold_helper * RealScalar(rows - i)) {
511 this->m_nonzero_pivots = i;
518template <
typename MatrixType,
typename PermutationIndex>
519void RandColPivHouseholderQR<MatrixType, PermutationIndex>::computeInPlace() {
520 eigen_assert(m_qr.cols() <= NumTraits<PermutationIndex>::highest());
522 const Index rows = m_qr.rows();
523 const Index cols = m_qr.cols();
524 const Index size = (std::min)(rows, cols);
526 m_hCoeffs.resize(size);
528 m_colsPermutation.resize(cols);
529 m_colsPermutation.setIdentity();
530 this->m_nonzero_pivots = size;
531 this->m_maxpivot = RealScalar(0);
532 Index num_transpositions = 0;
537 RealScalar max_initial_norm = RealScalar(0);
538 for (Index j = 0; j < cols; ++j) max_initial_norm = numext::maxi(max_initial_norm, m_qr.col(j).norm());
539 const RealScalar threshold_helper =
540 cols == 0 ? RealScalar(0)
541 : numext::abs2<RealScalar>(max_initial_norm *
NumTraits<RealScalar>::epsilon()) / RealScalar(rows);
543 const bool auto_block = (m_blockSize == kAutoBlockSize);
544 const Index requested_b = auto_block ? computeAutoBlockSize(rows, size) : m_blockSize;
545 const Index b = (std::min)(requested_b, size);
556 const bool can_block = (b > 0) && (size >= 2 * b) && (rows > b) && (!auto_block || size >= kAutoBlockedPathMinSize);
558 num_transpositions += unblocked_pivoted_qr(0, 0, cols, threshold_helper);
559 m_det_p = (num_transpositions % 2) ? -1 : 1;
560 finalize_rank(threshold_helper);
561 m_isInitialized =
true;
581 WorkMatrix Y(b, cols);
582 WorkMatrix YT(cols, b);
583 WorkMatrix sketch_R(b, b);
585 WorkVector y_hcoeffs(b);
586 WorkVector y_temp(cols);
592 WorkMatrix G(b, rows);
593 uint64_t seed = m_seed;
595 std::random_device rd;
596 seed = (uint64_t(rd()) << 32) | uint64_t(rd());
598 std::mt19937_64 engine(seed);
599 internal::fill_gaussian(G, engine);
600 Y.noalias() = G * m_qr;
604 bool blocked_terminated_early =
false;
605 while (k + b <= size && cols - k > b) {
606 const Index n_remain = cols - k;
607 const Index trail_cols = n_remain - b;
608 const Index sub_rows = rows - k;
614 auto Y_curr = Y.middleCols(k, n_remain);
615 auto Y_T = YT.topRows(n_remain);
616 Y_T = Y_curr.transpose();
618 typename IpivType::StorageIndex nb_lu_transp = 0;
619 internal::partial_lu_inplace(Y_T, ipiv, nb_lu_transp);
625 for (Index i = 0; i < b; ++i) {
626 Index dst =
static_cast<Index
>(ipiv.coeff(i));
628 m_qr.col(k + i).swap(m_qr.col(k + dst));
629 Y.col(k + i).swap(Y.col(k + dst));
630 m_colsPermutation.applyTranspositionOnTheRight(k + i, k + dst);
631 ++num_transpositions;
642 WorkMatrixRef Y_curr_ref(Y_curr);
644 internal::householder_qr_inplace_blocked<WorkMatrixRef, Ref<WorkVector>>::run(
645 Y_curr_ref, y_hc_ref, (std::min)(b, Index(48)), y_temp.data());
651 sketch_R = Y.block(0, k, b, b);
654 sketch_R.template triangularView<StrictlyLower>().setZero();
657 auto panel = m_qr.block(k, k, sub_rows, b);
658 auto hCoeffsSegment = m_hCoeffs.segment(k, b);
660 QrPanelRef panel_ref(panel);
661 HCoeffsRef hc_ref(hCoeffsSegment);
662 internal::householder_qr_inplace_blocked<QrPanelRef, HCoeffsRef>::run(
663 panel_ref, hc_ref, (std::min)(b, Index(48)), m_temp.data());
666 this->m_maxpivot = (std::max)(this->m_maxpivot, m_qr.diagonal().segment(k, b).cwiseAbs().maxCoeff());
676 Index panel_rank = b;
679 const RealScalar relative_cutoff = this->m_maxpivot * NumTraits<RealScalar>::epsilon() * RealScalar(4 * size);
680 for (Index i = 0; i < b; ++i) {
681 RealScalar a = abs(m_qr.coeff(k + i, k + i));
682 if (a <= relative_cutoff) {
694 auto trailing = m_qr.block(k, k + b, sub_rows, trail_cols);
695 internal::apply_block_householder_on_the_left(trailing, panel, hCoeffsSegment,
698 if (panel_rank < b) {
703 if (this->m_nonzero_pivots == size) {
704 this->m_nonzero_pivots = k + panel_rank;
707 blocked_terminated_early =
true;
722 m_qr.block(k, k, b, b).template triangularView<Upper>().template solveInPlace<OnTheRight>(sketch_R);
723 Y.middleCols(k + b, trail_cols).noalias() -= sketch_R * m_qr.block(k, k + b, b, trail_cols);
733 if (k < cols && (k < size || blocked_terminated_early)) {
734 num_transpositions += unblocked_pivoted_qr(k, k, cols - k, threshold_helper);
737 m_det_p = (num_transpositions % 2) ? -1 : 1;
742 finalize_rank(threshold_helper);
744 m_isInitialized =
true;
747#ifndef EIGEN_PARSED_BY_DOXYGEN
748template <
typename MatrixType_,
typename PermutationIndex_>
749template <
typename RhsType,
typename DstType>
751 const Index nonzero_pivots = nonzeroPivots();
753 if (nonzero_pivots == 0) {
758 typename RhsType::PlainObject c(rhs);
760 c.applyOnTheLeft(householderQ().setLength(nonzero_pivots).adjoint());
762 m_qr.topLeftCorner(nonzero_pivots, nonzero_pivots)
763 .template triangularView<Upper>()
764 .solveInPlace(c.topRows(nonzero_pivots));
766 for (Index i = 0; i < nonzero_pivots; ++i) dst.row(m_colsPermutation.indices().coeff(i)) = c.row(i);
767 for (Index i = nonzero_pivots; i < cols(); ++i) dst.row(m_colsPermutation.indices().coeff(i)).setZero();
770template <
typename MatrixType_,
typename PermutationIndex_>
771template <
bool Conjugate,
typename RhsType,
typename DstType>
773 DstType& dst)
const {
774 const Index nonzero_pivots = nonzeroPivots();
776 if (nonzero_pivots == 0) {
781 typename RhsType::PlainObject c(m_colsPermutation.transpose() * rhs);
783 m_qr.topLeftCorner(nonzero_pivots, nonzero_pivots)
784 .template triangularView<Upper>()
786 .template conjugateIf<Conjugate>()
787 .solveInPlace(c.topRows(nonzero_pivots));
789 dst.topRows(nonzero_pivots) = c.topRows(nonzero_pivots);
790 dst.bottomRows(rows() - nonzero_pivots).setZero();
792 dst.applyOnTheLeft(householderQ().setLength(nonzero_pivots).
template conjugateIf<!Conjugate>());
798template <
typename DstXprType,
typename MatrixType,
typename PermutationIndex>
799struct Assignment<DstXprType, Inverse<RandColPivHouseholderQR<MatrixType, PermutationIndex>>,
800 internal::assign_op<typename DstXprType::Scalar,
801 typename RandColPivHouseholderQR<MatrixType, PermutationIndex>::Scalar>,
803 using QrType = RandColPivHouseholderQR<MatrixType, PermutationIndex>;
804 using SrcXprType = Inverse<QrType>;
805 static void run(DstXprType& dst,
const SrcXprType& src,
806 const internal::assign_op<typename DstXprType::Scalar, typename QrType::Scalar>&) {
807 dst = src.nestedExpression().solve(MatrixType::Identity(src.rows(), src.cols()));
813template <
typename MatrixType,
typename PermutationIndex>
814typename RandColPivHouseholderQR<MatrixType, PermutationIndex>::HouseholderSequenceType
815RandColPivHouseholderQR<MatrixType, PermutationIndex>::householderQ()
const {
816 eigen_assert(m_isInitialized &&
"RandColPivHouseholderQR is not initialized.");
817 return HouseholderSequenceType(m_qr, m_hCoeffs.conjugate());
824template <
typename Derived>
825template <
typename PermutationIndexType>
827MatrixBase<Derived>::randColPivHouseholderQr()
const {
EvalReturnType eval() const
Definition DenseBase.h:385
Sequence of Householder reflections acting on subspaces with decreasing size.
Definition HouseholderSequence.h:140
Base class for all dense matrices, vectors, and expressions.
Definition MatrixBase.h:53
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:188
Permutation matrix.
Definition PermutationMatrix.h:346
Randomized blocked Householder rank-revealing QR with column pivoting.
Definition RandColPivHouseholderQR.h:134
RandColPivHouseholderQR(Index rows, Index cols)
Constructor with memory preallocation.
Definition RandColPivHouseholderQR.h:246
RandColPivHouseholderQR & setBlockSize(Index b)
Sets the panel block size b.
Definition RandColPivHouseholderQR.h:324
RandColPivHouseholderQR & setOversampling(Index)
Sets the oversampling parameter p.
Definition RandColPivHouseholderQR.h:338
Index oversampling() const
Definition RandColPivHouseholderQR.h:358
Index blockSize() const
Returns the user-set panel block size, or 0 if the algorithm should pick automatically....
Definition RandColPivHouseholderQR.h:355
RandColPivHouseholderQR()=default
Default constructor.
RandColPivHouseholderQR(EigenBase< InputType > &matrix)
Inplace constructor: takes a Ref and decomposes in place.
Definition RandColPivHouseholderQR.h:257
RandColPivHouseholderQR(const EigenBase< InputType > &matrix)
Constructs and computes a QR factorization from matrix.
Definition RandColPivHouseholderQR.h:250
RandColPivHouseholderQR & setSeed(uint64_t seed)
Fixes the seed of the internal RNG for reproducible factorization.
Definition RandColPivHouseholderQR.h:345
Index dimensionOfKernel() const
Definition RankRevealingBase.h:110
bool isInjective() const
Definition RankRevealingBase.h:122
bool isSurjective() const
Definition RankRevealingBase.h:134
RandColPivHouseholderQR & setThreshold(const RealScalar &threshold)
Definition RankRevealingBase.h:57
Index nonzeroPivots() const
Definition RankRevealingBase.h:157
RealScalar threshold() const
Definition RankRevealingBase.h:80
RealScalar maxPivot() const
Definition RankRevealingBase.h:165
Index rank() const
Definition RankRevealingBase.h:95
bool isInvertible() const
Definition RankRevealingBase.h:145
A matrix or vector expression mapping an existing expression.
Definition Ref.h:262
Pseudo expression representing a solving operation.
Definition Solve.h:63
constexpr Derived & derived()
Definition EigenBase.h:50
Represents a sequence of transpositions (row/column interchange)
Definition Transpositions.h:144
ComputationInfo
Definition Constants.h:455
@ Success
Definition Constants.h:457
Definition EigenBase.h:34
constexpr Index cols() const noexcept
Definition EigenBase.h:62
constexpr Derived & derived()
Definition EigenBase.h:50
constexpr Index rows() const noexcept
Definition EigenBase.h:60
constexpr Index size() const noexcept
Definition EigenBase.h:65
Eigen::Index Index
The interface type of indices.
Definition EigenBase.h:44
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition NumTraits.h:233