Tsathoggua
Tsathoggua

Reputation: 819

reduction of eigen based templates

I am trying to do a reduction based on eigen matrix.

#include <iostream>
#include <Eigen/Dense>
#include <type_traits>

template<typename T1, typename T2, int n1, int n2>
auto reduction(Eigen::Matrix<T1, n1, n2> &a1,
               Eigen::Matrix<T2, n1, n2> &a2)
     -> decltype(T1{}*T2{})
{
  using BaseT3 = 
    typename std::remove_cv<typename std::remove_reference<decltype(T1{}*T2{})>::type>::type;

  BaseT3 res = a1(0, 0)*a2(0, 0);

  for (int i=0; i<n1; ++i)
    for (int j=0; j<n2; ++j)
      if (i+j)
        res = res + a1(i, j)*a2(i, j);

  return res;
}

int main()
{
  Eigen::Matrix<double, 3, 3> m;
  Eigen::Matrix<Eigen::Vector3d, 3, 3> n;

  std::cout << reduction(m, n) << std::endl;
}

Basically, Im a trying to get sum_{i, j} a1[i, j] * a2[i, j] where a1 and a2 are some eigen mathix but I get compilation errors. The error I get is

error: no match for ‘operator=’ (operand types are ‘BaseT3 {aka 
Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>, 
const Eigen::Matrix<double, 3, 1> >}’ 
and 
‘const Eigen::CwiseBinaryOp<Eigen::internal::scalar_sum_op<double>, 
const Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>, 
const Eigen::Matrix<double, 3, 1> >, 
const Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>, 
const Eigen::Matrix<double, 3, 1> > >’)
         res = res + a1(i, j)*a2(i, j);
             ^

If I am not mistaken, for the given main, type BaseT3 should have been Eigen::Vector3d. I also tried to static cast so the operator= should not fail but I then get other errors.

This is c++11, I use Eigen3 and the compiler is g++ 5.4.1.

Upvotes: 2

Views: 482

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275385

Eigen uses expression templates to optimize chains of operations.

So MatrixA*MatrixB is not a Matrix type, but rather an expression that says "when evaluated this will be the product of a MatrixA times a MatrixB".

The result is that A*B+C*D doesn't create (at least as many) temporary matrixes, but instead when stored in an output matrix the results are "lazily" calculated directly into the output matrix.

Now, you are multipying elements. But one of your element types is in turn a matrix. And Eigen does expression template optimization of scalar times vector it turns out.

The type you want is std::decay_t<decltype((T1{}+T2{}).eval())> (well the C++11 verbose version of that).

You could write a fancy SFINAE thing that checks if it can be evaluated, and if so does that. Or you can test for Eigen expression template types.

Upvotes: 1

Joris Timmermans
Joris Timmermans

Reputation: 10978

The decltype of T1 * T2 isn't what you expect here - Eigen heavily uses expression templates. The CWiseUnaryOp and CWiseBinaryOp types in your error are indicative of that. In other words, the result of "double * Vector3d" isn't what you'd expect (it's not a Vector3d, it's a cwisebinaryop).

See also: Writing functions taking Eigen Types.

In this specific case you may find a solution by creating partial specializations for Eigen base types for both the first and second parameters of your template function.

Upvotes: 4

Related Questions