【问题标题】:row times column is not a scalar, with custom scalar type行时间列不是标量,具有自定义标量类型
【发布时间】:2020-11-26 09:30:48
【问题描述】:

我正在尝试将 CppAD 标量类型与 Eigen 一起使用。

以下编译失败:

#include <Eigen/Dense>
#include <cppad/cppad.hpp>
#include <cppad/example/cppad_eigen.hpp>

int main()
{
    using Scalar = CppAD::AD<double>;
    //using Scalar = double;

    Eigen::Matrix<Scalar, 1,4> row;
    Eigen::Matrix<Scalar, 4,1> col;
    Scalar scalar = 5;
    
    Scalar res2 = row * col + scalar; //fails

    return 0;
}

错误基本上是它不能将标量添加到乘法的结果中。但是,乘法的结果本身就是一个标量,所以应该不成问题。事实上,当使用double 作为Scalar 类型时,没有问题。

这是编译器错误:

cppad-eigen-problem.cpp:14:29: error: no match for ‘operator+’ (operand types are ‘const Eigen::Product<Eigen::Matrix<CppAD::AD<double>, 1, 4>, Eigen::Matrix<CppAD::AD<double>, 4, 1, 0, 4, 1>, 0>’ and ‘Scalar’ {aka ‘CppAD::AD<double>’})
   14 |     Scalar res2 = row * col + scalar; //fails
      |                   ~~~~~~~~~ ^ ~~~~~~
      |                       |       |
      |                       |       Scalar {aka CppAD::AD<double>}
      |                       const Eigen::Product<Eigen::Matrix<CppAD::AD<double>, 1, 4>, Eigen::Matrix<CppAD::AD<double>, 4, 1, 0, 4, 1>, 0>

有一个issue on the CppAD project,但我不确定问题出在哪里:

  1. 如果我使用另一个微不足道的自定义标量,我将无法重现错误...

  2. 另一方面,CppAD Eigen traits 的标量类型在我看来还可以。

版本:Eigen 3.3.7,CppAD 最新大师,g++ 9.3.0

有什么线索吗?

谢谢

【问题讨论】:

  • 我同意 NumTraits 似乎满足here 列出的要求。虽然这不能回答您的潜在问题,但请注意避免使用 Product 类型可以解决您的问题:Scalar res2 = row.adjoint().dot(col) + scalar; // works
  • 谢谢。是的,其他解决方法也是可能的,比如使用value(),因为我知道结果是一个标量:(row * col).value() + scalar。但是,这对我来说是不可行的,因为我有很多已经编写好的代码,可以很好地与double 配合使用。

标签: c++ linear-algebra eigen


【解决方案1】:

虽然 (1x1) 矩阵是标量在数学上是正确的,但 C++ 是另一回事:row * coloperator* 的返回类型不是标量,也不是 (1x1) 矩阵而是乘积表达式.此乘积表达式可隐式转换为其操作数的标量类型,在本例中为 CppAD::AD&lt;double&gt;

您看到错误的原因是 CppAD::AD&lt;double&gt; 是一个模板,因此它的运算符是函数模板,例如像

template<typename T>
CppAD::AD<T> operator+(const CppAD::AD<T>& lhs, const CppAD::AD<T>& rhs);

现在的问题是上面的 templated operator+ 不能被调用,因为模板参数中没有隐式转换,而这正是将产品表达式转换为 CppAD::AD&lt;double&gt; 所需要的。

这也是避免产品起作用的原因(例如,通过使用 Eigen 的 .dot 函数)。

不过,您可以通过例如定义一个合适的operator+,类似于(未测试)

template<typename Derived>
CppAD::AD<double>  operator+(const MatrixBase<Derived>& lhs, const CppAD::AD<double> & rhs) {
  return lhs.derived().coeff(0, 0) + rhs;
                     //^^^^^^^^^^^ No need for an implicit conversion
}

或者您可以使用Eigen's plugin mechanism 并将运算符添加到MatrixBase

【讨论】:

  • 谢谢@dtell!我知道结果是一个表达式,但正如你所说,它可以隐式转换为标量。如果我理解正确,通常会发生隐式转换,因为找到了带有两个标量的 operator+,并且 Eigen 尝试将产品表达式转换为操作数之一,即标量。当 operator+ 是模板时,它不被认为是可行的,因此不会触发转换,一切都会失败。这是正确的吗?认为认识到产品可以转换为标量取决于 operator+ 的可用性,这很奇怪
  • 基本上是的。我不是语言律师,但根据经验:你不能在模板参数的推导中使用转换。
  • 另外,您提出的解决方案有一个缺点(我认为),它允许将标量与任何矩阵相加。我们可以添加一些编译时检查 lhs 的大小等于 (1,1) 吗?
  • 是的,当然。那只是一个非常快速的草稿。你应该检查一下。
  • @L.Bruce 如果它解决了您的问题,请考虑接受这个答案。干杯!
猜你喜欢
  • 2017-07-18
  • 1970-01-01
  • 2021-09-04
  • 2019-10-12
  • 1970-01-01
  • 2022-11-04
  • 1970-01-01
  • 2019-01-20
  • 2019-06-24
相关资源
最近更新 更多