mirror of
https://gitlab.com/libeigen/eigen.git
synced 2025-08-14 20:56:00 +08:00
Improve numerical robustness of RealSchur: add scaling and compare sub-diag entries to largest diagonal entry instead of the 2 neighbors.
This commit is contained in:
parent
d2b5a19e0f
commit
5b3a6f51d3
@ -236,7 +236,7 @@ template<typename _MatrixType> class RealSchur
|
|||||||
typedef Matrix<Scalar,3,1> Vector3s;
|
typedef Matrix<Scalar,3,1> Vector3s;
|
||||||
|
|
||||||
Scalar computeNormOfT();
|
Scalar computeNormOfT();
|
||||||
Index findSmallSubdiagEntry(Index iu);
|
Index findSmallSubdiagEntry(Index iu, const Scalar& maxDiagEntry);
|
||||||
void splitOffTwoRows(Index iu, bool computeU, const Scalar& exshift);
|
void splitOffTwoRows(Index iu, bool computeU, const Scalar& exshift);
|
||||||
void computeShift(Index iu, Index iter, Scalar& exshift, Vector3s& shiftInfo);
|
void computeShift(Index iu, Index iter, Scalar& exshift, Vector3s& shiftInfo);
|
||||||
void initFrancisQRStep(Index il, Index iu, const Vector3s& shiftInfo, Index& im, Vector3s& firstHouseholderVector);
|
void initFrancisQRStep(Index il, Index iu, const Vector3s& shiftInfo, Index& im, Vector3s& firstHouseholderVector);
|
||||||
@ -253,18 +253,24 @@ RealSchur<MatrixType>& RealSchur<MatrixType>::compute(const EigenBase<InputType>
|
|||||||
if (maxIters == -1)
|
if (maxIters == -1)
|
||||||
maxIters = m_maxIterationsPerRow * matrix.rows();
|
maxIters = m_maxIterationsPerRow * matrix.rows();
|
||||||
|
|
||||||
|
Scalar scale = matrix.derived().cwiseAbs().maxCoeff();
|
||||||
|
|
||||||
// Step 1. Reduce to Hessenberg form
|
// Step 1. Reduce to Hessenberg form
|
||||||
m_hess.compute(matrix.derived());
|
m_hess.compute(matrix.derived()/scale);
|
||||||
|
|
||||||
// Step 2. Reduce to real Schur form
|
// Step 2. Reduce to real Schur form
|
||||||
computeFromHessenberg(m_hess.matrixH(), m_hess.matrixQ(), computeU);
|
computeFromHessenberg(m_hess.matrixH(), m_hess.matrixQ(), computeU);
|
||||||
|
|
||||||
|
m_matT *= scale;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
template<typename MatrixType>
|
template<typename MatrixType>
|
||||||
template<typename HessMatrixType, typename OrthMatrixType>
|
template<typename HessMatrixType, typename OrthMatrixType>
|
||||||
RealSchur<MatrixType>& RealSchur<MatrixType>::computeFromHessenberg(const HessMatrixType& matrixH, const OrthMatrixType& matrixQ, bool computeU)
|
RealSchur<MatrixType>& RealSchur<MatrixType>::computeFromHessenberg(const HessMatrixType& matrixH, const OrthMatrixType& matrixQ, bool computeU)
|
||||||
{
|
{
|
||||||
|
using std::abs;
|
||||||
|
|
||||||
m_matT = matrixH;
|
m_matT = matrixH;
|
||||||
if(computeU)
|
if(computeU)
|
||||||
m_matU = matrixQ;
|
m_matU = matrixQ;
|
||||||
@ -287,14 +293,18 @@ RealSchur<MatrixType>& RealSchur<MatrixType>::computeFromHessenberg(const HessMa
|
|||||||
|
|
||||||
if(norm!=0)
|
if(norm!=0)
|
||||||
{
|
{
|
||||||
|
Scalar maxDiagEntry = m_matT.cwiseAbs().diagonal().maxCoeff();
|
||||||
|
|
||||||
while (iu >= 0)
|
while (iu >= 0)
|
||||||
{
|
{
|
||||||
Index il = findSmallSubdiagEntry(iu);
|
Index il = findSmallSubdiagEntry(iu,maxDiagEntry);
|
||||||
|
|
||||||
// Check for convergence
|
// Check for convergence
|
||||||
if (il == iu) // One root found
|
if (il == iu) // One root found
|
||||||
{
|
{
|
||||||
m_matT.coeffRef(iu,iu) = m_matT.coeff(iu,iu) + exshift;
|
m_matT.coeffRef(iu,iu) = m_matT.coeff(iu,iu) + exshift;
|
||||||
|
// keep track of the largest diagonal coefficient
|
||||||
|
maxDiagEntry = numext::maxi(maxDiagEntry,abs(m_matT.coeffRef(iu,iu)));
|
||||||
if (iu > 0)
|
if (iu > 0)
|
||||||
m_matT.coeffRef(iu, iu-1) = Scalar(0);
|
m_matT.coeffRef(iu, iu-1) = Scalar(0);
|
||||||
iu--;
|
iu--;
|
||||||
@ -303,6 +313,8 @@ RealSchur<MatrixType>& RealSchur<MatrixType>::computeFromHessenberg(const HessMa
|
|||||||
else if (il == iu-1) // Two roots found
|
else if (il == iu-1) // Two roots found
|
||||||
{
|
{
|
||||||
splitOffTwoRows(iu, computeU, exshift);
|
splitOffTwoRows(iu, computeU, exshift);
|
||||||
|
// keep track of the largest diagonal coefficient
|
||||||
|
maxDiagEntry = numext::maxi(maxDiagEntry,numext::maxi(abs(m_matT.coeff(iu,iu)), abs(m_matT.coeff(iu-1,iu-1))));
|
||||||
iu -= 2;
|
iu -= 2;
|
||||||
iter = 0;
|
iter = 0;
|
||||||
}
|
}
|
||||||
@ -317,6 +329,8 @@ RealSchur<MatrixType>& RealSchur<MatrixType>::computeFromHessenberg(const HessMa
|
|||||||
Index im;
|
Index im;
|
||||||
initFrancisQRStep(il, iu, shiftInfo, im, firstHouseholderVector);
|
initFrancisQRStep(il, iu, shiftInfo, im, firstHouseholderVector);
|
||||||
performFrancisQRStep(il, im, iu, computeU, firstHouseholderVector, workspace);
|
performFrancisQRStep(il, im, iu, computeU, firstHouseholderVector, workspace);
|
||||||
|
// keep track of the largest diagonal coefficient
|
||||||
|
maxDiagEntry = numext::maxi(maxDiagEntry,m_matT.cwiseAbs().diagonal().segment(im,iu-im).maxCoeff());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,14 +360,13 @@ inline typename MatrixType::Scalar RealSchur<MatrixType>::computeNormOfT()
|
|||||||
|
|
||||||
/** \internal Look for single small sub-diagonal element and returns its index */
|
/** \internal Look for single small sub-diagonal element and returns its index */
|
||||||
template<typename MatrixType>
|
template<typename MatrixType>
|
||||||
inline Index RealSchur<MatrixType>::findSmallSubdiagEntry(Index iu)
|
inline Index RealSchur<MatrixType>::findSmallSubdiagEntry(Index iu, const Scalar& maxDiagEntry)
|
||||||
{
|
{
|
||||||
using std::abs;
|
using std::abs;
|
||||||
Index res = iu;
|
Index res = iu;
|
||||||
while (res > 0)
|
while (res > 0)
|
||||||
{
|
{
|
||||||
Scalar s = abs(m_matT.coeff(res-1,res-1)) + abs(m_matT.coeff(res,res));
|
if (abs(m_matT.coeff(res,res-1)) <= NumTraits<Scalar>::epsilon() * maxDiagEntry)
|
||||||
if (abs(m_matT.coeff(res,res-1)) <= NumTraits<Scalar>::epsilon() * s)
|
|
||||||
break;
|
break;
|
||||||
res--;
|
res--;
|
||||||
}
|
}
|
||||||
|
@ -127,16 +127,29 @@ void test_eigensolver_generic()
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// regression test for bug 793
|
|
||||||
#ifdef EIGEN_TEST_PART_2
|
#ifdef EIGEN_TEST_PART_2
|
||||||
{
|
{
|
||||||
|
// regression test for bug 793
|
||||||
MatrixXd a(3,3);
|
MatrixXd a(3,3);
|
||||||
a << 0, 0, 1,
|
a << 0, 0, 1,
|
||||||
1, 1, 1,
|
1, 1, 1,
|
||||||
1, 1e+200, 1;
|
1, 1e+200, 1;
|
||||||
Eigen::EigenSolver<MatrixXd> eig(a);
|
Eigen::EigenSolver<MatrixXd> eig(a);
|
||||||
VERIFY_IS_APPROX(a * eig.pseudoEigenvectors(), eig.pseudoEigenvectors() * eig.pseudoEigenvalueMatrix());
|
double scale = 1e-200; // scale to avoid overflow during the comparisons
|
||||||
VERIFY_IS_APPROX(a * eig.eigenvectors(), eig.eigenvectors() * eig.eigenvalues().asDiagonal());
|
VERIFY_IS_APPROX(a * eig.pseudoEigenvectors()*scale, eig.pseudoEigenvectors() * eig.pseudoEigenvalueMatrix()*scale);
|
||||||
|
VERIFY_IS_APPROX(a * eig.eigenvectors()*scale, eig.eigenvectors() * eig.eigenvalues().asDiagonal()*scale);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// check a case where all eigenvalues are null.
|
||||||
|
MatrixXd a(2,2);
|
||||||
|
a << 1, 1,
|
||||||
|
-1, -1;
|
||||||
|
Eigen::EigenSolver<MatrixXd> eig(a);
|
||||||
|
VERIFY_IS_APPROX(eig.pseudoEigenvectors().squaredNorm(), 2.);
|
||||||
|
VERIFY_IS_APPROX((a * eig.pseudoEigenvectors()).norm()+1., 1.);
|
||||||
|
VERIFY_IS_APPROX((eig.pseudoEigenvectors() * eig.pseudoEigenvalueMatrix()).norm()+1., 1.);
|
||||||
|
VERIFY_IS_APPROX((a * eig.eigenvectors()).norm()+1., 1.);
|
||||||
|
VERIFY_IS_APPROX((eig.eigenvectors() * eig.eigenvalues().asDiagonal()).norm()+1., 1.);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user