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,34 +435,33 @@ 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);
// Wilkinson's original ad hoc shift
if (iter == 10)
{
exshift += shiftInfo.coeff(0);
for (Index i = 0; i <= iu; ++i)
m_matT.coeffRef(i,i) -= shiftInfo.coeff(0);
Scalar s = abs(m_matT.coeff(iu,iu-1)) + abs(m_matT.coeff(iu-1,iu-2));
shiftInfo.coeffRef(0) = Scalar(0.75) * s;
shiftInfo.coeffRef(1) = Scalar(0.75) * s;
shiftInfo.coeffRef(2) = Scalar(-0.4375) * s * s;
}
// 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))
{
s = sqrt(s);
if (shiftInfo.coeff(1) < shiftInfo.coeff(0))
s = -s;
s = s + (shiftInfo.coeff(1) - shiftInfo.coeff(0)) / Scalar(2.0);
s = shiftInfo.coeff(0) - shiftInfo.coeff(2) / s;
exshift += s;
// Alternate exceptional shifting strategy every 16 iterations.
if (iter % 16 == 0) {
// Wilkinson's original ad hoc shift
if (iter % 32 != 0) {
exshift += shiftInfo.coeff(0);
for (Index i = 0; i <= iu; ++i)
m_matT.coeffRef(i,i) -= s;
shiftInfo.setConstant(Scalar(0.964));
m_matT.coeffRef(i,i) -= shiftInfo.coeff(0);
Scalar s = abs(m_matT.coeff(iu,iu-1)) + abs(m_matT.coeff(iu-1,iu-2));
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
Scalar s = (shiftInfo.coeff(1) - shiftInfo.coeff(0)) / Scalar(2.0);
s = s * s + shiftInfo.coeff(2);
if (s > Scalar(0))
{
s = sqrt(s);
if (shiftInfo.coeff(1) < shiftInfo.coeff(0))
s = -s;
s = s + (shiftInfo.coeff(1) - shiftInfo.coeff(0)) / Scalar(2.0);
s = shiftInfo.coeff(0) - shiftInfo.coeff(2) / s;
exshift += s;
for (Index i = 0; i <= iu; ++i)
m_matT.coeffRef(i,i) -= s;
shiftInfo.setConstant(Scalar(0.964));
}
}
}
}