Построение выражений матрицы времени компиляции - PullRequest
0 голосов
/ 15 марта 2020

Я только начал использовать 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;
}

Существует ли способ построения матриц с размерами, которые зависят от параметров шаблона, без обращения к a для l oop? Ничего против петель, это то, что я использую в то же время. Я просто хотел бы знать, есть ли способ инициализировать матричное выражение, используя циклы шаблона.

Редактировать: * Я обновил пример, потому что функтор градиента должен был возвращать вектор а не скаляр. Однако возникает проблема с примером.

1 Ответ

0 голосов
/ 25 марта 2020

Если я правильно понимаю вашу проблему здесь, и вы можете использовать c ++ 11, возможное решение может быть следующим:

(a) расширить класс Eigen::Matrix, как объяснено здесь чтобы включить конструктор из 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) определить вашу buildGradient функцию, используя variadi c шаблонов :

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

Примечание

Как объяснено в первой ссылке, для создания этого компилятора вам нужно определить переменную препроцессора EIGEN_MATRIX_PLUGIN, указывающую на абсолютный путь к вашему файлу плагина:

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