c ++ Eigen3矩阵奇怪的行为

时间:2017-08-22 08:52:33

标签: c++ matrix eigen3

我试图用线性代数c ++库Eigen3得到一个随机对称矩阵。我是这样做的:

Eigen::MatrixXd m(3, 3);
m.setRandom();
m = 0.5 * (m + m.transpose());

但是这种结果是完全错误的。但是,如果我不会重写m变量,只需将其输出到控制台,如下所示:

Eigen::MatrixXd m(3, 3);
m.setRandom();
cout << 0.5 * (m + m.transpose()) << endl;

一切似乎都正常。我无法理解问题出在哪里。是因为转换和像*和+这样的操作之类的方法不会立即创建一个新矩阵,而是以一种懒惰的方式进行并保持对矩阵m的引用?但是,我应该如何从官方文档中了解它呢?并非像这样的行为极其容易出错吗?

更新: 是的,我认为我对懒惰计算的猜测是正确的。在transpose方法的文档化中提到了它:

/** \returns an expression of the transpose of *this.
  *
  * Example: \include MatrixBase_transpose.cpp
  * Output: \verbinclude MatrixBase_transpose.out
  *
  * \warning If you want to replace a matrix by its own transpose, do \b NOT do this:
  * \code
  * m = m.transpose(); // bug!!! caused by aliasing effect
  * \endcode
  * Instead, use the transposeInPlace() method:
  * \code
  * m.transposeInPlace();
  * \endcode
  * which gives Eigen good opportunities for optimization, or alternatively you can also do:
  * \code
  * m = m.transpose().eval();
  * \endcode
  *
  * \sa transposeInPlace(), adjoint() */

所以现在我想知道在执行长链计算时我应该使用哪些模式?在哪里写.eval()?说实话,它很丑陋而且容易出错。

2 个答案:

答案 0 :(得分:3)

“我认为我对懒惰计算的猜测是正确的。”

是的,你是对的。描述了惰性评估的规则here。我已经提取了以下几点:

  

对于每个子表达式,Eigen自动确定是否将其评估为临时变量。 [...]

     

基于表达式模板的库可以避免将子表达式评估为临时表,这在很多情况下会导致大的速度提升。这称为延迟评估,因为表达式会尽可能晚地进行评估,而不是立即进行评估。但是,大多数其他基于表达模板的库总是选择延迟评估。这有两个问题:首先,懒惰评估并不总是性能的好选择;第二,懒惰的评估可能非常危险,例如使用矩阵产品:如果矩阵乘积是惰性评估的话,做matrix = matrix*matrix会产生错误的结果,因为矩阵产品的工作方式。

“所以现在我想知道在执行长链计算时我应该使用哪些模式?”

通常,表达式模板应该解决是否立即/懒惰地评估的问题,但是正如您所指出的,有时他们找不到所有边缘情况,matrix.transpose()似乎是一个。您可以添加.eval().noalias()来强制进行延迟或即时评估,否则将选择其他评估:

  • 强制延迟评估:matrix1.noalias() = matrix2 * matrix2;延迟评估没问题 - 在matrix1和matrix2之间没有别名

  • 强制立即评估:matrix1 = matrix1.transpose().eval()懒惰评估不正常

对于转置案例,只需添加.eval()

m = 0.5 * (m + m.transpose()).eval();

答案 1 :(得分:1)

pingul的答案描述了为什么你的解决方案失败了,我只想添加你可以做的,如果你想避免临时的。基本上,诀窍是将表达式分配给矩阵的一半三角形:

#include <iostream>
#include <Eigen/Core>

int main() {
    Eigen::Matrix4f M;
    M.setRandom();
    std::cout << "M:\n" << M << '\n';
    // for comparison, evaluate into other matrix (similar to using .eval()):
    Eigen::Matrix4f M2 = 0.5f*(M+M.transpose());
    // in place operation, only assing to lower half, then to upper half:
    M.triangularView<Eigen::StrictlyLower>() = 0.5f*(M + M.transpose());
    M.triangularView<Eigen::StrictlyUpper>() = M.transpose();
    std::cout << "M2:\n" << M2 << "\nM:\n" << M << '\n';
}

事实上,如果你只想要一个“随机对称矩阵”而不一定是给定矩阵M的对称部分,你可以简单地将上部复制到下部(或者反之) :

    M.triangularView<Eigen::StrictlyLower>() = M.transpose();