Reputation: 1080
I read in the Eigen docs (http://eigen.tuxfamily.org/index.php?title=Pit_Falls#Ternary_operator) that Eigen doesn't play nicely with ternary operations; that is certainly my experience.
What I am trying to do is to construct an array based on several boolean flags, the use_XXX
flags in my snippet below. I know that at least one of the flags is true from a check before, but I can't get this block to compile. Here are other options that I have tried that don't work:
Construct the 2^4 = 16 logical options for umat
using something like bit masks - the code ends up verbose and hard to maintain; yuck...
Initialize umat
to zero, and then loop over the conditionals, doing inplace subtraction - this is quite a bit slower than the single sum when I manually comment out terms
Another idea was to try to multiply the expression by the flag, in the hopes that Eigen would use its template magic to figure out what to do, but that didn't work either, since in my case I don't initialize the array if I don't use it (very performance critical code in this loop)
umat = (
(use_gauss_delta ? -coeffs.eta*delta_minus_epsilon.square() : 0)
+
(use_delta_ld ? -coeffs.cd*delta_to_ld : 0)
+
(use_gauss_tau ? -coeffs.beta*tau_minus_gamma.square() : 0)
+
(use_tau_lt ? -coeffs.ct*tau_to_lt : 0)
)
);
EDIT
I also tried the select
function, which works, but that is very slow. Each of the mask_XXX are Eigen::ArrayXi
, and all the others are Eigen::ArrayXd
umat = (
mask_gauss_delta.select(-coeffs.eta*delta_minus_epsilon.square(),0)
+
mask_delta_ld.select(-coeffs.cd*delta_to_ld,0)
+
mask_gauss_tau.select(-coeffs.beta*tau_minus_gamma.square(),0)
+
mask_tau_lt.select(-coeffs.ct*tau_to_lt,0)
);
Upvotes: 1
Views: 368
Reputation: 10596
You can force the type (as noted in the link you included in your question) to an ArrayXd
(or whatever other object you're using) by adding .eval()
to one condition. See example below:
#include <Eigen/Core>
#include <iostream>
using Eigen::ArrayXd;
int main(int argc, char** argv)
{
ArrayXd aa, res;
int size = 6;
aa.setLinSpaced(size, 0, 5);
double d = 345.5;
res = (true ? (d * aa.square()).eval() : ArrayXd::Zero(size));
std::cout << res << std::endl;
res = (false ? (d * aa.square()).eval() : ArrayXd::Zero(size));
std::cout << res << std::endl;
return 0;
}
d * aa.square()
is a CwiseBinaryOp
where ArrayXd::Zero(size)
is a CwiseNullaryOp
, neither of which can be cast to the other. Adding .eval()
to one makes it an ArrayXd
(and will create a temporary object, which it you seem not to want) and make the ternary operation work. However,
whatever =
(true ? (d * aa.square()).eval() : ArrayXd::Zero(size)) +
(false ? (d * aa.square()).eval() : ArrayXd::Zero(size));
will still result in ArrayXd::Zero(size)
being evaluated to a temporary, reducing performance. The option with the probably the best performance would be
if(use_gauss_delta) umat += -coeffs.eta*delta_minus_epsilon.square();
if(use_delta_ld) umat += -coeffs.cd*delta_to_ld;
if(use_gauss_tau) umat += -coeffs.beta*tau_minus_gamma.square();
if(use_tau_lt) umat += -coeffs.ct*tau_to_lt;
The main disadvantage would be that the evaluation would happen up to four times, but without constructing the 2^4 options you mentioned, I can't think of a way to avoid that.
Upvotes: 1