我正在为Eigen编写一个小的线性代数实用程序库,用于我的个人代码库。为了尽可能灵活,我输入了不同的特征矩阵类型作为参数。但是,我一直遇到的一个问题是,当我使用它时,我无法将固定大小(即在编译时设置)矩阵作为参数传递给具有动态大小的函数(设置为runtime)matrix typedef作为参数。我可以理解反向 - 由于编译时检查而无法传递动态大小的矩阵,但似乎这应该有效。
一个可测试的例子是下面的pdist2
函数(它真的应该在Eigen API中有一个原生实现)。
#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;
}
此代码无法编译:
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);
上述功能已经过单元测试并正确评估,但仅当X
和Y
为Eigen::MatrixXd
类型时才有效。它似乎必须是我的模板typedef导致问题,但它只是一个动态(即在运行时)大小的矩阵与模板类型。
错误如下:
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>’
为什么这不起作用?或者更具体地说,如何使用模板化typedef 来确保我的固定大小的矩阵 派生自Eigen::MatrixXT<T>
?
注意:这都是使用Eigen 3.3.3。
答案 0 :(得分:4)
问题是Matrix<double, 3, 5>
和MatrixXT<double>
的类型不同,因此,将前者传递给pdist2
的唯一方法是将其转换为MatrixXT<double>
。如果pdist2
不是模板函数,则编译器会自动执行此操作:
MatrixXT<double> pdist2(const MatrixXT<double>&,const MatrixXT<double>&);
但由于pdist2模板且 MatrixXT<double>
不是Matrix<double, 3, 5>
的基类,因此在C ++中不允许编译器自动推导出模板参数来实例化pdist2 。您的解决方案是将您的函数概括为更多,以便将MatrixBase<>
作为输入:
template<typename D1,typename D2>
Matrix<typename D1::Scalar,D1::ColsAtCompileTime,D2::ColsAtCompileTime>
pdist2(const MatrixBase<D1>& _X,const MatrixBase<D2>& _Y);
由于X和Y将被多次使用,并且它们现在可以是任意表达式(包括昂贵的矩阵产品),您可能希望让Eigen在需要时评估参数,为此,您可以使用Ref
:
Ref<const typename D1::PlainObject> X(_X);
Ref<const typename D2::PlainObject> Y(_Y);
这样,如果不能将X表示为指针+步长到实际值,则会对X进行求值。