| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // 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/. | |
| // SPDX-FileCopyrightText: The Eigen Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| #ifndef EIGEN_RANK_REVEALING_BASE_H | |
| #define EIGEN_RANK_REVEALING_BASE_H | |
| // IWYU pragma: private | |
| #include "./InternalHeaderCheck.h" | |
| namespace Eigen { | |
| /** \brief CRTP mixin providing threshold management, rank computation, and rank-derived queries | |
| * for rank-revealing decompositions (FullPivLU, ColPivHouseholderQR, FullPivHouseholderQR). | |
| * | |
| * \tparam Derived the concrete decomposition class (CRTP parameter) | |
| * | |
| * The derived class must provide: | |
| * - rows(), cols() (inherited from SolverBase) | |
| * - m_isInitialized (bool member, also used by SolverBase) | |
| * - pivotCoeff(Index i) returning the absolute value of the i-th pivot | |
| */ | |
| template <typename Derived> | |
| class RankRevealingBase { | |
| public: | |
| using Scalar = typename internal::traits<Derived>::Scalar; | |
| using RealScalar = typename NumTraits<Scalar>::Real; | |
| RankRevealingBase() | |
| : m_usePrescribedThreshold(false), | |
| m_prescribedThreshold(RealScalar(0)), | |
| m_maxpivot(RealScalar(0)), | |
| m_nonzero_pivots(0) {} | |
| /** Allows to prescribe a threshold to be used by certain methods, such as rank(), | |
| * which need to determine when pivots are to be considered nonzero. This is not used for the | |
| * decomposition itself. | |
| * | |
| * When it needs to get the threshold value, Eigen calls threshold(). By default, this | |
| * uses a formula to automatically determine a reasonable threshold. | |
| * Once you have called the present method setThreshold(const RealScalar&), | |
| * your value is used instead. | |
| * | |
| * \param threshold The new value to use as the threshold. | |
| * | |
| * A pivot will be considered nonzero if its absolute value is strictly greater than | |
| * \f$ \vert pivot \vert \leqslant threshold \times \vert maxpivot \vert \f$ | |
| * where maxpivot is the biggest pivot. | |
| * | |
| * If you want to come back to the default behavior, call setThreshold(Default_t) | |
| */ | |
| Derived& setThreshold(const RealScalar& threshold) { | |
| m_usePrescribedThreshold = true; | |
| m_prescribedThreshold = threshold; | |
| return self(); | |
| } | |
| /** Allows to come back to the default behavior, letting Eigen use its default formula for | |
| * determining the threshold. | |
| * | |
| * You should pass the special object Eigen::Default as parameter here. | |
| * \code dec.setThreshold(Eigen::Default); \endcode | |
| * | |
| * See the documentation of setThreshold(const RealScalar&). | |
| */ | |
| Derived& setThreshold(Default_t) { | |
| m_usePrescribedThreshold = false; | |
| return self(); | |
| } | |
| /** Returns the threshold that will be used by certain methods such as rank(). | |
| * | |
| * See the documentation of setThreshold(const RealScalar&). | |
| */ | |
| RealScalar threshold() const { | |
| eigen_assert(self().m_isInitialized || m_usePrescribedThreshold); | |
| // Higham's backward error bound: ||ΔA||₂ ≤ c·min(m,n)·u·||A||₂. | |
| // The factor of 4 covers the constant c. | |
| return m_usePrescribedThreshold | |
| ? m_prescribedThreshold | |
| : NumTraits<Scalar>::epsilon() * RealScalar(4 * (std::min)(self().rows(), self().cols())); | |
| } | |
| /** \returns the rank of the matrix of which *this is the decomposition. | |
| * | |
| * \note This method has to determine which pivots should be considered nonzero. | |
| * For that, it uses the threshold value that you can control by calling | |
| * setThreshold(const RealScalar&). | |
| */ | |
| inline Index rank() const { | |
| using std::abs; | |
| eigen_assert(self().m_isInitialized && "Decomposition is not initialized."); | |
| RealScalar premultiplied_threshold = abs(m_maxpivot) * threshold(); | |
| Index result = 0; | |
| for (Index i = 0; i < m_nonzero_pivots; ++i) result += (self().pivotCoeff(i) > premultiplied_threshold); | |
| return result; | |
| } | |
| /** \returns the dimension of the kernel of the matrix of which *this is the decomposition. | |
| * | |
| * \note This method has to determine which pivots should be considered nonzero. | |
| * For that, it uses the threshold value that you can control by calling | |
| * setThreshold(const RealScalar&). | |
| */ | |
| inline Index dimensionOfKernel() const { | |
| eigen_assert(self().m_isInitialized && "Decomposition is not initialized."); | |
| return self().cols() - rank(); | |
| } | |
| /** \returns true if the matrix of which *this is the decomposition represents an injective | |
| * linear map, i.e. has trivial kernel; false otherwise. | |
| * | |
| * \note This method has to determine which pivots should be considered nonzero. | |
| * For that, it uses the threshold value that you can control by calling | |
| * setThreshold(const RealScalar&). | |
| */ | |
| inline bool isInjective() const { | |
| eigen_assert(self().m_isInitialized && "Decomposition is not initialized."); | |
| return rank() == self().cols(); | |
| } | |
| /** \returns true if the matrix of which *this is the decomposition represents a surjective | |
| * linear map; false otherwise. | |
| * | |
| * \note This method has to determine which pivots should be considered nonzero. | |
| * For that, it uses the threshold value that you can control by calling | |
| * setThreshold(const RealScalar&). | |
| */ | |
| inline bool isSurjective() const { | |
| eigen_assert(self().m_isInitialized && "Decomposition is not initialized."); | |
| return rank() == self().rows(); | |
| } | |
| /** \returns true if the matrix of which *this is the decomposition is invertible. | |
| * | |
| * \note This method has to determine which pivots should be considered nonzero. | |
| * For that, it uses the threshold value that you can control by calling | |
| * setThreshold(const RealScalar&). | |
| */ | |
| inline bool isInvertible() const { | |
| eigen_assert(self().m_isInitialized && "Decomposition is not initialized."); | |
| return isInjective() && isSurjective(); | |
| } | |
| /** \returns the number of nonzero pivots in the decomposition. | |
| * Here nonzero is meant in the exact sense, not in a fuzzy sense. | |
| * So that notion isn't really intrinsically interesting, but it is | |
| * still useful when implementing algorithms. | |
| * | |
| * \sa rank() | |
| */ | |
| inline Index nonzeroPivots() const { | |
| eigen_assert(self().m_isInitialized && "Decomposition is not initialized."); | |
| return m_nonzero_pivots; | |
| } | |
| /** \returns the absolute value of the biggest pivot, i.e. the biggest | |
| * diagonal coefficient of U (or R). | |
| */ | |
| RealScalar maxPivot() const { return m_maxpivot; } | |
| protected: | |
| bool m_usePrescribedThreshold; | |
| RealScalar m_prescribedThreshold; | |
| RealScalar m_maxpivot; | |
| Index m_nonzero_pivots; | |
| private: | |
| Derived& self() { return static_cast<Derived&>(*this); } | |
| const Derived& self() const { return static_cast<const Derived&>(*this); } | |
| }; | |
| } // end namespace Eigen | |
| #endif // EIGEN_RANK_REVEALING_BASE_H |