2017-06-10 2 views
0

J'ai lu dans les documents Eigen (http://eigen.tuxfamily.org/index.php?title=Pit_Falls#Ternary_operator) que Eigen ne joue pas bien avec les opérations ternaires; c'est certainement mon expérience. Ce que j'essaie de faire est de construire un tableau basé sur plusieurs drapeaux booléens, les drapeaux use_XXX dans mon extrait ci-dessous. Je sais qu'au moins un des drapeaux est vrai d'une vérification avant, mais je ne peux pas obtenir ce bloc pour compiler. Voici d'autres options que j'ai essayé qui ne fonctionnent pas:Mélange des opérations ternaires et des rangées Eigen

  1. Construct les 2^4 = 16 possibilités logiques pour umat en utilisant quelque chose comme des masques de bits - le code finit par bavard et difficile à maintenir; beurk ...

  2. Initialiser umat à zéro, puis boucle sur les conditionals, en faisant la soustraction inplace - c'est tout à fait un peu plus lent que la somme unique lorsque je commente manuellement termes

Une autre idée Était d'essayer de multiplier l'expression par le drapeau, dans l'espoir qu'Eigen utiliserait sa magie de modèle pour comprendre quoi faire, mais cela n'a pas fonctionné non plus, puisque dans mon cas je n'initialise pas le tableau si je fais ne l'utilisez pas (code très performant dans cette boucle)

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

J'ai aussi essayé la fonction select qui fonctionne, mais qui est très lent. Chacun des mask_XXX sont Eigen::ArrayXi, et tous les autres sont 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) 
); 
+2

Dans la déclaration ternaire '(: )', 'la fois ' et '' doivent avoir le même type. Ce n'est pas seulement une curiosité d'Eigen. Par exemple '(quoi? 1.0:" abc ")' ne fonctionnerait pas non plus avec l'erreur 'types d'opérandes incompatibles ('double' et 'const char *')'. –

+1

Malheureusement, dans votre cas, il est impossible de dire quels types ont les opérandes puisque vous n'avez pas réussi à publier [Exemple minimal, complet et vérifiable] (http://stackoverflow.com/help/mcve). –

+0

Oui, désolé je connais les joies des exemples non-MVE. À la fin, je suis allé pour la solution choisie. Il s'est avéré ne pas être beaucoup plus lent, grâce à la magie interne d'Eigen. – ibell

Répondre

0

Vous pouvez forcer le type (comme indiqué dans le lien que vous avez inclus dans votre question) à un ArrayXd (ou tout autre objet que vous utilisez) en ajoutant .eval() à une condition. Voir exemple ci-dessous:

#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() est un CwiseBinaryOpArrayXd::Zero(size) est un CwiseNullaryOp, qui ne peuvent être jeté à l'autre. Ajouter .eval() à un en fait un ArrayXd (et créera un objet temporaire, ce que vous semblez ne pas vouloir) et faire fonctionner l'opération ternaire. Cependant,

whatever = 
(true ? (d * aa.square()).eval() : ArrayXd::Zero(size)) + 
(false ? (d * aa.square()).eval() : ArrayXd::Zero(size)); 

entraînera encore ArrayXd::Zero(size) en cours d'évaluation à une performance temporaire, ce qui réduit. L'option avec le probablement la meilleure performance serait

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; 

Le principal inconvénient serait que l'évaluation se passerait-il jusqu'à quatre fois, mais sans construire les 2^4 options que vous mentionnez, je ne peux pas penser à un manière d'éviter cela.

+0

Mais dans votre ternaire, ce n'est pas une fausse condition, non? C'est vraiment du code critique de performance, et j'essaye puissamment d'éviter l'allocation et les copies – ibell

+1

Ensuite, optez pour les anciennes instructions if-else. Vous ne pouvez pas l'avoir dans les deux sens. –