Eigenvalues: Make dense tridiagonal deflation scale invariant

libeigen/eigen!3000

diff --git a/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h b/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h
index 48dce9a..c79acca 100644
--- a/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h
+++ b/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h
@@ -568,13 +568,21 @@
 
   using RealScalar = typename DiagType::RealScalar;
   const RealScalar considerAsZero = (std::numeric_limits<RealScalar>::min)();
-  const RealScalar precision_inv = RealScalar(1) / NumTraits<RealScalar>::epsilon();
+  const RealScalar precision = NumTraits<RealScalar>::epsilon();
+  const RealScalar precision_inv = RealScalar(1) / precision;
 
   // Helper lambda for the deflation test.
   auto deflate = [&](Index lo, Index hi) {
     for (Index i = lo; i < hi; ++i) {
-      if (numext::abs(subdiag[i]) < considerAsZero) {
+      const RealScalar absSubdiag = numext::abs(subdiag[i]);
+      if (absSubdiag < considerAsZero) {
         subdiag[i] = RealScalar(0);
+      } else if (!PerBlockScaling) {
+        // Homogeneous in the global scale: both sides are linear in it, so prescaling cannot change which couplings
+        // are discarded (the previous test was quadratic on the left).
+        if (absSubdiag <= precision * numext::maxi(numext::abs(diag[i]), numext::abs(diag[i + 1]))) {
+          subdiag[i] = RealScalar(0);
+        }
       } else {
         const RealScalar scaled_subdiag = precision_inv * subdiag[i];
         if (scaled_subdiag * scaled_subdiag <= (numext::abs(diag[i]) + numext::abs(diag[i + 1]))) {
diff --git a/test/eigensolver_selfadjoint.cpp b/test/eigensolver_selfadjoint.cpp
index 48c2d71..5f7a32a 100644
--- a/test/eigensolver_selfadjoint.cpp
+++ b/test/eigensolver_selfadjoint.cpp
@@ -486,6 +486,23 @@
   VERIFY_IS_APPROX(eig2.eigenvalues(), eig2v.eigenvalues());
 }
 
+template <typename RealScalar>
+void selfadjointeigensolver_dense_deflation_scale_invariance() {
+  const RealScalar epsilon = NumTraits<RealScalar>::epsilon();
+  const RealScalar scales[] = {RealScalar(1), RealScalar(2)};
+  for (RealScalar scale : scales) {
+    Matrix<RealScalar, 2, 1> diag = Matrix<RealScalar, 2, 1>::Constant(scale);
+    Matrix<RealScalar, Dynamic, 1> subdiag(1);
+    subdiag[0] = RealScalar(1.25) * epsilon * scale;
+    Matrix<std::complex<RealScalar>, Dynamic, Dynamic> eigenvectors =
+        Matrix<std::complex<RealScalar>, Dynamic, Dynamic>::Identity(2, 2);
+
+    const ComputationInfo info = internal::computeFromTridiagonal_impl<false>(diag, subdiag, 30, true, eigenvectors);
+    VERIFY_IS_EQUAL(info, Success);
+    VERIFY(diag[0] < diag[1]);
+  }
+}
+
 // Test computeFromTridiagonal with wide dynamic range across decoupled blocks.
 // This exercises the per-block scaling in computeFromTridiagonal_impl: a zero on the
 // subdiagonal decouples the matrix into blocks with vastly different scales. Global
@@ -967,6 +984,8 @@
   int s = 0;
   CALL_SUBTEST_4(generalizedselfadjointeigensolver_no_malloc<MatrixXd>());
   CALL_SUBTEST_5(generalizedselfadjointeigensolver_no_malloc<MatrixXcd>());
+  CALL_SUBTEST_5(selfadjointeigensolver_dense_deflation_scale_invariance<float>());
+  CALL_SUBTEST_5(selfadjointeigensolver_dense_deflation_scale_invariance<double>());
   CALL_SUBTEST_13(selfadjointeigensolver_subnormal_coefficients());
 
   for (int i = 0; i < g_repeat; i++) {