将固定大小的特征矩阵作为参数传递给调用动态大小矩阵的函数

Posted

技术标签:

【中文标题】将固定大小的特征矩阵作为参数传递给调用动态大小矩阵的函数【英文标题】:Passing fixed-size Eigen matrices as arguments to function calling for dynamic-size matrices 【发布时间】:2017-06-27 21:55:29 【问题描述】:

我正在为我的个人代码库在 Eigen 之上编写一个小型线性代数实用程序库。为了使其尽可能灵活,我定义了不同的 Eigen 矩阵类型以用作参数。但是,我一直遇到的一个问题是,当我使用它时,我无法将固定大小(即在编译时设置)的矩阵作为参数传递给具有动态大小(在运行时设置)的函数矩阵 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);

上述函数已经过单元测试并正确评估,但它仅在XYEigen::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&lt;T&gt;?

注意:这都是使用 Eigen 3.3.3。

【问题讨论】:

这是不可能的。 Matrix&lt;double, 5, 4&gt;MatrixXt&lt;double&gt; 是不同的类型。这意味着您不能将对MatrixXt&lt;double&gt; 的引用绑定到Matrix&lt;double, 5, 4&gt; 的实例。使用传值,或者看看Eigen::Ref @HenriMenke 我不认为这是问题所在,正在进行转换(即使暗淡不正确,您也会收到断言错误)。问题在于推断模板类型T,仍在试图找出原因。 【参考方案1】:

问题在于Matrix&lt;double, 3, 5&gt;MatrixXT&lt;double&gt; 不是同一类型,因此,将前者传递给pdist2 的唯一方法是将其转换为MatrixXT&lt;double&gt;。如果pdist2 不是模板函数,这将由编译器自动完成:

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

但由于 pdist2 是模板化的并且MatrixXT&lt;double&gt; 不是Matrix&lt;double, 3, 5&gt; 的基类,因此在C++ 中不允许编译器自动推导出模板参数来实例化pdist2。您的解决方案是进一步概括您的函数以将任何MatrixBase&lt;&gt; 作为输入:

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。

【讨论】:

您能否阐明为什么将函数调用为pdist2&lt;double&gt;(X, Y) 会绕过模板参数推导问题? 在这种情况下,编译器不必为您推导出任何模板参数,pdist2&lt;double&gt; 因此的行为与我的答案的非模板函数完全相同,并且适用于不同 Matrix 类型的隐式转换。 @ggael 你应该启动一个 Kickstarter 来写一本权威的 Eigen 书。我会为此付出很多。

以上是关于将固定大小的特征矩阵作为参数传递给调用动态大小矩阵的函数的主要内容,如果未能解决你的问题,请参考以下文章

PyQt4 将转换矩阵作为参数传递给构造函数

如何循环遍历动态大小的数组并将属性作为参数传递给可变参数函数?

将参数传递给特征工具中的聚合原语

可以作为参数传递给 POST 方法的对象的最大大小

如果已知 void* 分配大小,如何将确切的内存大小传递给 free()

将值列表作为输入参数传递给 PL/SQL 过程