首页 > 解决方案 > 为自动微分 (AD) 类型调整 CwiseUnaryOperation 产生的 Eigen::Matrix

问题描述

此问题与调整 Eigen::Ref 大小的解决方法有关,但是,我没有尝试避免使用模板的限制(实际上,我希望有一个使用模板的解决方案)

我正在使用 eigen 库(版本 3.2.9,但 testet 具有最新版本的 eigen 以及相同的结果)进行一些自动微分(AD)计算,并遇到了这个已知的“错误”开发人员(另请参阅以下错误报告:错误报告 1错误报告 2错误报告 3)。TL;DR:这并不是一个真正的错误,但一个通用且干净的解决方法可能需要一些广泛的工作,并且因为 Eigen 不支持它,所以不追求(我猜......)。对我来说,我只对表达式的一个子集感兴趣,可以让它工作(至少现在是这样),可能有一个可接受的解决方法。

问题如下,考虑这个简化的代码,我们有一个固定和动态矩阵大小的 AD 类型

#include <Eigen/Core>
#include "unsupported/Eigen/src/AutoDiff/AutoDiffScalar.h"

typedef Eigen::AutoDiffScalar<Eigen::Matrix<double, -1, 1>> T_dynamic;
typedef Eigen::AutoDiffScalar<Eigen::Matrix<double, 40, 1>> T_fixed;

int main() {
  const T_fixed   fixed   = 10.0;
  const T_dynamic dynamic = 100.0;

  const auto result = fixed - dynamic;

  return 0;
}

这很好用。当我们点击 时fixed - dynamic,Eigen 将首先调用重载operator-,它同时查看左侧和右侧,并通过该make_coherent_impl()方法相应地调整导数,这是一个模板专用版本make_coherent()

template<typename OtherDerType>
inline const AutoDiffScalar<CwiseBinaryOp<internal::scalar_difference_op<Scalar>, const DerType,const typename internal::remove_all<OtherDerType>::type> >
operator-(const AutoDiffScalar<OtherDerType>& other) const
{
  internal::make_coherent(m_derivatives, other.derivatives());
  return AutoDiffScalar<CwiseBinaryOp<internal::scalar_difference_op<Scalar>, const DerType,const typename internal::remove_all<OtherDerType>::type> >(
  m_value - other.value(),
  m_derivatives - other.derivatives());
}

// resize a to match b is a.size()==0, and conversely.
template<typename A, typename B>
void make_coherent(const A& a, const B&b)
{
  make_coherent_impl<A,B>::run(a.const_cast_derived(), b.const_cast_derived());
}

template<typename A_Scalar, int A_Rows, int A_Cols, int A_Options, int A_MaxRows, int A_MaxCols, typename B>
struct make_coherent_impl<Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols>, B> {
  typedef Matrix<A_Scalar, A_Rows, A_Cols, A_Options, A_MaxRows, A_MaxCols> A;
  static void run(A& a, B& b) {
    if((A_Rows==Dynamic || A_Cols==Dynamic) && (a.size()==0))
    {
      a.resize(b.size());
      a.setZero();
    }
  }
};

这是可行的,因为 typeABare of type (这里省略Eigen::Matrix<...>了版本的其他排列)。make_coherent_impl()

但是,请考虑以下稍作修改的示例:

#include <Eigen/Core>
#include "unsupported/Eigen/src/AutoDiff/AutoDiffScalar.h"

typedef Eigen::AutoDiffScalar<Eigen::Matrix<double, -1, 1>> T_dynamic;
typedef Eigen::AutoDiffScalar<Eigen::Matrix<double, 40, 1>> T_fixed;

int main() {

  const double    scalar  = 1.0;
  const T_fixed   fixed   = 10.0;
  const T_dynamic dynamic = 100.0;

  const auto result = dynamic * scalar - fixed * scalar;

  return 0;
}

现在,当我们点击dynamic * scalar - fixed * scalar并通过该make_coherent()方法时,我们调用

template<typename A, typename B>
struct make_coherent_impl {
  static void run(A&, B&) {}
};

相反, asAB不再是类型Eigen::matrix<...>,而是CwiseUnaryOp<Operation, DerivativeType>.

这种类型最终还是一个矩阵,我想调整它的大小,但是这样做时,eigen 检测到它不是矩阵类型并调用一个无操作调整大小函数(在 DenseBase.h 中),因为它只允许调整矩阵和数组的大小(另见函数描述,这里CwiseUnaryOp是一个表达式)

/** Only plain matrices/arrays, not expressions, may be resized; therefore the only useful resize methods are
* Matrix::resize() and Array::resize(). The present method only asserts that the new size equals the old size, and does
* nothing else.
*/
void resize(Index newSize)
{
  EIGEN_ONLY_USED_FOR_DEBUG(newSize);
  eigen_assert(newSize == this->size() && "DenseBase::resize() does not actually allow to resize.");
}

我用来调用resize函数的部分专用模板版本如下

template<class UnaryOpLhs, class DerTypeLhs, class UnaryOpRhs, class DerTypeRhs>
struct make_coherent_impl<Eigen::CwiseUnaryOp<UnaryOpLhs, DerTypeLhs>, Eigen::CwiseUnaryOp<UnaryOpRhs, DerTypeRhs> >
{
  typedef Eigen::CwiseUnaryOp<UnaryOpLhs, DerTypeLhs> A;
  typedef Eigen::CwiseUnaryOp<UnaryOpRhs, DerTypeRhs> B;

  static void run(A& a, B& b) {
    if(a.size()==0 && b.size()!=0)
      a.resize(b.size());
    else if(b.size()==0 && a.size()!=0)
      b.resize(a.size());
  }
};

有没有办法将它CwiseUnaryOp转换为矩阵,以便我们可以再次调整它的大小,或者在这里实现相同目的的不同路线?我只CwiseUnaryOp在这里展示,但它应该同样适用于CwiseBinaryOp

标签: c++eigenautomatic-differentiation

解决方案


推荐阅读