简体   繁体   English

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

[英]Eigen and C++11 type inference fails for Cholesky of matrix product

I am trying to take the cholesky decomposition of the product of a matrix with its transpose, using Eigen and C++11 "auto" type. 我试图使用Eigen和C ++ 11“auto”类型对其转置矩阵的乘积进行cholesky分解。 The problem comes when I try to do 我试图做的时候出现问题

auto c = a * b
auto cTc = c.tranpose() * c;
auto chol = cTc.llt();

I am using XCode 6.1, Eigen 3.2.2. 我正在使用XCode 6.1,Eigen 3.2.2。 The type error I get is here . 我得到的类型错误在这里

This minimal example shows the problem on my machine. 这个最小的例子显示了我的机器上的问题。 Change the type of c from auto to MatrixXd to see it work. 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;
}

Is there a way to make this work while still using auto? 有没有办法让这个工作仍然使用自动?

As a side question, is there a performance reason to not assert the matrix is a MatrixXd at each stage? 作为一个附带问题,是否有一个性能原因不断MatrixXd矩阵是每个阶段的MatrixXd Using auto would allow Eigen to keep the answer as whatever weird template expression it fancies. 使用auto可以让Eigen将答案保持为它所想象的任何奇怪的模板表达式。 I'm not sure if typing it as MatrixXd would cause problems or not. 我不确定是否输入MatrixXd会导致问题。

The problem is that the first multiplication returns a Eigen::GeneralProduct instead of a MatrixXd and auto is picking up the return type. 问题是第一次乘法返回Eigen::GeneralProduct而不是MatrixXdauto正在拾取返回类型。 You can implicitly create a MatrixXd from a Eigen::GeneralProduct so when you explicitly declare the type it works correctly. 您可以从Eigen::GeneralProduct隐式创建MatrixXd ,因此当您明确声明其类型时,它可以正常工作。 See http://eigen.tuxfamily.org/dox/classEigen_1_1GeneralProduct.html http://eigen.tuxfamily.org/dox/classEigen_1_1GeneralProduct.html

EDIT: I'm not an expert on the Eigen product or performance characteristics of doing the casting. 编辑:我不是铸造的特征产品或性能特征方面的专家。 I just surmised the answer from the error message and confirmed from the online documentation. 我刚从错误消息中推测出答案并从在线文档中确认。 Profiling is always your best bet for checking the performance of different parts of your code. 分析始终是检查代码不同部分性能的最佳选择。

Let me summarize what's is going on and why it's wrong. 让我总结一下发生了什么以及为什么这是错误的。 First of all, let's instantiate the auto keywords with the types they are taking: 首先,让我们使用他们正在采用的类型来实例化auto关键字:

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

Note that Eigen is an expression template library. 请注意,Eigen是一个表达式模板库。 Here, GeneralProduct<> is an abstract type representing the product. 这里, GeneralProduct<>是表示产品的抽象类型。 No computation are performed. 不执行任何计算。 Therefore, if you copy cTc to a MatrixXd as: 因此,如果复制cTcMatrixXd为:

MatrixXd d = cTc;

which is equivalent to: 这相当于:

MatrixXd d = c.transpose() * c;

then the product a*b will be carried out twice! 然后产品a*b将进行两次! So in any case it is much preferable to evaluate a*b within an explicit temporary, and same for c^T*c : 因此,在任何情况下,最好在显式临时内评估a*b ,对c^T*c评估相同:

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

The last line: 最后一行:

auto chol = cTc.llt();

is also rather wrong. 也是错误的。 If cTc is an abstract product type, then it tries to instantiate a Cholesky factorization working on a an abstract product type which is not possible. 如果cTc是抽象产品类型,那么它会尝试实例化一个不可能的抽象产品类型的Cholesky分解。 Now, if cTc is a MatrixXd , then your code should work but this still not the preferred way as the method llt() is rather to implement one-liner expression like: 现在,如果cTcMatrixXd ,那么你的代码应该工作,但是这仍然不是首选的方法方式llt()是相当实行一班轮表达,如:

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

If you want a named Cholesky object, then rather use its constructor: 如果你想要一个名为Cholesky的对象,那么请使用它的构造函数:

LLT<MatrixXd> chol(cTc);

or even: 甚至:

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

which is equivalent unless you have to use c.transpose() * c in other computations. 这是等效的,除非您必须在其他计算中使用c.transpose() * c

Finally, depending of the sizes of a and b , it might be preferable to compute cTc as: 最后,根据ab的大小,最好将cTc计算为:

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

In the future (ie, Eigen 3.3), Eigen will be able to see: 在未来(即Eigen 3.3),Eigen将能够看到:

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

as a product of four matrices m0.transpose() * m1.transpose() * m2 * m3 and put the parenthesis at the right place. 作为四个矩阵m0.transpose() * m1.transpose() * m2 * m3 ,并将括号放在正确的位置。 However, it cannot know that m0==m3 and m1==m2 , and therefore if the preferred way is to evaluate a*b in a temporary, then you will still have to do it yourself. 但是,它不能知道m0==m3m1==m2 ,因此如果首选方法是在临时中评估a*b ,那么你仍然必须自己做。

I'm not an expert at Eigen , but libraries like this often return proxy objects from operations and then use implicit conversion or constructors to force the actual work. 我不是Eigen的专家,但像这样的库经常从操作中返回代理对象,然后使用隐式转换或构造函数来强制实际工作。 (Expression Templates are an extreme example of this.) This avoids unnecessary copying of the full matrix of data in many situations. (表达式模板就是一个极端的例子。)这避免了在许多情况下不必要地复制完整的数据矩阵。 Unfortunately, auto is quite happy to just create an object of the proxy type, which normally users of the library would never explicitly declare. 不幸的是, auto很高兴只创建一个代理类型的对象,通常库的用户永远不会明确声明。 Since you need to ultimately have the numbers calculated, there is not a performance hit per se from casting to a MatrixXd . 由于您需要最终计算出数字,因此从投射到MatrixXd不会产生性能MatrixXd (Scott Meyers, in "Effective Modern C++", gives the related example of using auto with vector<bool> , where operator[](size_t i) returns a proxy.) (Scott Meyers,在“Effective Modern C ++”中,给出了使用autovector<bool>的相关示例,其中operator[](size_t i)返回代理。)

DO NOT use auto with Eigen expressions. 不要使用带有特征表达式的auto I bumped into even more "dramatic" issues with this before, see 我之前碰到了更多“戏剧性”的问题,请看

eigen auto type deduction in general product 一般产品中的特征自动型扣除

and was advised by one of the Eigen creators (Gael) not to use auto with Eigen expressions. 并且被其中一个Eigen创造者(Gael)建议不要使用带有特征表达式的auto

The cast from an expression to a specific type like MatrixXd should be extremely fast, unless you want lazy evaluation (since when doing the cast the result is evaluated). 从表达式到像MatrixXd这样的特定类型的MatrixXd应该非常快,除非你想要延迟评估(因为在进行转换时,评估结果)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM