| // SPDX-FileCopyrightText: The Eigen Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| #include "main.h" | |
| #include <Eigen/QR> | |
| // -ffinite-math-only lets the compiler assume every result is finite, so isfinite(), isinf(), and isnan() fold to | |
| // constants in this translation unit and cannot be used to check a result. A comparison against a finite bound is | |
| // still evaluated, and rejects infinities and NaNs alike. | |
| template <typename RealScalar> | |
| bool is_representable(const RealScalar& x) { | |
| return numext::abs(x) <= NumTraits<RealScalar>::highest(); | |
| } | |
| // makeHouseholder must reach the scaled path whenever the direct construction would square the input out of range. | |
| // The dispatch has to be decided from the input magnitudes, because a test on the overflowed sum does not survive | |
| // here. | |
| template <typename RealScalar> | |
| void householder_overflow(const Matrix<RealScalar, Dynamic, 1>& vector) { | |
| typedef Matrix<RealScalar, Dynamic, 1> VectorType; | |
| typedef Matrix<RealScalar, Dynamic, Dynamic> MatrixType; | |
| const Index size = vector.size(); | |
| VectorType essential(size - 1); | |
| RealScalar tau; | |
| RealScalar beta; | |
| vector.makeHouseholder(essential, tau, beta); | |
| VERIFY(is_representable(tau)); | |
| VERIFY(is_representable(beta)); | |
| VERIFY(is_representable(essential.cwiseAbs().maxCoeff())); | |
| // tau and the essential vector are scale invariant and beta is homogeneous, so applying the reflector to a | |
| // power-of-two rescaling of the input is exact and keeps the check itself inside the range. | |
| int exponent = 0; | |
| (void)std::frexp(vector.cwiseAbs().maxCoeff(), &exponent); | |
| const RealScalar scale = std::ldexp(RealScalar(1), exponent); | |
| const VectorType scaled = vector / scale; | |
| VectorType v(size); | |
| v[0] = RealScalar(1); | |
| v.tail(size - 1) = essential; | |
| const MatrixType reflector = MatrixType::Identity(size, size) - tau * v * v.transpose(); | |
| VERIFY_IS_APPROX(reflector.transpose() * reflector, MatrixType::Identity(size, size)); | |
| const VectorType reflected = reflector * scaled; | |
| VERIFY_IS_APPROX(reflected[0], beta / scale); | |
| VERIFY_IS_MUCH_SMALLER_THAN(reflected.tail(size - 1).cwiseAbs().maxCoeff(), RealScalar(1)); | |
| } | |
| template <typename RealScalar> | |
| void householder_overflow_cases() { | |
| typedef Matrix<RealScalar, Dynamic, 1> VectorType; | |
| const RealScalar huge = numext::sqrt(NumTraits<RealScalar>::highest()) * RealScalar(4); | |
| // The head alone squares out of range. | |
| VectorType head(2); | |
| head << huge, huge / RealScalar(8); | |
| householder_overflow<RealScalar>(head); | |
| // A tail component alone squares out of range, with a zero head. | |
| VectorType tail(3); | |
| tail << RealScalar(0), huge * RealScalar(0.6), huge * RealScalar(0.8); | |
| householder_overflow<RealScalar>(tail); | |
| // Accumulated overflow: every square is representable, their sum is not. | |
| householder_overflow<RealScalar>(VectorType::Constant(65, huge / RealScalar(4))); | |
| // A QR factorization of a matrix at this scale has to compose from those reflectors. | |
| const Index rows = 6, cols = 4; | |
| Matrix<RealScalar, Dynamic, Dynamic> matrix(rows, cols); | |
| for (Index j = 0; j < cols; ++j) | |
| for (Index i = 0; i < rows; ++i) matrix(i, j) = huge * RealScalar(internal::random<double>(-1.0, 1.0)); | |
| const HouseholderQR<Matrix<RealScalar, Dynamic, Dynamic> > qr(matrix); | |
| const Matrix<RealScalar, Dynamic, Dynamic> q = | |
| qr.householderQ() * Matrix<RealScalar, Dynamic, Dynamic>::Identity(rows, cols); | |
| const Matrix<RealScalar, Dynamic, Dynamic> r = | |
| qr.matrixQR().topRows(cols).template triangularView<Upper>().toDenseMatrix(); | |
| const Matrix<RealScalar, Dynamic, Dynamic> scaledMatrix = matrix / huge; | |
| VERIFY_IS_APPROX((q * r) / huge, scaledMatrix); | |
| } | |
| EIGEN_DECLARE_TEST(householder_fastmath) { | |
| CALL_SUBTEST_1(householder_overflow_cases<float>()); | |
| CALL_SUBTEST_2(householder_overflow_cases<double>()); | |
| } |