2017-06-27 5 views
4

J'écris une petite bibliothèque d'utilitaires d'algèbre linéaire sur Eigen pour mon code-base personnel. Pour essayer de le rendre aussi flexible que possible, j'ai typedefed différents types de matrice Eigen à utiliser comme paramètres. Cependant, un problème que je continue de rencontrer est que lorsque je l'utilise, je ne peux pas passer une matrice de taille fixe (c'est-à-dire à la compilation) comme argument à une fonction qui a une taille dynamique (définie à l'exécution) matrice typedef en tant que paramètre. Je pourrais comprendre l'inverse - ne pas être en mesure de passer une matrice de taille dynamique comme fixe en raison de vérifications à la compilation, mais il semble que cela devrait fonctionner.Passage de matrices Eigen de taille fixe en tant qu'arguments de fonction appelant des matrices de taille dynamique

Un exemple testable est la fonction pdist2 ci-dessous (qui devrait avoir une implémentation native dans l'API Eigen).

#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; 
} 

Ce code ne compile pas:

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); 

La fonction ci-dessus a été testé et unité correctement évalue, mais il ne fonctionnera que si X et Y sont Eigen::MatrixXd types. Il semble que ce soit mon template typedef qui cause le problème, mais c'est juste une matrice de taille dynamique (c'est-à-dire à l'exécution) avec un type de template.

L'erreur se lit comme suit:

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>’ 

Pourquoi ce ne fonctionne pas? Ou peut-être plus précisément, comment puis-je utiliser un typedef de modèle pour m'assurer que mes matrices de taille fixe dérivent de Eigen::MatrixXT<T>?

Remarque: Tout cela utilise Eigen 3.3.3.

+0

Ce n'est pas possible. 'Matrix ' et MatrixXt 'sont des types différents. Cela signifie que vous ne pouvez pas lier une référence à 'MatrixXt ' à une instance de 'Matrix '. Utilisez pass by value ou jetez un oeil à 'Eigen :: Ref'. –

+0

@HenriMenke Je ne pense pas que ce soit le problème, il y a des conversions en cours (même si les dims sont incorrects, vous obtiendrez une erreur d'affirmation). Le problème est de déduire le type de template 'T', essayant toujours de comprendre pourquoi. – vsoftco

Répondre

4

Le problème est que Matrix<double, 3, 5> et MatrixXT<double> ne sont pas du même type, par conséquent, la seule façon de passer le premier à pdist2 est de le convertir en un MatrixXT<double>. Cela se fait automatiquement par le compilateur si pdist2 n'étaient pas une fonction de modèle:

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

mais depuis pdist2 est basé sur un modèle et que MatrixXT<double> n'est pas une classe de base de Matrix<double, 3, 5>, en C++ le compilateur ne peut pas automatiquement déduit le paramètre template pour instancier pdist2. La solution pour vous est de généraliser encore plus votre fonction de prendre une MatrixBase<> comme entrées:

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

Puisque X et Y vont être utilisés plusieurs fois, et qu'ils peuvent maintenant être des expressions arbitraires (y compris une matrice coûteuse produit), vous voudrez peut-être laisser Eigen évaluer les arguments si nécessaire, à cette fin, vous pouvez utiliser Ref:

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

de cette façon, X sera évaluée si elle ne peut être représenté comme un pointeur + foulée aux valeurs réelles .

+0

Pourriez-vous expliquer pourquoi appeler la fonction comme 'pdist2 (X, Y)' contourne le problème de déduction de paramètres de modèle? – marcman

+1

Dans ce cas, le compilateur n'a pas besoin de déduire le paramètre du template, 'pdist2 ' se comporte donc exactement comme la fonction non-modélisée de ma réponse, et la conversion implicite de différents types Matrix s'applique. – ggael

+0

Je comprends maintenant, merci! – marcman