2017-06-27 5 views
4

Ich schreibe eine kleine lineare Algebra-Dienstprogramm-Bibliothek über Eigen für meine persönliche Code-Basis. Um es so flexibel wie möglich zu gestalten, habe ich verschiedene Eigen-Matrixtypen zur Verwendung als Parameter angegeben. Ein Problem, auf das ich immer wieder zurückgreife, ist, dass ich keine Matrix fester Größe (dh zur Kompilierungszeit gesetzt) ​​als Argument an eine Funktion übergeben kann, die eine dynamisch große Größe hat (zur Laufzeit gesetzt). matrix typedef als Parameter. Ich könnte die Umkehrung verstehen - nicht in der Lage zu sein, eine Matrix dynamischer Größe aufgrund von Compile-Time-Prüfungen als fixiert zu übergeben, aber es scheint, dass dies funktionieren sollte.Übergeben von festen Eigen-Matrizen als Argumente zum Aufrufen von Matrizen mit dynamischer Größe

Ein testbares Beispiel ist die pdist2-Funktion unten (die wirklich eine native Implementierung in der Eigen-API haben sollte).

#include <Eigen/Core> 

namespace Eigen 
{ 
    template <typename T> 
    using MatrixXT = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>; 
} 

// X is M x N 
// Y is M x K 
// Output is N x K 
template <typename T> 
inline Eigen::MatrixXT<T> pdist2(const Eigen::MatrixXT<T> &X, const Eigen::MatrixXT<T> &Y) 
{ 
    // ASSERT(X.rows() == Y.rows(), "Input observations must have same number of rows (" + 
    // std::to_string(X.rows()) + "!=" + std::to_string(Y.rows()) + ")"); 

    Eigen::MatrixXT<T> dists = X.colwise().squaredNorm().transpose() * Eigen::MatrixXT<T>::Ones(1, Y.cols()) + 
     Eigen::MatrixXT<T>::Ones(X.cols(), 1) * Y.colwise().squaredNorm() - 
     2 * X.transpose() * Y; 

    return dists; 
} 

Dieser Code nicht kompiliert:

Eigen::Matrix<double, 3, 5> X; 
X << 8.147236863931790, 9.133758561390193, 2.784982188670484, 9.648885351992766, 9.571669482429456, 
    9.057919370756192, 6.323592462254095, 5.468815192049838, 1.576130816775483, 4.853756487228412, 
    1.269868162935061, 0.975404049994095, 9.575068354342976, 9.705927817606156, 8.002804688888002; 

Eigen::Matrix<double, 3, 4> Y; 
Y << 1.418863386272153, 7.922073295595544, 0.357116785741896, 6.787351548577734, 
    4.217612826262750, 9.594924263929030, 8.491293058687772, 7.577401305783335, 
    9.157355251890671, 6.557406991565868, 9.339932477575505, 7.431324681249162; 

Eigen::Matrix<double, 5, 4> D = pdist2(X, Y); 

Die obige Funktion korrekt Einheit geprüft und bewertet wurde, aber es wird nur funktionieren, wenn X und YEigen::MatrixXd Typen sind. Es scheint, dass es meine Vorlage typedef sein muss, die das Problem verursacht, aber es ist nur eine dynamisch (d. H. Zur Laufzeit) große Matrix mit einem Templat-Typ.

Der Fehler lautet:

error: no matching function for call to ‘pdist2(Eigen::Matrix<double, 3, 5>&, Eigen::Matrix<double, 3, 4>&)’ 
    Eigen::Matrix<double, 5, 4> D = Util::Math::pdist2(X, Y); 
                  ^
note: candidate: template<class T> Eigen::MatrixXT<T> Util::Math::pdist2(Eigen::MatrixXT<T>&, Eigen::MatrixXT<T>&) 
    inline Eigen::MatrixXT<T> pdist2(const Eigen::MatrixXT<T> &X, const Eigen::MatrixXT<T> &Y) 
         ^
note: template argument deduction/substitution failed: 
note: template argument ‘3’ does not match ‘#‘integer_cst’ not supported by dump_decl#<declaration error>’ 
    Eigen::Matrix<double, 5, 4> D_est = Util::Math::pdist2(X, Y); 
                  ^
note: ‘Eigen::Matrix<double, 3, 5>’ is not derived from ‘Eigen::MatrixXT<T>’ 

Warum ist das nicht? Oder vielleicht genauer gesagt Wie verwende ich eine Vorlage typedef um sicherzustellen, dass meine feste Größe Matrizen tun von Eigen::MatrixXT<T> abgeleitet werden?

Hinweis: Dies ist alles mit Eigen 3.3.3.

+0

Das ist nicht möglich. 'Matrix ' und 'MatrixXt ' sind unterschiedliche Typen. Das bedeutet, dass Sie einen Verweis auf MatrixXt 'nicht an eine Instanz von' Matrix 'binden können. Verwenden Sie pass-by-value oder werfen Sie einen Blick auf 'Eigen :: Ref'. –

+0

@HenriMenke Ich glaube nicht, dass das das Problem ist, es gibt Konvertierungen (selbst wenn die Dims falsch sind, würde man einen Assert-Fehler bekommen). Das Problem besteht darin, den Schablonentyp "T" abzuleiten und immer noch herauszufinden, warum. – vsoftco

Antwort

4

Das Problem ist, dass Matrix<double, 3, 5> und MatrixXT<double> sind nicht vom gleichen Typ, daher der einzige Weg, um die ersteren zu pdist2 passieren ist es MatrixXT<double> zu einem konvertieren. Dies würde automatisch durch den Compiler erfolgen, wenn pdist2 keine Funktion Vorlage waren:

MatrixXT<double> pdist2(const MatrixXT<double>&,const MatrixXT<double>&); 

aber da pdist2 ist Templat-und dass MatrixXT<double> ist keine Basisklasse von Matrix<double, 3, 5>, in C++ die Compiler nicht erlaubt ist, automatisch hat den Vorlagenparameter abgeleitet, um pdist2 zu instanziieren. Die Lösung ist für Sie noch mehr zu verallgemeinern Ihre Funktion jede MatrixBase<> als Eingänge zu nehmen:

template<typename D1,typename D2> 
Matrix<typename D1::Scalar,D1::ColsAtCompileTime,D2::ColsAtCompileTime> 
pdist2(const MatrixBase<D1>& _X,const MatrixBase<D2>& _Y); 

Da X und Y werden zu mehrfach verwendet werden, und dass sie nun beliebige Ausdrücke (einschließlich einer teuren Matrix Produkt), möchten Sie vielleicht Eigen lassen werten die Argumente, wenn nötig, zu diesem Zweck, können Sie Ref:

Ref<const typename D1::PlainObject> X(_X); 
Ref<const typename D2::PlainObject> Y(_Y); 

auf diese Weise X ausgewertet wird, wenn sie nicht als Zeiger + Lauf Istwerte dargestellt werden können .

+0

Können Sie artikulieren, warum der Aufruf der Funktion als 'pdist2 (X, Y)' das Template-Parameter-Deduktionsproblem umgeht? – marcman

+1

In diesem Fall muss der Compiler keinen Template-Parameter für Sie ableiten, 'pdist2 ' verhält sich also genauso wie die nicht-templated-Funktion meiner Antwort und es gilt die implizite Konvertierung von verschiedenen Matrix-Typen. – ggael

+0

Ich verstehe jetzt, danke! – marcman

Verwandte Themen