I have the following code snippet (I apologize for the slightly larger snippet code, this is the minimum example where I was able to reduce my problem):
#include <Eigen/Dense> #include <complex> #include <iostream> #include <typeinfo> // Dynamic Matrix over Scalar field template <typename Scalar> using DynMat = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>; // Dynamic column vector over Scalar field template <typename Scalar> using DynVect = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>; // Returns the D x D Identity matrix over the field Derived::Scalar // deduced from the expression Eigen::MatrixBase<Derived>& A template<typename Derived> DynMat<typename Derived::Scalar> Id(const Eigen::MatrixBase<Derived>& A, std::size_t D) { DynMat<typename Derived::Scalar> result = DynMat<typename Derived::Scalar>::Identity(D, D); return result; } int main() { //using ScalarField = std::complex<double>; // same issue even if I use complex numbers using ScalarField = double; // we use doubles in this example // A double dynamic matrix (ie MatrixXd) DynMat<ScalarField> Foo; // used to deduce the type in Id<>() // A double dynamic column vector (ie VectorXd) DynVect<ScalarField> v(4); v << 1., 0. , 0. ,0.; // plug in some values into it // Make sure that Id(Foo, 4) correctly deduces the template parameters std::cout << "Id(Foo, 4) is indeed the 4 x 4 identiy matrix over the ScalarField of " << "typeid().name(): " << typeid(ScalarField).name() << std::endl; std::cout << Id(Foo, 4) << std::endl; // Indeed the 4 x 4 complex Identity matrix // Use auto type deduction for GenMatProduct, junk is displayed. Why?! std::cout << std::endl << "Use auto type deduction for GenMatProduct,\ sometimes junk is displayed. Why?!" << std::endl; auto autoresult = Id(Foo, 4) * v; // evaluated result must be identically equal to v for(int i=0; i<10; i++) { std::cout << autoresult.transpose(); // thought 1 0 0 0 is the result, but NO, junk std::cout << " has norm: " << autoresult.norm() << std::endl; // junk } // Use implicit cast to Dynamic Matrix, works fine std::cout << std::endl << "Use implicit cast to Dynamic Matrix, works fine" << std::endl; DynMat<ScalarField> castresult = Id(Foo, 4) * v; // evaluated result must be identically equal to v for(int i=0; i<10; i++) { std::cout << castresult.transpose(); // 1 0 0 0, works ok std::cout << " has norm: " << castresult.norm() << std::endl; // ok } }
The main idea is that the template function Id<>() takes the expression Eigen A as a parameter, together with the size D and creates a unit matrix over the scalar field of the expression A This feature alone works great. However, when I use it in an Eigen product with auto inferred type, for example, in the line auto autoresult = Id(Foo, 4) * v , I would expect to multiply the vector v by the unit of the matrix, so the pure result should be an expression that, when evaluated, should be identically equal to v . But this is not so, see the first for loop, whenever I show the result and calculate its norm, I get garbage most of the time. If, on the other hand, I implicitly throw Eigen product Id(Foo, 4) * v to the dynamic matrix, everything works fine, the result is correctly evaluated.
I use Eigen 3.2.2 on OS X Yosemite and get the same weird behavior with both g ++ 4.9.1 and Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)
QUESTION:
- I donβt understand what happens in the first
for loop, why is it not a product that evaluates when I use std::cout , or even when I use the norm method? Am I missing something? There is no smoothing here, and I am really puzzled by what is happening. I know that Eigen uses lazy evaluation and evaluates expression when but this does not seem to be the case. This problem is extremely important for me, because I have many functions of the same flavor as Id<>() , which, when used in auto disabled expressions, can fail.
The problem arises quite often, but not always. However, if you run the program 3-4 times, you will definitely see it.
The command that I use to compile and run it:
clang++ (g++) -std=c++11 -isystem ./eigen_3.2.2/ testeigen.cpp -otesteigen; ./testeigen
Typical output I got in a real run:
Id(Foo, 4) is indeed the 4 x 4 identiy matrix over the ScalarField of typeid().name(): d 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 Use GenMatProduct, sometimes junk is displayed. Why?! 1 0 0 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf 3.10504e+231 3.10504e+231 3.95253e-323 0 has norm: inf Use implicit cast to Dynamic Matrix, works fine 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1 1 0 0 0 has norm: 1
Even if I use eval() in
std::cout << autoresult.eval().transpose();
I get the same weird behavior.