Fix real schur and polynomial solver.

For certain inputs, the real schur decomposition might get stuck in a cycle.
Exceptional shifts are supposed to knock us out of that - but previously
they were only ever applied at iteration 10 and 30, which doesn't help if
the cycle starts after cycle 30.  Modified to apply a shift every 16 iterations
(for reference, LAPACK seems to do it every 6 iterations).

Also added an assert in polynomial solver to verify that the schur decomposition
was successful.

Fixes #2633.
This commit is contained in:
Antonio Sanchez 2024-02-16 13:11:54 -08:00
parent 287c801780
commit 3ee06ec52f
3 changed files with 39 additions and 27 deletions

View File

@ -435,9 +435,10 @@ inline void RealSchur<MatrixType>::computeShift(Index iu, Index iter, Scalar& ex
shiftInfo.coeffRef(1) = m_matT.coeff(iu-1,iu-1);
shiftInfo.coeffRef(2) = m_matT.coeff(iu,iu-1) * m_matT.coeff(iu-1,iu);
// Alternate exceptional shifting strategy every 16 iterations.
if (iter % 16 == 0) {
// Wilkinson's original ad hoc shift
if (iter == 10)
{
if (iter % 32 != 0) {
exshift += shiftInfo.coeff(0);
for (Index i = 0; i <= iu; ++i)
m_matT.coeffRef(i,i) -= shiftInfo.coeff(0);
@ -445,11 +446,8 @@ inline void RealSchur<MatrixType>::computeShift(Index iu, Index iter, Scalar& ex
shiftInfo.coeffRef(0) = Scalar(0.75) * s;
shiftInfo.coeffRef(1) = Scalar(0.75) * s;
shiftInfo.coeffRef(2) = Scalar(-0.4375) * s * s;
}
} else {
// MATLAB's new ad hoc shift
if (iter == 30)
{
Scalar s = (shiftInfo.coeff(1) - shiftInfo.coeff(0)) / Scalar(2.0);
s = s * s + shiftInfo.coeff(2);
if (s > Scalar(0))
@ -465,6 +463,7 @@ inline void RealSchur<MatrixType>::computeShift(Index iu, Index iter, Scalar& ex
shiftInfo.setConstant(Scalar(0.964));
}
}
}
}
/** \internal Compute index im at which Francis QR step starts and the first Householder vector. */

View File

@ -98,6 +98,16 @@ template<typename MatrixType> void schur(int size = MatrixType::ColsAtCompileTim
}
}
void test_bug2633() {
Eigen::MatrixXd A(4, 4);
A << 0, 0, 0, -2,
1, 0, 0, -0,
0, 1, 0, 2,
0, 0, 2, -0;
RealSchur<Eigen::MatrixXd> schur(A);
VERIFY(schur.info() == Eigen::Success);
}
EIGEN_DECLARE_TEST(schur_real)
{
CALL_SUBTEST_1(( schur<Matrix4f>() ));
@ -107,4 +117,6 @@ EIGEN_DECLARE_TEST(schur_real)
// Test problem size constructors
CALL_SUBTEST_5(RealSchur<MatrixXf>(10));
CALL_SUBTEST_6(( test_bug2633() ));
}

View File

@ -354,6 +354,7 @@ class PolynomialSolver : public PolynomialSolverBase<_Scalar,_Deg>
internal::companion<Scalar,_Deg> companion( poly );
companion.balance();
m_eigenSolver.compute( companion.denseMatrix() );
eigen_assert(m_eigenSolver.info() == Eigen::Success);
m_roots = m_eigenSolver.eigenvalues();
// cleanup noise in imaginary part of real roots:
// if the imaginary part is rather small compared to the real part