构建编译时矩阵表达式

Building Compile-Time Matrix Expressions

我刚开始使用 Eigen,并从他们的文档中了解到最佳性能来自于他们对矩阵表达式的惰性求值。因此,这样的表达式在求值后非常有效:

Eigen::Matrix<float, 3, 1> a;
a << 0, 1, 2;

Eigen::Matrix<float, 3, 1> b;
b << 3, 4, 5;

Eigen::Matrix<float, 3, 1> c;
c << (a + b).sum(),
     (a - b).sum(),
     a.sum();

std::cout << c << std::endl;

我 运行 在构造列数取决于模板参数的矩阵时遇到问题。例如:

template <std::size_t w>
auto buildGradient() {
  Eigen::Matrix<float, 3, w> matrix;
  matrix << /* ? */;
  return matrix;
}

我的第一个想法是使用递归 C++ 模板来执行此操作。

template <std::size_t w, typename Functor>
auto buildGradientExpr(Functor functor) {
  if constexpr (w == 0) {
    return;
  } else if constexpr (w == 1) {
    return functor();
  } else {
    return functor(), buildGradientExpr<w - 1, Functor>(functor);
  }
}

但是使用它会导致 Eigen 发出运行时错误,因为表达式只有一个初始化器。

template <std::size_t w>
auto buildGradient() {
  Eigen::Matrix<float, 3, w> gradient;
  /* Emits an error about too few coefficients being passed to the initializer. */
  gradient << buildGradientExpr<w>([]() { /* Return 3x1 matrix */ });
  return gradient;
}

这是一个完整的可运行示例。

#include <Eigen/Dense>

#include <iostream>

#include <cstddef>

namespace {

template <std::size_t w, typename Functor>
auto buildGradientExpr(Functor functor) {
  if constexpr (w == 0) {
    return;
  } else if constexpr (w == 1) {
    return functor(w);
  } else {
    return functor(w), buildGradientExpr<w - 1, Functor>(functor);
  }
}

template <std::size_t w, typename Functor>
auto buildGradient(Functor functor) {
  Eigen::Matrix<float, 3, w> gradient;
  gradient << buildGradientExpr<w>(functor);
  return gradient;
}

} // namespace

int main() {

  constexpr std::size_t gradient_width = 10;

  auto gradient_functor = [](std::size_t w) {
    return Eigen::Matrix<float, 3, 1>::Constant(float(w) / gradient_width);
  };

  auto gradient = buildGradient<gradient_width>(gradient_functor);

  std::cout << gradient << std::endl;

  return 0;
}

有没有一种方法可以在不使用 for 循环的情况下构造大小取决于模板参数的矩阵?没有什么反对 for 循环的,这就是我同时使用的。我只是想知道是否有一种方法可以使用模板循环来初始化矩阵表达式。

编辑:* 我更新了这个例子,因为梯度仿函数应该返回一个向量而不是一个标量。但是,示例问题出现了。

如果我正确理解你的问题,并且你可以使用 c++11,可能的解决方案如下:

(a) 按照 here 的解释扩展 Eigen::Matrix class 以包含来自 std::initializer_list<T>:

的构造函数
// File "Eigen_plugin.h"
template<class T>
Matrix(std::initializer_list<T> elems) : Base()
{
    Base::_check_template_params();

    int idx = 0;
    for(auto el : elems)
        coeffRef(idx++) = el;
}

(b) 使用 variadic templates:

定义您的 buildGradient 函数
// File main.cpp
#include <iostream>
#include <eigen3/Eigen/Dense>

template<class... Args>
Eigen::Matrix<double, 3, sizeof...(Args)/3> buildGradient(Args&&... args)
{
    static_assert(sizeof...(Args) % 3 == 0, "Wrong number of elements");
    return Eigen::Matrix<double, 3, sizeof...(Args)/3>{ args... };
}

int main()
{
    std::cout << buildGradient(1.0,2.0,3.0) << std::endl;
    std::cout << buildGradient(1.0,2.0,3.0,4.0,5.0,6.0) << std::endl;
}

备注

如第一个 link 中所述,要进行此编译,您需要定义预处理器变量 EIGEN_MATRIX_PLUGIN 以指向插件文件的绝对路径:

g++ -std=c++11 main.cpp -D EIGEN_MATRIX_PLUGIN=\"path-to-Eigen_plugin.h\"