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));
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With