对于矩阵乘积的Cholesky,Eigen和C ++ 11类型推断失败

时间:2014-11-24 20:07:50

标签: c++ c++11 auto eigen eigen3

我正在尝试使用Eigen和C ++ 11" auto"使用它的转置对矩阵乘积进行cholesky分解。类型。当我尝试

时出现问题
auto c = a * b
auto cTc = c.tranpose() * c;
auto chol = cTc.llt();

我正在使用XCode 6.1,Eigen 3.2.2。我得到的类型错误是here

这个最小的例子显示了我的机器上的问题。将c的类型从auto更改为MatrixXd,以确保其有效。

#include <iostream>
#include <Eigen/Eigen>
using namespace std;
using namespace Eigen;


int main(int argc, const char * argv[]) {
    MatrixXd a = MatrixXd::Random(100, 3);
    MatrixXd b = MatrixXd::Random(3, 100);
    auto c = a * b;
    auto cTc = c.transpose() * c;
    auto chol = cTc.llt();
    return 0;
}

还有一种方法可以在使用自动时完成这项工作吗?

作为一个附带问题,是否存在性能原因,在每个阶段都没有断言矩阵是MatrixXd?使用auto可以让Eigen将答案保持为它所想象的任何奇怪的模板表达式。我不确定是否输入MatrixXd会导致问题。

4 个答案:

答案 0 :(得分:6)

问题是第一次乘法返回Eigen::GeneralProduct而不是MatrixXdauto正在拾取返回类型。您可以隐式地从MatrixXd创建Eigen::GeneralProduct,因此当您明确声明其正确运行的类型时。见http://eigen.tuxfamily.org/dox/classEigen_1_1GeneralProduct.html

编辑:我不是铸造的特征产品或性能特征方面的专家。我刚从错误消息中推测出答案并从在线文档中确认。分析始终是检查代码不同部分性能的最佳选择。

答案 1 :(得分:4)

让我总结一下发生了什么以及为什么这是错误的。首先,让我们使用他们正在采用的类型来实例化auto个关键字:

typedef GeneralProduct<MatrixXd,MatrixXd> Prod;
Prod c = a * b;
GeneralProduct<Transpose<Prod>,Prod> cTc = c.transpose() * c;

请注意,Eigen是一个表达式模板库。这里,GeneralProduct<>是表示产品的抽象类型。不执行任何计算。因此,如果您将cTc复制为MatrixXd为:

MatrixXd d = cTc;

相当于:

MatrixXd d = c.transpose() * c;

然后产品a*b将进行两次!因此,在任何情况下,最好在显式临时内评估a*b,对c^T*c评估相同:

MatrixXd c = a * b;
MatrixXd cTc = c.transpose() * c;

最后一行:

auto chol = cTc.llt();

也是错误的。如果cTc是抽象产品类型,那么它会尝试实例化一个不可能的抽象产品类型的Cholesky分解。现在,如果cTcMatrixXd,那么您的代码应该可以正常工作,但这仍然不是首选方法,因为方法llt()更像是实现单行表达式,如:

VectorXd b = ...;
VectorXd x = cTc.llt().solve(b);

如果你想要一个名为Cholesky的对象,那么请使用它的构造函数:

LLT<MatrixXd> chol(cTc);

甚至:

LLT chol(c.transpose()* c);

除非您必须在其他计算中使用c.transpose() * c,否则它是等效的。

最后,根据ab的大小,最好将cTc计算为:

MatrixXd cTc = b.transpose() * (a.transpose() * a) * b;

将来(即Eigen 3.3),Eigen将能够看到:

auto c = a * b;
MatrixXd cTc = c.transpose() * c;

作为四个矩阵m0.transpose() * m1.transpose() * m2 * m3的乘积,并将括号放在正确的位置。但是,它无法知道m0==m3m1==m2,因此如果首选方法是在临时评估a*b,那么您仍然必须自己完成。

答案 2 :(得分:2)

我不是Eigen的专家,但像这样的库经常从操作中返回代理对象,然后使用隐式转换或构造函数来强制实际工作。 (表达式模板就是一个极端的例子。)这避免了在许多情况下不必要地复制完整的数据矩阵。不幸的是,auto很高兴只创建一个代理类型的对象,通常库的用户永远不会明确声明。由于您需要最终计算出数字,因此从投射到MatrixXd不会产生性能损失。 (Scott Meyers,在“Effective Modern C ++”中,给出了autovector<bool>一起使用的相关示例,其中operator[](size_t i)返回代理。)

答案 3 :(得分:0)

不要将auto与特征表达式一起使用。我碰到了更多&#34;戏剧性的&#34;之前的问题,请参阅

eigen auto type deduction in general product

并且被其中一个特征创造者(Gael)建议不要将auto与特征表达式一起使用。

从表达式到特定类型(如MatrixXd)的强制转换应该非常快,除非您需要延迟评估(因为在进行强制转换时会评估结果)。