2017-06-10 10 views
0

Ich lese in den Eigenen Dokumenten (http://eigen.tuxfamily.org/index.php?title=Pit_Falls#Ternary_operator), dass Eigen gut nicht mit ternären Operationen spielt; das ist sicherlich meine Erfahrung.Mischen von ternären Operationen und Eigenen Arrays

Was ich versuche zu tun, ist ein Array basierend auf mehreren booleschen Flags, die use_XXX Flags in meinem Ausschnitt unten zu konstruieren. Ich weiß, dass mindestens eine der Flags von einer Überprüfung vorher wahr ist, aber ich kann diesen Block nicht kompilieren. Hier andere Optionen, die ich versucht habe, die nicht funktionieren:

  1. Konstruieren Sie die 2^4 = 16 logischen Optionen für umat mit so etwas wie Bitmasken - der Code landet ausführlich und schwer zu pflegen; igitt ...

  2. initialisieren umat auf Null, und dann Schleife über die Conditionalen tun inplace Subtraktion - dies ist etwas langsamer als die einzelne Summe ist, wenn ich manuell Bezug auf Kommentar

Eine weitere Idee Ich wollte versuchen, den Ausdruck mit der Flagge zu multiplizieren, in der Hoffnung, dass Eigen seine Schablonenmagie verwenden würde, um herauszufinden, was zu tun ist, aber das hat auch nicht funktioniert, da ich das Array in meinem Fall nicht initialisiere verwende es nicht (sehr leistungskritischer Code in dieser Schleife)

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

Ich habe auch versucht die select Funktion, die funktioniert, aber das ist sehr langsam. Jede der mask_XXX sind Eigen::ArrayXi, und alle anderen sind 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

In der ternären Anweisung '(: )', beide '' und '' hat die gleiche Art haben. Dies ist nicht nur eine Eigenheit von Eigen. Zum Beispiel würde '(was auch immer? 1.0:" abc ")' auch nicht mit dem Fehler 'inkompatible Operandentypen (' double 'und' const char * ') 'funktionieren. –

+1

Leider ist es in Ihrem Fall unmöglich zu sagen, welche Typen die Operanden haben, seit Sie [minimales, vollständiges und überprüfbares Beispiel] (http://stackoverflow.com/help/mcve) nicht gepostet haben. –

+0

Ja, tut mir leid, ich weiß über die Freuden von Nicht-MVE-Beispielen. Am Ende ging ich zur Auswahllösung. Es stellte sich heraus, dass es dank Eigens innerer Magie nicht so viel langsamer war. – ibell

Antwort

0

Sie können den Typ erzwingen (wie in der Verbindung festgestellt, Sie in Ihrer Frage enthalten) zu einem ArrayXd (oder was auch immer andere Objekt, das Sie verwenden) durch Hinzufügen von .eval() zu einer Bedingung. Siehe Beispiel unten:

#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() ist ein CwiseBinaryOp wo ArrayXd::Zero(size) ist ein CwiseNullaryOp, von denen keiner auf den anderen gegossen werden kann. Hinzufügen von .eval() zu einem macht es zu einem ArrayXd (und wird ein temporäres Objekt erstellen, das Sie nicht zu wollen scheinen) und die ternäre Operation arbeiten lassen. Allerdings

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

führt nach wie vor in ArrayXd::Zero(size) zu einer vorübergehenden, wodurch die Leistung bewertet wird. Die Option mit dem wahrscheinlich die beste Leistung würde

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; 

Der größte Nachteil wäre, dass die Auswertung bis zu vier Mal passieren würde, aber ohne die 2^4 Optionen zu Konstruieren Sie erwähnt haben, ich nicht von einem denken kann um das zu vermeiden.

+0

Aber in Ihrem ternären, das ist kein no-op falscher Zustand, richtig? Dies ist wirklich performance-kritischen Code, und ich versuche mächtig zu vermeiden, Zuteilung und Kopien – ibell

+1

Dann entscheiden Sie sich für die alten Mode-if-else-Anweisungen. Du kannst es nicht auf beide Arten haben. –