此问题与调整 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();
}
}
};
这是可行的,因为 typeA
和B
are 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&) {}
};
相反, asA
和B
不再是类型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