improve accuracy of 3x3 direct eigenvector extraction

This commit is contained in:
Gael Guennebaud 2011-11-23 22:43:40 +01:00
parent be9b87377f
commit 01b4b6e456

View File

@ -570,15 +570,16 @@ template<typename SolverType> struct direct_selfadjoint_eigenvalues<SolverType,3
// map the matrix coefficients to [-1:1] to avoid over- and underflow. // map the matrix coefficients to [-1:1] to avoid over- and underflow.
Scalar scale = mat.cwiseAbs().maxCoeff(); Scalar scale = mat.cwiseAbs().maxCoeff();
scale = (std::max)(scale,Scalar(1));
MatrixType scaledMat = mat / scale; MatrixType scaledMat = mat / scale;
// Compute the eigenvalues // compute the eigenvalues
computeRoots(scaledMat,eivals); computeRoots(scaledMat,eivals);
// compute the eigen vectors // compute the eigen vectors
if(computeEigenvectors) if(computeEigenvectors)
{ {
Scalar safeNorm2 = Eigen::NumTraits<Scalar>::epsilon();
safeNorm2 *= safeNorm2;
if((eivals(2)-eivals(0))<=Eigen::NumTraits<Scalar>::epsilon()) if((eivals(2)-eivals(0))<=Eigen::NumTraits<Scalar>::epsilon())
{ {
eivecs.setIdentity(); eivecs.setIdentity();
@ -588,28 +589,81 @@ template<typename SolverType> struct direct_selfadjoint_eigenvalues<SolverType,3
scaledMat = scaledMat.template selfadjointView<Lower>(); scaledMat = scaledMat.template selfadjointView<Lower>();
MatrixType tmp; MatrixType tmp;
tmp = scaledMat; tmp = scaledMat;
tmp.diagonal().array () -= eivals(2);
VectorType cross01 = tmp.row(0).cross(tmp.row(1)); Scalar d0 = eivals(2) - eivals(1);
VectorType cross02 = tmp.row(0).cross(tmp.row(2)); Scalar d1 = eivals(1) - eivals(0);
Scalar n01 = cross01.squaredNorm(); int k = d0 > d1 ? 2 : 0;
Scalar n02 = cross02.squaredNorm(); d0 = d0 > d1 ? d1 : d0;
if(n01>n02)
eivecs.col(2) = cross01 / sqrt(n01); tmp.diagonal().array () -= eivals(k);
VectorType cross;
Scalar n;
n = (cross = tmp.row(0).cross(tmp.row(1))).squaredNorm();
if(n>safeNorm2)
eivecs.col(k) = cross / sqrt(n);
else else
eivecs.col(2) = cross02 / sqrt(n02); {
n = (cross = tmp.row(0).cross(tmp.row(2))).squaredNorm();
if(n>safeNorm2)
eivecs.col(k) = cross / sqrt(n);
else
{
n = (cross = tmp.row(1).cross(tmp.row(2))).squaredNorm();
if(n>safeNorm2)
eivecs.col(k) = cross / sqrt(n);
else
{
// the input matrix and/or the eigenvaues probably contains some inf/NaN,
// => exit
// scale back to the original size.
eivals *= scale;
solver.m_info = NumericalIssue;
solver.m_isInitialized = true;
solver.m_eigenvectorsOk = computeEigenvectors;
return;
}
}
}
tmp = scaledMat; tmp = scaledMat;
tmp.diagonal().array() -= eivals(1); tmp.diagonal().array() -= eivals(1);
eivecs.col(1) = tmp.row(0).cross(tmp.row(1));
Scalar n1 = eivecs.col(1).norm(); if(d0<=Eigen::NumTraits<Scalar>::epsilon())
if(n1<=Eigen::NumTraits<Scalar>::epsilon()) eivecs.col(1) = eivecs.col(k).unitOrthogonal();
eivecs.col(1) = eivecs.col(2).unitOrthogonal();
else else
eivecs.col(1) /= n1; {
n = (cross = eivecs.col(k).cross(tmp.row(0).normalized())).squaredNorm();
// make sure that eivecs[1] is orthogonal to eivecs[2] if(n>safeNorm2)
eivecs.col(1) = eivecs.col(2).cross(eivecs.col(1).cross(eivecs.col(2))).normalized(); eivecs.col(1) = cross / sqrt(n);
eivecs.col(0) = eivecs.col(2).cross(eivecs.col(1)); else
{
n = (cross = eivecs.col(k).cross(tmp.row(1))).squaredNorm();
if(n>safeNorm2)
eivecs.col(1) = cross / sqrt(n);
else
{
n = (cross = eivecs.col(k).cross(tmp.row(2))).squaredNorm();
if(n>safeNorm2)
eivecs.col(1) = cross / sqrt(n);
else
{
// we should never reach this point,
// if so the last two eigenvalues are likely to ve very closed to each other
eivecs.col(1) = eivecs.col(k).unitOrthogonal();
}
}
}
// make sure that eivecs[1] is orthogonal to eivecs[2]
Scalar d = eivecs.col(1).dot(eivecs.col(k));
eivecs.col(1) = (eivecs.col(1) - d * eivecs.col(k)).normalized();
}
eivecs.col(k==2 ? 0 : 2) = eivecs.col(k).cross(eivecs.col(1)).normalized();
} }
} }
// Rescale back to the original size. // Rescale back to the original size.