Skip to content

Commit 0b2536b

Browse files
authored
Implement absolute threshold for early deflation in Schur algorithm (#1565)
* Implement absolute threshold for early deflation in Schur algorithm * Implement absolute threshold for early deflation in Schur algorithm * fix formatting * added comment
1 parent e0623ee commit 0b2536b

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

src/linalg/schur.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,12 +348,18 @@ where
348348
DefaultAllocator: Allocator<DimDiff<D, U1>>,
349349
{
350350
let mut n = end;
351+
// Equivalent to SMLNUM in LAPACK DLAHQR. Since SMLNUM depends on the machine
352+
// precision, we use eps^2 here as best approximation.
353+
// This is justified because the relative threshold use eps * ( t[n,n] + t[m,m] )
354+
let absolute_threshold = eps.clone() * eps.clone();
351355

352356
while n > 0 {
353357
let m = n - 1;
358+
let off_diag_norm = t[(n, m)].clone().norm1();
354359

355-
if t[(n, m)].clone().norm1()
356-
<= eps.clone() * (t[(n, n)].clone().norm1() + t[(m, m)].clone().norm1())
360+
if off_diag_norm <= absolute_threshold
361+
|| off_diag_norm
362+
<= eps.clone() * (t[(n, n)].clone().norm1() + t[(m, m)].clone().norm1())
357363
{
358364
t[(n, m)] = T::zero();
359365
} else {
@@ -372,8 +378,11 @@ where
372378
let m = new_start - 1;
373379

374380
let off_diag = t[(new_start, m)].clone();
381+
let off_diag_norm = off_diag.clone().norm1();
382+
375383
if off_diag.is_zero()
376-
|| off_diag.norm1()
384+
|| off_diag_norm <= absolute_threshold
385+
|| off_diag_norm
377386
<= eps.clone()
378387
* (t[(new_start, new_start)].clone().norm1() + t[(m, m)].clone().norm1())
379388
{

tests/linalg/eigen.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use na::{DMatrix, Matrix3};
1+
use na::{Complex, DMatrix, Matrix3};
22

33
#[cfg(feature = "proptest-support")]
44
mod proptest_tests {
@@ -141,6 +141,35 @@ fn very_small_deviation_from_identity_issue_1368() {
141141
}
142142
}
143143

144+
// Regression test for #1528
145+
#[test]
146+
#[rustfmt::skip]
147+
fn eigenvalues_search_should_not_hang_issue_1528() {
148+
let m = DMatrix::from_row_slice(
149+
4,
150+
4,
151+
&[
152+
0.0_f64, 0.5773502691896257, 0.0, 0.0,
153+
0.5773502691896257, 0.0, 0.5163977794943222, 0.0,
154+
0.0, 0.5163977794943222, 0.0, 0.50709255283711,
155+
0.0, 0.0, 0.50709255283711, 0.0,
156+
],
157+
);
158+
let complex_eigenvals = m.complex_eigenvalues();
159+
160+
assert_relative_eq!(
161+
complex_eigenvals.iter().sum::<Complex<f64>>().re,
162+
m.trace(),
163+
epsilon = 1e-10
164+
);
165+
166+
assert_relative_eq!(
167+
complex_eigenvals.iter().product::<Complex<f64>>().re,
168+
m.determinant(),
169+
epsilon = 1e-10
170+
);
171+
}
172+
144173
// #[cfg(feature = "arbitrary")]
145174
// quickcheck! {
146175
// TODO: full eigendecomposition is not implemented yet because of its complexity when some

0 commit comments

Comments
 (0)