-
Notifications
You must be signed in to change notification settings - Fork 343
Description
Problem
Lately, I've been working on the Ogden material model which fails the unit tests in Sofa/Component/SolidMechanics/FEM/HyperElastic/tests/Material_test.cpp.
This model uses the SelfAdjointEigenSolver from the Eigen lib to compute eigenpairs of a matrix. The eigenvalues are computed correctly but the eigenvectors it produces are wrong. To progress, I switched to the generic EigenSolver which does the job although it is not the optimal choice for symmetric matrices. I wanted to document this in case someone else comes across it. Maybe it gets fixed with Eigen 5.0 (I am currently using Eigen-3.4.0).
An example
In MaterialTest_cpp, a random tensor is generated, then adjusted to become positive-definite. On my machine, the random matrix is:
For this matrix, the eigenpairs (SelfAdjointEigenSolver fails the check.
SelfAdjointEigenSolver |
EigenSolver |
|---|---|
Code to reproduce this
Eigen::Matrix<double,3,3> CEigen;
CEigen(0,0) = 6.03838452029702;
CEigen(0,1) = -0.500804028823228;
CEigen(1,0) = -0.500804028823228;
CEigen(1,1) = 7.01386290516793;
CEigen(1,2) = 2.22215388836855;
CEigen(2,1) = 2.22215388836855;
CEigen(2,0) = 1.0692913506807;
CEigen(0,2) = 1.0692913506807;
CEigen(2,2) = 1.53182086321567;
std::cout << "C = " << std::endl;
for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) std::cout << std::setprecision(15) << CEigen(i, j) << " "; std::cout << std::endl;}
Eigen::SelfAdjointEigenSolver<Eigen::Matrix<double, 3, 3>> SelfAdjEigenSolver(CEigen, true);
Eigen::EigenSolver<Eigen::Matrix<double, 3, 3> > EigenSolver(CEigen, true);
// Evaluate ||Av - λv|| should be = 0
for (int i = 0; i < 3; i++)
std::cout << "||Cv"<< i << " - EigVal" << i << "v" << i << "|| = "
<< "SelfAdjointEigenSolver: "
<< (CEigen * SelfAdjEigenSolver.eigenvectors().col(i) - SelfAdjEigenSolver.eigenvalues()(i) * SelfAdjEigenSolver.eigenvectors().col(i)).norm()
<< " EigenSolver: "
<< (CEigen * EigenSolver.eigenvectors().real().col(i) - EigenSolver.eigenvalues().real()(i) * EigenSolver.eigenvectors().real().col(i)).norm()
<< std::endl;