// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2026 Rasmus Munk Larsen (rmlarsen@gmail.com) // // 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/. #include "main.h" #include template typename MatrixType::RealScalar matrix_l1_norm(const MatrixType& m) { return m.cwiseAbs().colwise().sum().maxCoeff(); } template void rcond_partial_piv_lu() { typedef typename MatrixType::RealScalar RealScalar; Index size = MatrixType::RowsAtCompileTime; if (size == Dynamic) size = internal::random(2, EIGEN_TEST_MAX_SIZE); // Create a random diagonally dominant (thus invertible) matrix. MatrixType m = MatrixType::Random(size, size); m.diagonal().array() += RealScalar(2 * size); PartialPivLU lu(m); MatrixType m_inverse = lu.inverse(); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m)) / matrix_l1_norm(m_inverse); RealScalar rcond_est = lu.rcond(); // Verify the estimate is within a factor of 3 of the truth. VERIFY(rcond_est > rcond / 3 && rcond_est < rcond * 3); } template void rcond_full_piv_lu() { typedef typename MatrixType::RealScalar RealScalar; Index size = MatrixType::RowsAtCompileTime; if (size == Dynamic) size = internal::random(2, EIGEN_TEST_MAX_SIZE); // Create a random diagonally dominant (thus invertible) matrix. MatrixType m = MatrixType::Random(size, size); m.diagonal().array() += RealScalar(2 * size); FullPivLU lu(m); MatrixType m_inverse = lu.inverse(); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m)) / matrix_l1_norm(m_inverse); RealScalar rcond_est = lu.rcond(); VERIFY(rcond_est > rcond / 3 && rcond_est < rcond * 3); } template void rcond_llt() { typedef typename MatrixType::RealScalar RealScalar; Index size = MatrixType::RowsAtCompileTime; if (size == Dynamic) size = internal::random(2, EIGEN_TEST_MAX_SIZE); // Create a random SPD matrix: A^T * A + I. MatrixType a = MatrixType::Random(size, size); MatrixType m = a.adjoint() * a + MatrixType::Identity(size, size); LLT llt(m); VERIFY(llt.info() == Success); MatrixType m_inverse = llt.solve(MatrixType::Identity(size, size)); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m)) / matrix_l1_norm(m_inverse); RealScalar rcond_est = llt.rcond(); VERIFY(rcond_est > rcond / 3 && rcond_est < rcond * 3); } template void rcond_ldlt() { typedef typename MatrixType::RealScalar RealScalar; Index size = MatrixType::RowsAtCompileTime; if (size == Dynamic) size = internal::random(2, EIGEN_TEST_MAX_SIZE); // Create a random SPD matrix: A^T * A + I. MatrixType a = MatrixType::Random(size, size); MatrixType m = a.adjoint() * a + MatrixType::Identity(size, size); LDLT ldlt(m); VERIFY(ldlt.info() == Success); MatrixType m_inverse = ldlt.solve(MatrixType::Identity(size, size)); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m)) / matrix_l1_norm(m_inverse); RealScalar rcond_est = ldlt.rcond(); VERIFY(rcond_est > rcond / 3 && rcond_est < rcond * 3); } template void rcond_singular() { typedef typename MatrixType::Scalar Scalar; Index size = MatrixType::RowsAtCompileTime; if (size == Dynamic) size = internal::random(2, EIGEN_TEST_MAX_SIZE); // Create a rank-deficient matrix: first row is zero. MatrixType m = MatrixType::Random(size, size); m.row(0).setZero(); FullPivLU lu(m); VERIFY_IS_EQUAL(lu.rcond(), Scalar(0)); } template void rcond_identity() { typedef typename MatrixType::RealScalar RealScalar; Index size = MatrixType::RowsAtCompileTime; if (size == Dynamic) size = internal::random(2, EIGEN_TEST_MAX_SIZE); MatrixType m = MatrixType::Identity(size, size); // All decompositions should give rcond ~= 1 for the identity. { PartialPivLU lu(m); VERIFY(lu.rcond() > RealScalar(0.5)); } { FullPivLU lu(m); VERIFY(lu.rcond() > RealScalar(0.5)); } { LLT llt(m); VERIFY(llt.rcond() > RealScalar(0.5)); } { LDLT ldlt(m); VERIFY(ldlt.rcond() > RealScalar(0.5)); } } template void rcond_ill_conditioned() { typedef typename MatrixType::RealScalar RealScalar; Index size = MatrixType::RowsAtCompileTime; if (size == Dynamic) size = internal::random(4, EIGEN_TEST_MAX_SIZE); // Create a diagonal matrix with known large condition number. // Use 1e-3 to stay well within single-precision range. MatrixType m = MatrixType::Zero(size, size); m(0, 0) = RealScalar(1); for (Index i = 1; i < size; ++i) { m(i, i) = RealScalar(1e-3); } // True condition number = 1e3, so rcond = 1e-3. { PartialPivLU lu(m); RealScalar rcond_est = lu.rcond(); VERIFY(rcond_est < RealScalar(1e-1)); VERIFY(rcond_est > RealScalar(1e-5)); } { FullPivLU lu(m); RealScalar rcond_est = lu.rcond(); VERIFY(rcond_est < RealScalar(1e-1)); VERIFY(rcond_est > RealScalar(1e-5)); } } template void rcond_1x1() { typedef typename MatrixType::RealScalar RealScalar; typedef Matrix Mat1; Mat1 m; m(0, 0) = internal::random(RealScalar(1), RealScalar(100)); { PartialPivLU lu(m); VERIFY_IS_APPROX(lu.rcond(), RealScalar(1)); } { FullPivLU lu(m); VERIFY_IS_APPROX(lu.rcond(), RealScalar(1)); } { LLT llt(m); VERIFY_IS_APPROX(llt.rcond(), RealScalar(1)); } { LDLT ldlt(m); VERIFY_IS_APPROX(ldlt.rcond(), RealScalar(1)); } } template void rcond_2x2() { typedef typename MatrixType::RealScalar RealScalar; typedef Matrix Mat2; // Well-conditioned 2x2 matrix. Mat2 m; m << RealScalar(2), RealScalar(1), RealScalar(1), RealScalar(3); { PartialPivLU lu(m); Mat2 m_inverse = lu.inverse(); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m)) / matrix_l1_norm(m_inverse); RealScalar rcond_est = lu.rcond(); VERIFY(rcond_est > rcond / 3 && rcond_est < rcond * 3); } { FullPivLU lu(m); Mat2 m_inverse = lu.inverse(); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m)) / matrix_l1_norm(m_inverse); RealScalar rcond_est = lu.rcond(); VERIFY(rcond_est > rcond / 3 && rcond_est < rcond * 3); } { LLT llt(m); Mat2 m_inverse = llt.solve(Mat2::Identity()); RealScalar rcond = (RealScalar(1) / matrix_l1_norm(m)) / matrix_l1_norm(m_inverse); RealScalar rcond_est = llt.rcond(); VERIFY(rcond_est > rcond / 3 && rcond_est < rcond * 3); } } EIGEN_DECLARE_TEST(condition_estimator) { for (int i = 0; i < g_repeat; i++) { CALL_SUBTEST_1(rcond_partial_piv_lu()); CALL_SUBTEST_1(rcond_full_piv_lu()); CALL_SUBTEST_1(rcond_llt()); CALL_SUBTEST_1(rcond_ldlt()); CALL_SUBTEST_1(rcond_singular()); CALL_SUBTEST_1(rcond_identity()); CALL_SUBTEST_1(rcond_1x1()); CALL_SUBTEST_1(rcond_2x2()); CALL_SUBTEST_2(rcond_partial_piv_lu()); CALL_SUBTEST_2(rcond_full_piv_lu()); CALL_SUBTEST_2(rcond_llt()); CALL_SUBTEST_2(rcond_ldlt()); CALL_SUBTEST_2(rcond_singular()); CALL_SUBTEST_2(rcond_identity()); CALL_SUBTEST_2(rcond_2x2()); CALL_SUBTEST_3(rcond_partial_piv_lu()); CALL_SUBTEST_3(rcond_full_piv_lu()); CALL_SUBTEST_3(rcond_llt()); CALL_SUBTEST_3(rcond_ldlt()); CALL_SUBTEST_3(rcond_singular()); CALL_SUBTEST_3(rcond_identity()); CALL_SUBTEST_3(rcond_ill_conditioned()); CALL_SUBTEST_4(rcond_partial_piv_lu()); CALL_SUBTEST_4(rcond_full_piv_lu()); CALL_SUBTEST_4(rcond_llt()); CALL_SUBTEST_4(rcond_ldlt()); CALL_SUBTEST_4(rcond_singular()); CALL_SUBTEST_4(rcond_identity()); CALL_SUBTEST_4(rcond_ill_conditioned()); CALL_SUBTEST_5(rcond_partial_piv_lu()); CALL_SUBTEST_5(rcond_full_piv_lu()); CALL_SUBTEST_5(rcond_llt()); CALL_SUBTEST_5(rcond_ldlt()); CALL_SUBTEST_5(rcond_singular()); CALL_SUBTEST_5(rcond_identity()); CALL_SUBTEST_6(rcond_partial_piv_lu()); CALL_SUBTEST_6(rcond_full_piv_lu()); CALL_SUBTEST_6(rcond_llt()); CALL_SUBTEST_6(rcond_ldlt()); CALL_SUBTEST_6(rcond_singular()); CALL_SUBTEST_6(rcond_identity()); } }