blob: f152727966c4ded9e65fb64bf8f23e09da09629b [file]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2013 Gauthier Brun <brun.gauthier@gmail.com>
// Copyright (C) 2013 Nicolas Carre <nicolas.carre@ensimag.fr>
// Copyright (C) 2013 Jean Ceccato <jean.ceccato@ensimag.fr>
// Copyright (C) 2013 Pierre Zoppitelli <pierre.zoppitelli@ensimag.fr>
//
// 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-License-Identifier: MPL-2.0
// discard stack allocation as that too bypasses malloc
#define EIGEN_STACK_ALLOCATION_LIMIT 0
#define EIGEN_RUNTIME_NO_MALLOC
#include "main.h"
#include "fp_control.h"
#include "tridiag_test_matrices.h"
#include <Eigen/SVD>
#include <cstdlib>
#define SVD_DEFAULT(M) BDCSVD<M>
#define SVD_FOR_MIN_NORM(M) BDCSVD<M>
#define SVD_STATIC_OPTIONS(M, O) BDCSVD<M, O>
#include "svd_common.h"
template <typename MatrixType>
void bdcsvd_method() {
enum { Size = MatrixType::RowsAtCompileTime };
typedef typename MatrixType::RealScalar RealScalar;
typedef Matrix<RealScalar, Size, 1> RealVecType;
MatrixType m = MatrixType::Identity();
VERIFY_IS_APPROX(m.bdcSvd().singularValues(), RealVecType::Ones());
VERIFY_RAISES_ASSERT(m.bdcSvd().matrixU());
VERIFY_RAISES_ASSERT(m.bdcSvd().matrixV());
}
// compare the Singular values returned with Jacobi and Bdc
template <typename MatrixType>
void compare_bdc_jacobi(const MatrixType& a = MatrixType(), int algoswap = 16, bool random = true) {
MatrixType m = random ? MatrixType::Random(a.rows(), a.cols()) : a;
BDCSVD<MatrixType> bdc_svd(m.rows(), m.cols());
bdc_svd.setSwitchSize(algoswap);
bdc_svd.compute(m);
JacobiSVD<MatrixType> jacobi_svd(m);
VERIFY_IS_APPROX(bdc_svd.singularValues(), jacobi_svd.singularValues());
}
#if defined(EIGEN_TEST_PART_46) || defined(EIGEN_TEST_PART_47) || defined(EIGEN_TEST_PART_48) || \
defined(EIGEN_TEST_PART_49) || defined(EIGEN_TEST_PART_ALL)
// Verifies total deflation is **not** triggered.
template <typename = void>
void compare_bdc_jacobi_instance(bool structure_as_m, int algoswap = 16) {
MatrixXd m(4, 3);
if (structure_as_m) {
// The first 3 rows are the reduced form of Matrix 1 as shown below, and it
// has nonzero elements in the first column and diagonals only.
m << 1.056293, 0, 0, -0.336468, 0.907359, 0, -1.566245, 0, 0.149150, -0.1, 0, 0;
} else {
// Matrix 1.
m << 0.882336, 18.3914, -26.7921, -5.58135, 17.1931, -24.0892, -20.794, 8.68496, -4.83103, -8.4981, -10.5451,
23.9072;
}
compare_bdc_jacobi(m, algoswap, false);
}
#endif
template <typename MatrixType>
void bdcsvd_thin_full_options(const MatrixType& input = MatrixType()) {
svd_thin_full_option_checks<MatrixType, 0>(input);
svd_thin_full_option_checks<MatrixType, PreconditionSquareMatrix>(input);
}
template <typename MatrixType>
void bdcsvd_asserts(const MatrixType& input = MatrixType()) {
MatrixType m(input.rows(), input.cols());
svd_fill_random(m);
svd_verify_assert<MatrixType>(m);
}
template <typename MatrixType>
void bdcsvd_check_convergence(const MatrixType& input) {
BDCSVD<MatrixType, Eigen::ComputeThinU | Eigen::ComputeThinV> svd(input);
VERIFY(svd.info() == Eigen::Success);
MatrixType D = svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().transpose();
VERIFY_IS_APPROX(input, D);
}
// Verify SVD of bidiagonal matrix given as diagonal + superdiagonal vectors.
template <typename RealScalar, int Options = ComputeFullU | ComputeFullV>
void verify_bidiagonal_svd(const Matrix<RealScalar, Dynamic, 1>& diag,
const Matrix<RealScalar, Dynamic, 1>& superdiag) {
typedef Matrix<RealScalar, Dynamic, Dynamic> MatrixXr;
typedef Matrix<RealScalar, Dynamic, 1> VectorXr;
const Index n = diag.size();
BDCSVD<MatrixXr, Options> bdcsvd(diag, superdiag);
VERIFY(bdcsvd.info() == Success);
const VectorXr& sv = bdcsvd.singularValues();
// Singular values must be non-negative.
for (Index i = 0; i < sv.size(); ++i) {
VERIFY(sv(i) >= RealScalar(0));
}
// Singular values must be sorted descending.
for (Index i = 1; i < sv.size(); ++i) {
VERIFY(sv(i - 1) >= sv(i));
}
// Orthogonality of U and V.
VERIFY_IS_APPROX(bdcsvd.matrixU().transpose() * bdcsvd.matrixU(), MatrixXr::Identity(n, n));
VERIFY_IS_APPROX(bdcsvd.matrixV().transpose() * bdcsvd.matrixV(), MatrixXr::Identity(n, n));
// Reconstruction: U * S * V^T should equal the original bidiagonal.
MatrixXr B = MatrixXr::Zero(n, n);
B.diagonal() = diag;
if (n > 1) B.diagonal(1) = superdiag;
MatrixXr recon = bdcsvd.matrixU() * sv.asDiagonal() * bdcsvd.matrixV().transpose();
VERIFY_IS_APPROX(recon, B);
// Cross-validate singular values against JacobiSVD.
JacobiSVD<MatrixXr> jacobi(B);
VERIFY_IS_APPROX(sv, jacobi.singularValues());
}
// Verify that bidiagonal API and matrix API produce matching singular values.
template <typename RealScalar, int Options = 0>
void verify_bidiagonal_vs_matrix_svd(const Matrix<RealScalar, Dynamic, 1>& diag,
const Matrix<RealScalar, Dynamic, 1>& superdiag) {
typedef Matrix<RealScalar, Dynamic, Dynamic> MatrixXr;
const Index n = diag.size();
// Build dense bidiagonal matrix.
MatrixXr B = MatrixXr::Zero(n, n);
B.diagonal() = diag;
if (n > 1) B.diagonal(1) = superdiag;
BDCSVD<MatrixXr, Options> bidiag_svd(diag, superdiag);
BDCSVD<MatrixXr, Options> matrix_svd(B);
VERIFY(bidiag_svd.info() == Success);
VERIFY(matrix_svd.info() == Success);
VERIFY_IS_APPROX(bidiag_svd.singularValues(), matrix_svd.singularValues());
}
template <typename RealScalar>
void bdcsvd_bidiagonal_hard_cases() {
Eigen::internal::set_is_malloc_allowed(true);
// Use the shared tridiagonal test matrix generators.
// Each generator fills (diag, offdiag) which we treat as (diagonal, superdiagonal)
// of a bidiagonal matrix.
test::for_all_tridiag_test_matrices<RealScalar>([](const auto& diag, const auto& offdiag) {
verify_bidiagonal_svd<RealScalar>(diag, offdiag);
verify_bidiagonal_svd<RealScalar, PreconditionSquareMatrix | ComputeFullU | ComputeFullV>(diag, offdiag);
});
// Additional SVD-specific test: identity with cross-validation against full matrix SVD.
test::for_tridiag_sizes<RealScalar>([](auto& diag, auto& offdiag) {
test::tridiag_identity(diag, offdiag);
verify_bidiagonal_vs_matrix_svd<RealScalar>(diag, offdiag);
});
// Additional SVD-specific test: scalar for n=1.
{
typedef Matrix<RealScalar, Dynamic, 1> VectorXr;
VectorXr diag(1), offdiag(0);
diag(0) = RealScalar(3.14);
verify_bidiagonal_svd<RealScalar>(diag, offdiag);
}
}
#if defined(EIGEN_TEST_PART_6) || defined(EIGEN_TEST_PART_ALL)
template <typename = void>
void bdcsvd_mixed_option_enum_regression() {
using NoQrFullSVD = BDCSVD<MatrixXd, NoQRPreconditioner | ComputeFullU | ComputeFullV>;
using ReversedMixedSVD = BDCSVD<MatrixXd, ComputeThinU | DisableQRDecomposition | ComputeFullV>;
STATIC_CHECK((int(NoQrFullSVD::QRDecomposition) == int(NoQRPreconditioner)));
STATIC_CHECK((NoQrFullSVD::ComputationOptions == (ComputeFullU | ComputeFullV)));
STATIC_CHECK((int(ReversedMixedSVD::QRDecomposition) == int(DisableQRDecomposition)));
STATIC_CHECK((ReversedMixedSVD::ComputationOptions == (ComputeThinU | ComputeFullV)));
// Runtime unitary options must reach the JacobiSVD used below the switch size when Options holds only QR bits.
const MatrixXd m = MatrixXd::Random(5, 5);
EIGEN_DIAGNOSTICS(push)
EIGEN_DISABLE_DEPRECATED_WARNING
const BDCSVD<MatrixXd, DisableQRDecomposition> svd(m, ComputeFullU | ComputeFullV);
EIGEN_DIAGNOSTICS(pop)
svd_check_full(m, svd);
}
#endif
#if defined(EIGEN_TEST_PART_65) || defined(EIGEN_TEST_PART_ALL)
template <typename = void>
void bdcsvd_switch_size() {
const MatrixXd m = MatrixXd::Random(20, 20);
BDCSVD<MatrixXd, ComputeFullU | ComputeFullV> svd(m.rows(), m.cols());
svd.setSwitchSize(32);
svd.compute(m);
svd_check_full(m, svd);
svd.setSwitchSize(4);
svd.compute(m);
svd_check_full(m, svd);
svd.setSwitchSize(32);
svd.compute(m);
svd_check_full(m, svd);
// The bidiagonal entry point allocates through the same path.
const VectorXd diag = VectorXd::Random(20);
const VectorXd superdiag = VectorXd::Random(19);
MatrixXd bidiag = MatrixXd::Zero(20, 20);
bidiag.diagonal() = diag;
bidiag.diagonal(1) = superdiag;
BDCSVD<MatrixXd, ComputeFullU | ComputeFullV> bidiag_svd(bidiag.rows(), bidiag.cols());
bidiag_svd.setSwitchSize(32);
bidiag_svd.compute(diag, superdiag);
svd_check_full(bidiag, bidiag_svd);
}
#endif
#if defined(EIGEN_TEST_PART_62) || defined(EIGEN_TEST_PART_ALL)
// Below the switch size, the bidiagonal API forwards PreconditionSquareMatrix to BDCSVD's JacobiSVD.
template <typename RealScalar>
void bdcsvd_precondition_square_matrix_bidiagonal(Index n) {
using VectorXr = Matrix<RealScalar, Dynamic, 1>;
const VectorXr diagonal = VectorXr::Random(n);
const VectorXr superdiagonal = VectorXr::Random(n - 1);
verify_bidiagonal_vs_matrix_svd<RealScalar, PreconditionSquareMatrix>(diagonal, superdiagonal);
}
#endif
#if defined(EIGEN_TEST_PART_53) || defined(EIGEN_TEST_PART_ALL)
template <typename = void>
void bdcsvd_extreme_scale_regressions() {
typedef Matrix<double, 6, 6> Matrix6d;
const double kTolerance = 16 * Matrix6d::RowsAtCompileTime * NumTraits<double>::epsilon();
const auto verify_decomposition = [kTolerance](const Matrix6d& matrix) {
BDCSVD<Matrix6d, ComputeFullU | ComputeFullV> svd;
svd.setSwitchSize(3);
svd.compute(matrix);
VERIFY(svd.info() == Success);
const Matrix6d reconstruction = svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().transpose();
VERIFY((reconstruction - matrix).stableNorm() <= kTolerance * matrix.stableNorm());
const Matrix6d identity = Matrix6d::Identity();
VERIFY((svd.matrixU().transpose() * svd.matrixU() - identity).stableNorm() <= kTolerance);
VERIFY((svd.matrixV().transpose() * svd.matrixV() - identity).stableNorm() <= kTolerance);
// Also exercise the values-only path, which uses a compact m_naiveU and
// linear workspace during divide-and-conquer merges.
BDCSVD<Matrix6d> valuesOnlySvd;
valuesOnlySvd.setSwitchSize(3);
valuesOnlySvd.compute(matrix);
VERIFY(valuesOnlySvd.info() == Success);
VERIFY_IS_APPROX(valuesOnlySvd.singularValues(), svd.singularValues());
};
Matrix6d matrix = Matrix6d::Zero();
const double kSubnormal1040 = std::numeric_limits<double>::denorm_min() * 17179869184.0; // 2^-1040
const double kSubnormal1060 = std::numeric_limits<double>::denorm_min() * 16384.0; // 2^-1060
const double kSmallestNormal = (std::numeric_limits<double>::min)(); // 2^-1022
const double kNormal1000 = kSmallestNormal * 4194304.0; // 2^-1000
// The merge combines normal and subnormal couplings. Squaring the two
// coupling terms directly used to underflow, which later left perturbCol0
// without a predecessor and made the decomposition report NumericalIssue.
matrix.diagonal() << kSubnormal1040, -kSubnormal1060, kSmallestNormal, 0.5, 1.0, kSmallestNormal;
matrix.diagonal(1) << -kNormal1000, kNormal1000, kSubnormal1040, kSubnormal1060, -8.0;
verify_decomposition(matrix);
// A singular-vector coefficient grows to about 2^570 here. Its squared
// norm overflows even though the vector has a finite, well-scaled
// normalization.
using std::ldexp;
matrix.setZero();
matrix.diagonal() << 0.0, 0.0, ldexp(1.0, -487), -1.0, 0.0, 0.0;
matrix.diagonal(1) << 0.0, ldexp(1.0, -453), -ldexp(1.0, -627), 0.0, 0.0;
verify_decomposition(matrix);
}
#endif
#if defined(EIGEN_TEST_PART_54) || defined(EIGEN_TEST_PART_ALL)
template <typename = void>
void bdcsvd_fast_math_regression_1588() {
const Index n = 500;
MatrixXd matrix = MatrixXd::Zero(n, n);
std::srand(1);
for (Index k = 0; k < 5000; ++k) {
const Index row = std::rand() % n;
const Index col = std::rand() % n;
matrix(row, col) = static_cast<double>(std::rand()) / static_cast<double>(RAND_MAX);
}
matrix = matrix * matrix;
BDCSVD<MatrixXd, ComputeThinU | ComputeThinV> svd(matrix);
VERIFY(svd.info() == Success);
MatrixXd reconstruction = svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().transpose();
const double relative_error = (reconstruction - matrix).norm() / matrix.norm();
// Deterministic input (fixed seed); the reconstruction is backward stable, so the relative error stays near eps.
VERIFY(relative_error < 64 * NumTraits<double>::epsilon());
}
#endif
// An all-subnormal matrix, which a SIMD unit that flushes subnormal inputs (ARMv7 NEON, Arm FZ, DAZ) reads as zero in
// the maxCoeff() selecting the scale: recovering the maximum from the representation and scaling through integer
// significands keeps the decomposition, and the singular values come back through the subnormal range exactly. The
// switch size below the dimensions keeps the divide-and-conquer path rather than the JacobiSVD fallback.
template <typename MatrixType>
void bdcsvd_flushed_subnormal_matrix(Index rows, Index cols) {
using RealScalar = typename MatrixType::RealScalar;
const MatrixType m = svd_subnormal_fixture<MatrixType>(rows, cols);
const int k = 60;
const MatrixType ms = m.unaryExpr(internal::scale_by_exponent_op<RealScalar>(k));
BDCSVD<MatrixType, ComputeFullU | ComputeFullV> scaled;
scaled.setSwitchSize(8);
scaled.compute(ms);
forEachFlushToZeroMode([&](FlushToZeroMode) {
BDCSVD<MatrixType, ComputeFullU | ComputeFullV> svd;
svd.setSwitchSize(8);
svd.compute(m);
svd_check_flushed_subnormal(svd, scaled, m, k);
});
}
// The bidiagonal entry point, on both sides of the switch size: divide and conquer above it, JacobiSVD below.
template <typename RealScalar>
void bdcsvd_flushed_subnormal_bidiagonal(Index n, int switchSize) {
using Vec = Matrix<RealScalar, Dynamic, 1>;
using Mat = Matrix<RealScalar, Dynamic, Dynamic>;
const Vec diagonal = svd_subnormal_fixture<Vec>(n, 1), superdiagonal = svd_subnormal_fixture<Vec>(n - 1, 1);
const int k = 60;
const Vec ds = diagonal.unaryExpr(internal::scale_by_exponent_op<RealScalar>(k));
const Vec ss = superdiagonal.unaryExpr(internal::scale_by_exponent_op<RealScalar>(k));
Mat B = Mat::Zero(n, n);
B.diagonal() = diagonal;
B.diagonal(1) = superdiagonal;
BDCSVD<Mat, ComputeFullU | ComputeFullV> scaled;
scaled.setSwitchSize(switchSize);
scaled.compute(ds, ss);
forEachFlushToZeroMode([&](FlushToZeroMode) {
BDCSVD<Mat, ComputeFullU | ComputeFullV> svd;
svd.setSwitchSize(switchSize);
svd.compute(diagonal, superdiagonal);
svd_check_flushed_subnormal(svd, scaled, B, k);
});
}
template <typename = void>
void bdcsvd_power_of_two_scaling() {
// Reciprocal scaling rounds the smaller singular value down by one ULP in both entry paths.
const Index size = 20;
VectorXf diagonal = VectorXf::Zero(size);
diagonal(0) = numext::bit_cast<float>(numext::uint32_t(0x13afd4a1));
diagonal(1) = numext::bit_cast<float>(numext::uint32_t(0x11525720));
MatrixXf matrix = MatrixXf::Zero(size, size + 1);
matrix.leftCols(size).diagonal() = diagonal;
BDCSVD<MatrixXf> denseSvd;
denseSvd.setSwitchSize(8);
denseSvd.compute(matrix);
VERIFY_IS_EQUAL(denseSvd.singularValues()(0), diagonal(0));
VERIFY_IS_EQUAL(denseSvd.singularValues()(1), diagonal(1));
BDCSVD<MatrixXf> bidiagonalSvd;
bidiagonalSvd.setSwitchSize(8);
bidiagonalSvd.compute(diagonal, VectorXf::Zero(size - 1));
VERIFY_IS_EQUAL(bidiagonalSvd.singularValues()(0), diagonal(0));
VERIFY_IS_EQUAL(bidiagonalSvd.singularValues()(1), diagonal(1));
// A subnormal coupling remains significant relative to a uniformly tiny bidiagonal matrix. Scale it without first
// feeding the subnormal operand to packet arithmetic, which flushes it on ARMv7 NEON.
const auto checkSubnormalCoupling = [&]() {
volatile float normalMin = (std::numeric_limits<float>::min)();
const float largest = 256.0f * normalMin;
// Construct min_normal / 2 without arithmetic that FTZ would flush before it reaches the solver.
const float coupling = numext::bit_cast<float>(numext::uint32_t(0x00400000));
diagonal.setZero();
diagonal(0) = largest;
VectorXf superdiagonal = VectorXf::Zero(size - 1);
superdiagonal(0) = coupling;
VERIFY_IS_EQUAL(numext::bit_cast<numext::uint32_t>(superdiagonal(0)), numext::uint32_t(0x00400000));
const float expectedRatio = numext::sqrt(1.0f + 1.0f / (512.0f * 512.0f));
for (int switchSize : {8, 32}) {
bidiagonalSvd.setSwitchSize(switchSize);
bidiagonalSvd.compute(diagonal, superdiagonal);
VERIFY(numext::abs(bidiagonalSvd.singularValues()(0) / largest - expectedRatio) <=
2.0f * NumTraits<float>::epsilon());
}
};
checkSubnormalCoupling();
ScopedFlushToZero ftz;
checkSubnormalCoupling();
}
template <typename RealScalar>
void bdcsvd_secular_extrapolation() {
using Mat = Matrix<RealScalar, Dynamic, Dynamic>;
using Vec = Matrix<RealScalar, Dynamic, 1>;
for (Index n : {31, 32, 33, 63, 64, 65, 127, 128, 129}) {
Mat bidiagonal = Mat::Zero(n, n);
Vec diagonal = Vec::Random(n), superdiagonal = Vec::Random(n - 1);
for (int kind = 0; kind < 3; ++kind) {
if (kind == 1) {
diagonal.setOnes();
superdiagonal *= numext::sqrt(NumTraits<RealScalar>::epsilon());
} else if (kind == 2) {
diagonal = Vec::LinSpaced(n, RealScalar(0), RealScalar(1));
superdiagonal.setRandom();
for (Index i = 0; i + 1 < n; i += 7) superdiagonal(i) = RealScalar(0);
}
bidiagonal.diagonal() = diagonal;
bidiagonal.template diagonal<1>() = superdiagonal;
BDCSVD<Mat, ComputeFullU | ComputeFullV> svd(diagonal, superdiagonal);
VERIFY_IS_EQUAL(svd.info(), Success);
// Reconstruction and orthogonality each accumulate O(n * epsilon) rounding.
const RealScalar bound = RealScalar(64 * n) * NumTraits<RealScalar>::epsilon();
VERIFY((bidiagonal - svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().transpose()).norm() <=
bound * bidiagonal.norm());
const Mat identity = Mat::Identity(n, n);
VERIFY((svd.matrixU().transpose() * svd.matrixU() - identity).norm() <= bound);
VERIFY((svd.matrixV().transpose() * svd.matrixV() - identity).norm() <= bound);
JacobiSVD<Mat> reference(bidiagonal);
VERIFY((svd.singularValues() - reference.singularValues()).norm() <= bound * bidiagonal.norm());
BDCSVD<Mat> valuesOnly(diagonal, superdiagonal);
VERIFY_IS_EQUAL(valuesOnly.info(), Success);
VERIFY((valuesOnly.singularValues() - reference.singularValues()).norm() <= bound * bidiagonal.norm());
}
}
}
template <typename Scalar, int StorageOrder>
void bdcsvd_qr_crossover() {
using RealScalar = typename NumTraits<Scalar>::Real;
using Mat = Matrix<Scalar, Dynamic, Dynamic, StorageOrder>;
constexpr int options = ComputeThinU | ComputeThinV;
for (Index n : {16, 31}) {
for (Index rows : {4 * n - 1, 4 * n, 4 * n + 3, 4 * n + 4}) {
Mat tall = Mat::Random(rows, n);
tall.col(1) = tall.col(0);
for (bool transpose : {false, true}) {
Mat a;
if (transpose)
a = tall.adjoint();
else
a = tall;
BDCSVD<Mat, options> svd(a);
BDCSVD<Mat, options | int(DisableQRDecomposition)> direct(a);
VERIFY_IS_EQUAL(svd.info(), Success);
VERIFY_IS_EQUAL(direct.info(), Success);
const RealScalar bound = RealScalar(64 * rows) * NumTraits<RealScalar>::epsilon();
VERIFY((a - svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().adjoint()).norm() <=
bound * a.norm());
const Mat identity = Mat::Identity(n, n);
VERIFY((svd.matrixU().adjoint() * svd.matrixU() - identity).norm() <= bound);
VERIFY((svd.matrixV().adjoint() * svd.matrixV() - identity).norm() <= bound);
VERIFY((svd.singularValues() - direct.singularValues()).norm() <= bound * a.norm());
}
}
}
}
EIGEN_DECLARE_TEST(bdcsvd) {
CALL_SUBTEST_60((svd_normal_equation_roundoff<float, ColMajor>()));
CALL_SUBTEST_60((svd_normal_equation_roundoff<double, RowMajor>()));
CALL_SUBTEST_61((svd_normal_equation_roundoff<std::complex<float>, RowMajor>()));
CALL_SUBTEST_61((svd_normal_equation_roundoff<std::complex<double>, ColMajor>()));
CALL_SUBTEST_2((bdcsvd_asserts<Matrix4d>()));
CALL_SUBTEST_3((bdcsvd_asserts<Matrix<float, 10, 7>>()));
CALL_SUBTEST_4((bdcsvd_asserts<Matrix<float, 7, 10>>()));
CALL_SUBTEST_5((bdcsvd_asserts<Matrix<std::complex<double>, 6, 9>>()));
CALL_SUBTEST_6((bdcsvd_mixed_option_enum_regression<>()));
CALL_SUBTEST_7((bdcsvd_thin_full_options<Matrix2cd>()));
CALL_SUBTEST_9((bdcsvd_thin_full_options<Matrix2d>()));
for (int i = 0; i < g_repeat; i++) {
int r = internal::random<int>(1, EIGEN_TEST_MAX_SIZE / 2), c = internal::random<int>(1, EIGEN_TEST_MAX_SIZE / 2);
TEST_SET_BUT_UNUSED_VARIABLE(r);
TEST_SET_BUT_UNUSED_VARIABLE(c);
CALL_SUBTEST_11((compare_bdc_jacobi<MatrixXf>(MatrixXf(r, c))));
CALL_SUBTEST_12((compare_bdc_jacobi<MatrixXd>(MatrixXd(r, c))));
CALL_SUBTEST_13((compare_bdc_jacobi<MatrixXcd>(MatrixXcd(r, c))));
// Test on inf/nan matrix
CALL_SUBTEST_14((svd_inf_nan<MatrixXf>()));
CALL_SUBTEST_15((svd_inf_nan<MatrixXd>()));
// Verify some computations using all combinations of the Options template parameter.
CALL_SUBTEST_16((bdcsvd_thin_full_options<Matrix3f>()));
CALL_SUBTEST_18((bdcsvd_thin_full_options<Matrix<float, 2, 3>>()));
CALL_SUBTEST_20((bdcsvd_thin_full_options<MatrixXd>(MatrixXd(20, 17))));
CALL_SUBTEST_22((bdcsvd_thin_full_options<MatrixXd>(MatrixXd(17, 20))));
CALL_SUBTEST_24((bdcsvd_thin_full_options<Matrix<double, Dynamic, 15>>(Matrix<double, Dynamic, 15>(r, 15))));
CALL_SUBTEST_26((bdcsvd_thin_full_options<Matrix<double, 13, Dynamic>>(Matrix<double, 13, Dynamic>(13, c))));
CALL_SUBTEST_28((bdcsvd_thin_full_options<MatrixXf>(MatrixXf(r, c))));
CALL_SUBTEST_30((bdcsvd_thin_full_options<MatrixXcd>(MatrixXcd(r, c))));
CALL_SUBTEST_32((bdcsvd_thin_full_options<MatrixXd>(MatrixXd(r, c))));
CALL_SUBTEST_34((bdcsvd_thin_full_options<Matrix<double, Dynamic, Dynamic, RowMajor>>(
Matrix<double, Dynamic, Dynamic, RowMajor>(20, 27))));
CALL_SUBTEST_36((bdcsvd_thin_full_options<Matrix<double, Dynamic, Dynamic, RowMajor>>(
Matrix<double, Dynamic, Dynamic, RowMajor>(27, 20))));
CALL_SUBTEST_38((
svd_check_max_size_matrix<Matrix<float, Dynamic, Dynamic, ColMajor, 20, 35>, ColPivHouseholderQRPreconditioner>(
r, c)));
CALL_SUBTEST_39(
(svd_check_max_size_matrix<Matrix<float, Dynamic, Dynamic, ColMajor, 35, 20>, HouseholderQRPreconditioner>(r,
c)));
CALL_SUBTEST_40((
svd_check_max_size_matrix<Matrix<float, Dynamic, Dynamic, RowMajor, 20, 35>, ColPivHouseholderQRPreconditioner>(
r, c)));
CALL_SUBTEST_41(
(svd_check_max_size_matrix<Matrix<float, Dynamic, Dynamic, RowMajor, 35, 20>, HouseholderQRPreconditioner>(r,
c)));
const Index smallSize = internal::random<Index>(2, 15);
TEST_SET_BUT_UNUSED_VARIABLE(smallSize);
CALL_SUBTEST_62((bdcsvd_precondition_square_matrix_bidiagonal<double>(smallSize)));
CALL_SUBTEST_62((bdcsvd_precondition_square_matrix_bidiagonal<float>(smallSize)));
CALL_SUBTEST_62((svd_precondition_square_matrix_accuracy<Matrix2f>(2)));
CALL_SUBTEST_62((svd_precondition_square_matrix_accuracy<Matrix4d>(4)));
CALL_SUBTEST_62((svd_precondition_square_matrix_accuracy<MatrixXf>(12)));
CALL_SUBTEST_62((svd_precondition_square_matrix_accuracy<MatrixXd>(12)));
CALL_SUBTEST_62((svd_precondition_square_matrix_accuracy<Matrix<double, Dynamic, Dynamic, RowMajor>>(12)));
CALL_SUBTEST_63((svd_precondition_square_matrix_accuracy<Matrix2cd>(2)));
CALL_SUBTEST_63((svd_precondition_square_matrix_accuracy<Matrix4cf>(4)));
CALL_SUBTEST_63((svd_precondition_square_matrix_accuracy<MatrixXcf>(12)));
CALL_SUBTEST_63((svd_precondition_square_matrix_accuracy<MatrixXcd>(12)));
CALL_SUBTEST_64((svd_precondition_square_matrix_runtime_options<MatrixXf>(12)));
CALL_SUBTEST_64((svd_precondition_square_matrix_runtime_options<MatrixXcd>(internal::random<Index>(1, 30))));
CALL_SUBTEST_64((svd_precondition_square_matrix_runtime_options<Matrix2f>(2)));
CALL_SUBTEST_64((bdcsvd_thin_full_options<Matrix<double, Dynamic, Dynamic, RowMajor>>(
Matrix<double, Dynamic, Dynamic, RowMajor>(8, 8))));
// smallSvd keeps its QR preconditioner, so small non-square inputs work with DisableQRDecomposition too.
CALL_SUBTEST_64(
(svd_thin_full_option_checks<MatrixXd, DisableQRDecomposition | PreconditionSquareMatrix>(MatrixXd(10, 5))));
CALL_SUBTEST_64(
(svd_thin_full_option_checks<MatrixXd, DisableQRDecomposition | PreconditionSquareMatrix>(MatrixXd(5, 10))));
}
// test matrixbase method
CALL_SUBTEST_42((bdcsvd_method<Matrix2cd>()));
CALL_SUBTEST_43((bdcsvd_method<Matrix3f>()));
// Check that preallocation avoids subsequent mallocs
// Disabled because not supported by BDCSVD
// CALL_SUBTEST_9( svd_preallocate<void>() );
CALL_SUBTEST_45(svd_underoverflow<void>());
CALL_SUBTEST_45(bdcsvd_power_of_two_scaling<>());
CALL_SUBTEST_45((svd_zero_matrix_solvers<Matrix3d>(3, 3)));
CALL_SUBTEST_45((svd_zero_matrix_solvers<Matrix<float, 2, 5>>(2, 5)));
CALL_SUBTEST_45((svd_zero_matrix_solvers<MatrixXcf>(5, 3)));
// Without total deflation issues.
CALL_SUBTEST_46((compare_bdc_jacobi_instance<>(true)));
CALL_SUBTEST_47((compare_bdc_jacobi_instance<>(false)));
// With total deflation issues before, when it shouldn't be triggered.
CALL_SUBTEST_48((compare_bdc_jacobi_instance<>(true, 3)));
CALL_SUBTEST_49((compare_bdc_jacobi_instance<>(false, 3)));
// Convergence for large constant matrix (https://gitlab.com/libeigen/eigen/-/issues/2491)
CALL_SUBTEST_50(bdcsvd_check_convergence<MatrixXf>(MatrixXf::Constant(500, 500, 1)));
// Bidiagonal SVD hard test cases
CALL_SUBTEST_51((bdcsvd_bidiagonal_hard_cases<float>()));
CALL_SUBTEST_52((bdcsvd_bidiagonal_hard_cases<double>()));
CALL_SUBTEST_53((bdcsvd_extreme_scale_regressions<>()));
CALL_SUBTEST_54((bdcsvd_fast_math_regression_1588<>()));
CALL_SUBTEST_55((bdcsvd_secular_extrapolation<float>()));
CALL_SUBTEST_56((bdcsvd_secular_extrapolation<double>()));
CALL_SUBTEST_57((bdcsvd_qr_crossover<float, ColMajor>()));
CALL_SUBTEST_58((bdcsvd_qr_crossover<double, RowMajor>()));
CALL_SUBTEST_59((bdcsvd_qr_crossover<std::complex<double>, ColMajor>()));
CALL_SUBTEST_60((bdcsvd_flushed_subnormal_matrix<MatrixXf>(12, 10)));
CALL_SUBTEST_60((bdcsvd_flushed_subnormal_matrix<MatrixXd>(10, 12)));
CALL_SUBTEST_60((bdcsvd_flushed_subnormal_matrix<MatrixXcd>(10, 10)));
CALL_SUBTEST_60((bdcsvd_flushed_subnormal_bidiagonal<float>(12, 8)));
CALL_SUBTEST_60((bdcsvd_flushed_subnormal_bidiagonal<double>(16, 8)));
CALL_SUBTEST_60((bdcsvd_flushed_subnormal_bidiagonal<double>(6, 8)));
CALL_SUBTEST_65((bdcsvd_switch_size<>()));
}