使用Eigen和为自动微分设计的自定义复数

时间:2018-03-23 18:25:07

标签: c++ eigen

我在Eigen Library中使用混合表达式中的自定义标量类型时遇到问题。我按照指示添加了自定义标量类型  这里https://eigen.tuxfamily.org/dox/TopicCustomizing_CustomScalar.html。尽管遵循这些我无法得到我的代码编译。我假设我没有正确地遵循指示,所以我希望有人可以帮我告诉我我做错了什么。

具体来说,我把我的问题归结为一个非常简单的例子。我有一个名为“cplx”的类型(你可以在这里找到它:http://mdolab.engin.umich.edu/sites/default/files/complexify.h_0.txt),我用它来做复杂的数值微分。我试图在Eigen中使用cplx作为标量类型。我按照上面链接的说明生成了以下NumTraits结构,

template<> struct NumTraits<cplx> : NumTraits<std::complex<double>>
{
    typedef double Real;
    typedef cplx NonInteger;
    typedef cplx Nested;

    enum {
        IsComplex = 1,
        IsInteger = 0,
        IsSigned = 1,
        RequireInitialization = 1,
        ReadCost = 2 * NumTraits<double>::ReadCost,
        AddCost = 2 * NumTraits<double>::AddCost,
        MulCost = 4 * NumTraits<double>::MulCost + 2 * NumTraits<double>::AddCost
    };

};

然后我想构建以下形式的表达式:

Eigen::Matrix<cplx, 3, 10> dc;
Eigen::Matrix<double, 10, 3> dNdx;
Eigen::Matrix<cplx, 3, 3> Fc = dc * dNdx;

当我尝试编译时,我得到以下错误:

Severity    Code    Description Project File    Line    Suppression State
Error   C2672   'Eigen::internal::gebp_traits<cplx,double,false,false>::madd': no matching overloaded function found    Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  999 
Error   C2782   'void Eigen::internal::gebp_traits<cplx,double,false,false>::madd(const LhsPacketType &,const RhsPacketType &,AccPacketType &,AccPacketType &) const': template parameter 'AccPacketType' is ambiguous  Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  999 
Error   C2784   'void Eigen::internal::gebp_traits<cplx,double,false,false>::madd(const LhsPacketType &,const RhsPacketType &,AccPacketType &,AccPacketType &) const': could not deduce template argument for 'AccPacketType &' from 'double'   Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  999 
Error   C2672   'Eigen::internal::gebp_traits<cplx,double,false,false>::madd': no matching overloaded function found    Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  1000    
Error   C2782   'void Eigen::internal::gebp_traits<cplx,double,false,false>::madd(const LhsPacketType &,const RhsPacketType &,AccPacketType &,AccPacketType &) const': template parameter 'AccPacketType' is ambiguous  Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  1000    
Error   C2784   'void Eigen::internal::gebp_traits<cplx,double,false,false>::madd(const LhsPacketType &,const RhsPacketType &,AccPacketType &,AccPacketType &) const': could not deduce template argument for 'AccPacketType &' from 'double'   Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  1000    
Error   C2672   'Eigen::internal::gebp_traits<cplx,double,false,false>::madd': no matching overloaded function found    Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  1001    
Error   C2782   'void Eigen::internal::gebp_traits<cplx,double,false,false>::madd(const LhsPacketType &,const RhsPacketType &,AccPacketType &,AccPacketType &) const': template parameter 'AccPacketType' is ambiguous  Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  1001    
Error   C2784   'void Eigen::internal::gebp_traits<cplx,double,false,false>::madd(const LhsPacketType &,const RhsPacketType &,AccPacketType &,AccPacketType &) const': could not deduce template argument for 'AccPacketType &' from 'double'   Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  1001    
Error   C2782   'void Eigen::internal::gebp_traits<cplx,double,false,false>::madd(const LhsPacketType &,const RhsPacketType &,AccPacketType &,AccPacketType &) const': template parameter 'AccPacketType' is ambiguous  Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  1002    
Error   C2672   'Eigen::internal::gebp_traits<cplx,double,false,false>::madd': no matching overloaded function found    Simulation_MechanicalTest   c:\users\nick.burgess\documents\code\toolsets\eigen\3.3.3\source\eigen\src\core\products\generalblockpanelkernel.h  1002

之前有没有人遇到此错误。我对可能出错的事感到困惑?作为尝试调试的一部分,我当然尝试像

这样的表达式
Eigen::Matrix<cplx, 3, 10> dc;
Eigen::Matrix<cplx, 10, 3> dNdx;
Eigen::Matrix<cplx, 3, 3> Fc = dc * dNdx;

编译得很好。完全没有错误。因此,将double和cplx混合在一起显然是一个问题。任何帮助,将不胜感激。

3 个答案:

答案 0 :(得分:1)

默认情况下,Eigen不允许在表达式中混合使用不同的标量类型(无隐式转换)。

Eigen::Matrix<float, 4,4> Af;
Eigen::Matrix<double,4,4> Ad;
// Af*Ad;  // not allowed
Af.cast<double>()*Ad; // explicitly cast Af to double

要允许混合不同的标量类型,您可以如上所述明确地将一种类型转换为另一种类型,或者专门化以下特征(另外还有Eigen::NumTraits):

namespace Eigen{
  template<typename BinOp>
  struct ScalarBinaryOpTraits<cplx,double,BinOp>
  { typedef cplx ReturnType; };
  template<typename BinOp>
  struct ScalarBinaryOpTraits<double,cplx,BinOp>
  { typedef cplx ReturnType; };
}

这还应允许添加,减去和除以cplxdouble表达式(我没有使用您的代码对此进行测试)。

答案 1 :(得分:0)

我不知道这是否会对你有所帮助;我正在使用visual studio 2017 CE,编译器选项设置为c ++ 17。我不得不禁用sdl检查。我将您的复杂类移植到头文件中,这就是我所看到的:

如果你定义它并尝试编译它可以正常工作:

Eigen::Matrix<cplx, 3, 10> dc;
Eigen::Matrix<double, 10, 3> dNdx;

但是,当您尝试声明您的特征矩阵的第三个实例时

Eigen::Matrix<cplx, 3, 3> Fc = dc * dNdx;

这是编译器错误的来源;尝试扩展表达式,这可能会对您有所帮助。

Eigen::Matrix<cplx, 3, 3> = Eigen::Matrix<cplx, 3, 10> * Eigen::Matrix<double, 10, 3> 

您似乎收到operator=operator*或两者的编译错误,因为它不知道如何解决简单表达式的歧义

cplx = cplx * double.

它不知道如何从double转换为cplx,要么没有定义转换,要么模板实例化不明确。

修改

我对你的情况做了一些研究。在您的标题中,我注释掉了行using namespace std;并在需要时使用了std::解析运算符,认为库命名空间之间可能存在冲突。这最终给了我与编译完全相同的结果。

我的下一步是我使用了所有用户定义的重载构造函数,对于只采用单个参数的任何构造函数,我使用explicit作为前缀。他们现在看起来像这样:

cplx() : std::complex<double>() {};
explicit cplx( const double& d ) : std::complex<double>( d ) {};
cplx( const double& r, const double& i ) : std::complex<double>( r, i ) {};
explicit cplx( const std::complex<double>& z ) : std::complex<double>( z ) {};
explicit cplx( const std::complex<float>& z ) : std::complex<double>( z ) {};

我还为此cpp file添加了.h,这样我就可以单独编译此文件。

你的大多数重载和一些函数现在都给我带来了与MSVC完全相同的编译器错误。

  

错误C2440:'return':无法从'std :: complex'转换为'cplx'

这些运算符和函数是给出此错误的:

inline cplx cplx::operator+() const {
    return +std::complex<double>( *this );
}

inline cplx cplx::operator+( const cplx& z ) const {
    return std::complex<double>( *this ) + std::complex<double>( z );
}

inline cplx cplx::operator+( const double& r ) const {
    return std::complex<double>( *this ) + r;
}

inline cplx cplx::operator+( const int& i ) const {
    return std::complex<double>( *this ) + double( i );
}

inline cplx operator+( const double& r, const cplx& z ) {
    return r + std::complex<double>( z );
}

inline cplx operator+( const int& i, const cplx& z ) {
    return double( i ) + std::complex<double>( z );
}

inline cplx cplx::operator-() const {
    return -std::complex<double>( *this );
}

inline cplx cplx::operator-( const cplx& z ) const {
    return std::complex<double>( *this ) - std::complex<double>( z );
}

inline cplx cplx::operator-( const double& r ) const {
    return std::complex<double>( *this ) - r;
}

inline cplx cplx::operator-( const int& i ) const {
    return std::complex<double>( *this ) - double( i );
}

inline cplx operator-( const double& r, const cplx& z ) {
    return r - std::complex<double>( z );
}

inline cplx operator-( const int& i, const cplx& z ) {
    return double( i ) - std::complex<double>( z );
}

inline cplx cplx::operator*( const cplx& z ) const {
    return std::complex<double>( *this )*std::complex<double>( z );
}

inline cplx cplx::operator*( const double& r ) const {
    return std::complex<double>( *this )*r;
}

inline cplx cplx::operator*( const int& i ) const {
    return std::complex<double>( *this )*double( i );
}

inline cplx operator*( const double& r, const cplx& z ) {
    return r * std::complex<double>( z );
}

inline cplx operator*( const int& i, const cplx& z ) {
    return double( i )*std::complex<double>( z );
}

inline cplx cplx::operator/( const cplx& z ) const {
    return std::complex<double>( *this ) / std::complex<double>( z );
}

inline cplx cplx::operator/( const double& r ) const {
    return std::complex<double>( *this ) / r;
}

inline cplx cplx::operator/( const int& i ) const {
    return std::complex<double>( *this ) / double( i );
}

inline cplx operator/( const double& r, const cplx& z ) {
    return r / std::complex<double>( z );
}

inline cplx operator/( const int& i, const cplx& z ) {
    return double( i ) / std::complex<double>( z );
}

inline cplx sin( const cplx& z ) {
    return sin( std::complex<double>( z ) );
}

inline cplx sinh( const cplx& z ) {
    return sinh( std::complex<double>( z ) );
}

inline cplx cos( const cplx& z ) {
    return cos( std::complex<double>( z ) );
}

inline cplx cosh( const cplx& z ) {
    return cosh( std::complex<double>( z ) );
}

inline cplx tan( const cplx& z ) {
    return tan( std::complex<double>( z ) );
}

inline cplx tanh( const cplx& z ) {
    return tanh( std::complex<double>( z ) );
}

inline cplx log10( const cplx& z ) {
    return log10( std::complex<double>( z ) );
}

inline cplx log( const cplx& z ) {
    return log( std::complex<double>( z ) );
}

inline cplx sqrt( const cplx& z ) {
    return sqrt( std::complex<double>( z ) );
}

inline cplx exp( const cplx& z ) {
    return exp( std::complex<double>( z ) );
}

inline cplx pow( const cplx& a, const cplx& b ) {
    return pow( std::complex<double>( a ), std::complex<double>( b ) );
}

inline cplx pow( const cplx& a, const double& b ) {
    return pow( std::complex<double>( a ), b );
}

inline cplx pow( const cplx& a, const int& b ) {
    return pow( std::complex<double>( a ), double( b ) );
}

inline cplx pow( const double& a, const cplx& b ) {
    return pow( a, std::complex<double>( b ) );
}

inline cplx pow( const int& a, const cplx& b ) {
    return pow( double( a ), std::complex<double>( b ) );
}

此外,我没有为您的班级operator=()

看到作业运算符cplx

根据Eigen在using custom scalar types的网站上的最后一点,我没有看到Eigen::NumTraits<T>的专业化来自他们对第2步的要求:

  

2.添加struct Eigen :: NumTraits的特化(参见NumTraits)

答案 2 :(得分:0)

我一直在研究类似的问题,但是使用双数的类进行微分而不是复杂(这是更好的选择-操作更少,没有epsilon大小调整问题)。

仍然存在一些较小的编译器问题,但是无论如何...以防万一这些将有助于将来有人再次遇到这个问题,以下是在Eigen中使用该类型的文件:

https://github.com/tesch1/CppNumericalSolvers/tree/templated/unsupported/Eigen/src/DualNum