Tests: Bound the SVD normal-equation check by the truncated singular values libeigen/eigen!2966 Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
diff --git a/test/svd_common.h b/test/svd_common.h index ab869cf..679af65 100644 --- a/test/svd_common.h +++ b/test/svd_common.h
@@ -80,12 +80,21 @@ // evaluate normal equation which works also for least-squares solutions if (std::is_same<RealScalar, double>::value || svd.rank() == m.diagonal().size()) { - using std::sqrt; // This test is not stable with single precision. // This is likely because squaring m significantly affects the precision. if (std::is_same<RealScalar, float>::value) ++g_test_level; - VERIFY_IS_APPROX(m.adjoint() * (m * x), m.adjoint() * rhs); + // solve() zeroes the singular values below the threshold, so x satisfies the normal equations + // only up to the truncated part, m^H (m x - rhs) = -sum_{i >= rank} sigma_i (u_i^H rhs) v_i, + // whose norm is at most sigma_rank * ||rhs||. + const RealScalar truncated = + svd.rank() < svd.singularValues().size() ? svd.singularValues()(svd.rank()) * rhs_norm : RealScalar(0); + const SolutionType normal_lhs = m.adjoint() * (m * x); + const SolutionType normal_rhs = m.adjoint() * rhs; + const RealScalar normal_error = (normal_lhs - normal_rhs).norm(); + const RealScalar normal_tolerance = + test_precision<RealScalar>() * numext::mini(normal_lhs.norm(), normal_rhs.norm()) + truncated; + VERIFY_LE(normal_error, normal_tolerance); if (std::is_same<RealScalar, float>::value) --g_test_level; }