矩阵积的 Cholesky 的特征和 C++11 类型推理失败
Eigen and C++11 type inference fails for Cholesky of matrix product
我正在尝试使用特征和 C++11"auto" 类型对矩阵乘积进行乔莱斯基分解及其转置。当我尝试做时,问题就来了
auto c = a * b
auto cTc = c.tranpose() * c;
auto chol = cTc.llt();
我正在使用XCode 6.1,Eigen 3.2.2。我得到的类型错误在这里。
这个最小的示例显示了我的计算机上的问题。将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是否会导致问题。
问题是第一个乘法返回Eigen::GeneralProduct
而不是MatrixXd
,并且auto
正在选取返回类型。可以从Eigen::GeneralProduct
隐式创建MatrixXd
,以便在显式声明类型时,它可以正常工作。见 http://eigen.tuxfamily.org/dox/classEigen_1_1GeneralProduct.html
编辑:我不是特征产品或铸造性能特征的专家。我只是从错误消息中推测出答案,并从在线文档中确认。分析始终是检查代码不同部分性能的最佳选择。
让我总结一下正在发生的事情以及为什么它是错误的。首先,让我们用它们所采用的类型实例化auto
关键字:
typedef GeneralProduct<MatrixXd,MatrixXd> Prod;
Prod c = a * b;
GeneralProduct<Transpose<Prod>,Prod> cTc = c.transpose() * c;
请注意,特征是一个表达式模板库。在这里,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分解,处理一个抽象的积类型,这是不可能的。现在,如果cTc
是一个MatrixXd
,那么你的代码应该可以工作,但这仍然不是首选的方式,因为llt()
的方法更像是实现单行表达式,如下所示:
VectorXd b = ...;
VectorXd x = cTc.llt().solve(b);
如果你想要一个命名的 Cholesky 对象,那么使用它的构造函数:
LLT<MatrixXd> chol(cTc);
甚至:
LLT chol(c.transpose() * c);
这是等效的,除非您必须在其他计算中使用c.transpose() * c
。
最后,根据a
和b
的大小,最好将cTc
计算为:
MatrixXd cTc = b.transpose() * (a.transpose() * a) * b;
在未来(即特征3.3),本征将能够看到:
auto c = a * b;
MatrixXd cTc = c.transpose() * c;
作为四个矩阵的乘积m0.transpose() * m1.transpose() * m2 * m3
并将括号放在正确的位置。但是,它无法知道 m0==m3
和 m1==m2
,因此,如果首选方法是临时评估a*b
,那么您仍然必须自己做。
我不是Eigen
的专家,但像这样的库经常从操作中返回代理对象,然后使用隐式转换或构造函数来强制实际工作。(表达式模板就是一个极端的例子。在许多情况下,这避免了对完整数据矩阵的不必要复制。不幸的是,auto
很乐意只创建一个代理类型的对象,通常库的用户永远不会显式声明。由于您最终需要计算数字,因此从铸造到MatrixXd
本身不会对性能产生影响。(Scott Meyers在"Effective Modern C++"中给出了将auto
与vector<bool>
一起使用的相关示例,其中operator[](size_t i)
返回一个代理。
将auto
与特征表达式一起使用。我之前遇到了更"戏剧性"的问题,请参阅
一般产品中的特征自动类型扣除
并被其中一位特征创建者(Gael)建议不要在特征表达式中使用auto
。
从表达式到特定类型(如 MatrixXd
)的强制转换应该非常快,除非您想要延迟计算(因为在执行强制转换时会计算结果)。
- C++11 中不同类型的对象的 std::array 的替代方案
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- c++ 11 带有 decltype 的尾随返回类型无法按预期工作
- 在 C++11 函数中使用尾随返回类型的优点
- 使用C++11标准的哪些规则来确定({..})中表达式的类型
- 带有void类型和参数的C++11模板专用化
- 从迭代器转换为变量的类型.(C++11 模板)
- C++11: Atomic::compare_exchange_weak 是否支持非基元类型?
- C++11 与不同类型的随机分布共享相同的函数
- 在 C++11 标准中,为什么要依赖 char 类型实现?
- 使用 (c++11) 声明类型时放置 __declspec(dllimport) 关键字的位置
- C++11中的自动类型扣除和类型转换有什么区别
- C++11 标准中的哪一部分规定了基元数据类型大小之间的相对顺序?
- C++11 按 POD 类型的值传递比常量引用差
- OpenCL 矢量类型:在启用 C++11 的情况下无法访问联合组件 x,y,z
- 使用 C++11 C++不同输入类型的可变参数模板
- 在运行时C++11 个索引模板参数包,以便访问第 N 种类型
- 找出函子的返回类型 C++11Boost
- 具有更高含义的简单类型(C++11)
- 如何从boost::bind的类型中获取参数类型(c++11不可用)