Reputation: 921
I am using the Tensor aspect of the Eigen library in C++ and would like to compute the element-wise product of one Eigen Tensor multiplied by the scalar sum of the elements in a second Eigen Tensor. Something like:
#include <Eigen/Dense>
Eigen::Tensor<float,2>u(5,5);
Eigen::Tensor<float,2>v(5,5);
u.setConstant(1.f);
v.setConstant(2.f);
Eigen::Tensor<float,2>ans(5,5);
ans = u*v.sum();
However the * operator is not supported in this way.
The README suggest to use the .constant()
method associated with Tensor objects. And while
ans = u*u.constant(5.);
auto ans2 = u.sum();
compiles and functions properly
ans = u*u.constant(v.sum());
does not.
error: no matching function for call to ‘Eigen::Tensor<float, 2>::constant(const Eigen::TensorReductionOp<Eigen::internal::SumReducer<float>, const Eigen::DimensionList<long int, 2ul>, const Eigen::Tensor<float, 2>, Eigen::MakePointer>)’
ans = u*u.constant(v.sum());
^
From further reading it appears that this is because u.constant()
expects a Scalar value to be passed to it, whereas v.sum()
returns a "non-evaluated expression" (see Tensor Operations and C++ "auto" in README). There is further suggestion that evaluation of v.sum()
can be forced using .eval()
, though this appears to return another "non-evaluated expression" type, albeit with a ForcedEvalOp
tagged on the end.
error: no matching function for call to ‘Eigen::Tensor<float, 2>::constant(const Eigen::TensorForcedEvalOp<const Eigen::TensorReductionOp<Eigen::internal::SumReducer<float>, const Eigen::DimensionList<long int, 2ul>, const Eigen::Tensor<float, 2>, Eigen::MakePointer> >)’
ans = u*u.constant(v.sum().eval());
^
The TODO section of README mentions:
"Representation of scalar values: Scalar values are often represented by tensors of size 1 and rank 1. It would be more logical and user friendly to use tensors of rank 0 instead. For example Tensor::maximum() currently returns a Tensor. Similarly, the inner product of 2 1d tensors (through contractions) returns a 1d tensor. In the future these operations might be updated to return 0d tensors instead."
Which implies that v.sum()
should return a rank 1 Tensor of length 1. But the ()
operator, typically used for indexing seem unable to access its value in a usable form for u.constant()
and:
ans = u*u.constant(v.sum()(0))
also fails to compile.
error: no match for call to ‘(const Eigen::TensorReductionOp<Eigen::internal::SumReducer<float>, const Eigen::DimensionList<long int, 2ul>, const Eigen::Tensor<float, 2>, Eigen::MakePointer>) (int)’
ans = u*u.constant(v.sum()(0));
^
as does
ans = u*u.constant(v.sum().eval()(0))
.
error: no match for call to ‘(const Eigen::TensorForcedEvalOp<const Eigen::TensorReductionOp<Eigen::internal::SumReducer<float>, const Eigen::DimensionList<long int, 2ul>, const Eigen::Tensor<float, 2>, Eigen::MakePointer> >) (int)’
ans = u*u.constant(v.sum().eval()(0));
Upvotes: 4
Views: 1814
Reputation: 59731
According to this exchange, you should be able to force the evaluation by assigning the result of the reduction:
Eigen::Tensor<float, 0> vSum = v.sum();
That should work with your line:
ans = u * u.constant(vSum);
The cause of this is that if you try to call the template method constant
directly with v.sum()
it will try to use the declared returning type as Scalar
, which does not work. Eigen uses a lot of complex, essentially opaque types to minimize unnecessary computation and copying, but also a lot of templating too, so it is not that uncommon having to explicitly force type conversions like in this case.
Upvotes: 5