前言
由于项目需要,需采用C实现数以百万量级的数据运算,原本已经采用C单文件实现了所有客户算法需求,但性能较低,在优化的过程中找到了该矩阵运算库Eigen3,手记一下。
本文档翻译整理自:《Overview》及《Getting started》
概览Overview
这是Eigen3的API文档。你可以下载它作为tgz档案离线阅读。
你已经是Eigen2用户了?这里有一个从Eigen2到Eigen3指南来帮助移植你的应用程序。
如果你是第一次接触Eigen,你最好先看一下入门页面的文档页Getting started(译者按:即本文的下一部分),它向您展示了如何使用Eigen编写和编译您的第一个程序。
然后,快速参考页面以一种非常简洁的格式为您提供了一个非常完整的API描述,这对于回忆特定功能的语法或快速查看API非常有用。它们目前包括以下两个功能集,将来还会有更多的功能集:
- [快速索引]密集矩阵和数组操作(译者按:为避免翻译有误,附原文
Dense matrix and array manipulations
) - [快速索引]稀疏线性代数(译者按:为避免翻译有误,附原文
Sparse linear algebra
)
你是MatLab用户?还有一个Matlab译文简短ASCII参考。
主文档被组织成涵盖不同功能领域的章节。它们本身由全面描述不同功能的用户手册页面组成,并且参考页提供了相关Eigen 的模块与类的API文档。
在_扩展/自定义Eigen_(Extending/Customizing Eigen)部分,您将找到关于扩展Eigen功能和支持自定义标量类型的讨论和示例。
在_通用规则_(General topics)部分,您将找到关于更多通用规则的文档,如预处理程序指令(preprocessor directives)、控制断言(controlling assertions)、多线程(multi-threading)、MKL支持(MKL support)、一些Eigen的内部见解,以及更多……
最后,不要错过本页面的搜索引擎,这对快速获得给定类或函数的文档很有用。
还想了解更多?请查看(Checkout)不支持的功能模块(unsupported modules)文档。
入门指南Getting started
这是一个关于如何开始使用Eigen的非常简短的指南。它有双重目的:对于那些希望尽快开始编码的人来说,它只是对Eigen库的一个简单介绍;你也可以阅读这一页作为教程的第一部分,它更详细地解释了这个库,在这种情况下,您将继续使用_矩阵类_(The Matrix class)。
如何安装“install” Eigen?
为了使用Eigen,您只需要下载并提取Eigen的源代码(下载说明请参阅wiki)。事实上,Eigen子目录中的头文件是使用Eigen编译程序所需的惟一文件。所有平台的头文件都是相同的,不需要使用CMake
或安装任何东西。
第一个简单的程序
这里有一个非常简单的程序可以帮助您入门。
// test.cpp
#include <iostream>
#include "Eigen/Dense" //原程序是#include <Eigen/Dense>
using Eigen::MatrixXd;
int main() {
MatrixXd m(2, 2);
m(0, 0) = 3;
m(1, 0) = 2.5;
m(0, 1) = -1;
m(1, 1) = m(1, 0) + m(0, 1);
std::cout << m << std::endl;
}
我们将在告诉你如何编译它之后解释程序。
译者按:本人在Ubuntu16.04下的VScode下编程实现,并做了一定改动,文档结构如下,即将压缩包解压后的
Eigen
子目录放在与test.cpp
同一目录下。
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$ tree ./ -L 2
./
├── Eigen
│ ├── Cholesky
│ ├── CholmodSupport
│ ├── CMakeLists.txt
│ ├── Core
│ ├── Dense
│ ├── Eigen
│ ├── Eigenvalues
│ ├── Geometry
│ ├── Householder
│ ├── IterativeLinearSolvers
│ ├── Jacobi
│ ├── LU
│ ├── MetisSupport
│ ├── OrderingMethods
│ ├── PardisoSupport
│ ├── PaStiXSupport
│ ├── QR
│ ├── QtAlignedMalloc
│ ├── Sparse
│ ├── SparseCholesky
│ ├── SparseCore
│ ├── SparseLU
│ ├── SparseQR
│ ├── SPQRSupport
│ ├── src
│ ├── StdDeque
│ ├── StdList
│ ├── StdVector
│ ├── SuperLUSupport
│ ├── SVD
│ └── UmfPackSupport
└── test.cpp
2 directories, 31 files
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$
编译并运行您的第一个程序
没有需要链接到的任何库。在编译上述程序时,您需要记住的唯一一件事是:编译器必须能够找到Eigen头文件,即放置Eigen源代码的目录必须位于包含路径中。在GCC
中,你使用-I
选项来实现这个,所以你可以用这样的命令来编译程序:
g++ -I /path/to/eigen/ my_program.cpp -o my_program
在Linux或Mac OS X上,另一个选项是符号链接或将Eigen文件夹复制到/usr/local/include/
中。这样,你可以用下面的语句编译程序:
g++ my_program.cpp -o my_program
当你运行程序,它产生以下输出:
3 -1
2.5 1.5
译者按:本人在Visual Studio Code(Version: 1.38.1)内编译运行,结果如下:
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$ g++ test.cpp -o test
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$ ./test
3 -1
2.5 1.5
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$
第一个程序的解释
Eigen头文件定义了许多类型,但是对于简单的应用程序来说,只使用MatrixXd
类型就足够了。这表示一个任意大小的矩阵(此处指MatrixXd
中的X
),其中每个条目都是双精度的(此处指MatrixXd
中的d
)。有关可用于表示矩阵的不同类型的概述,请参阅_快速参考指南_(quick reference guide )。
Eigen/density
头文件定义了MatrixXd
类型和相关类型的所有成员函数(参见头文件表)。在这个头文件(和其他_Eigen头文件_[table of header files])中定义的所有类和函数都在Eigen名称空间中。
main
函数的第一行声明了一个类型为MatrixXd
的变量,并指定它是一个2
行2
列的矩阵(条目没有初始化)。语句m(0,0) = 3
将左上角的条目设置为3
。您需要使用圆括号()
来索引矩阵中的元素。通常在计算机科学中,第一个索引的索引是0
,而在数学中,第一个索引是1
。
接下来的三个语句设置了其他三个条目。最后一行将矩阵m
输出到标准输出流。
例2:矩阵和向量
这是另一个例子,它结合了矩阵和向量。现在把注意力集中在左边的程序(在运行时设置大小)上;我们稍后会讨论右边的程序(在编译时设置大小)。
在运行时设置大小:
// test.cpp
#include <iostream>
#include "Eigen/Dense"
using namespace Eigen;
using namespace std;
int main() {
MatrixXd m = MatrixXd::Random(3, 3);
m = (m + MatrixXd::Constant(3, 3, 1.2)) * 50;
cout << "m =" << endl << m << endl;
VectorXd v(3);
v << 1, 2, 3;
cout << "m * v =" << endl << m * v << endl;
}
在编译时设置大小:
// test.cpp
#include <iostream>
#include "Eigen/Dense"
using namespace Eigen;
using namespace std;
int main()
{
Matrix3d m = Matrix3d::Random();
m = (m + Matrix3d::Constant(1.2)) * 50;
cout << "m =" << endl << m << endl;
Vector3d v(1,2,3);
cout << "m * v =" << endl << m * v << endl;
}
输出如下:
在运行时设置大小:
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$ g++ test.cpp -o test
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$ ./test
m =
94.0188 89.844 43.5223
49.4383 101.165 86.823
88.3099 29.7551 37.7775
m * v =
404.274
512.237
261.153
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$
在编译时设置大小:
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$ g++ test.cpp -o test
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$ ./test
m =
94.0188 89.844 43.5223
49.4383 101.165 86.823
88.3099 29.7551 37.7775
m * v =
404.274
512.237
261.153
patten@patten-hp:~/workspace/xj_collide/xj_collide/eigen_solution$
第二个例子的说明
第二个示例首先声明一个3×3
的矩阵m
,它是使用Random()方法初始化的,随机值在-1
和1
之间。下一行应用一个线性映射,其值介于10
和110
之间。函数调用MatrixXd::Constant(3,3,1.2)
(MatrixXd::Constant)返回一个3×3
的矩阵表达式,所有系数都等于1.2
。剩下的都是标准算术。
main
函数的下一行介绍了一个新类型:VectorXd
。这表示任意大小的(列)向量。在这里,向量v
被创建来包含3
个系数,这些系数被单独保留。最后一行使用所谓的逗号初始化器,
,在_高级初始化_(Advanced initialization)中解释过,将向量v
的所有系数设置为:
程序的最后一行将矩阵m
与向量v
相乘并输出结果。
现在回头看看第二个示例程序。我们展示了它的两个版本。在左列的版本(在运行时设置大小)中,矩阵类型为MatrixXd
,表示任意大小的矩阵。右栏中的版本(在编译时设置大小)与此类似,只是矩阵类型为Matrix3d
,它表示固定大小的矩阵(这里是3×3
)。因为类型已经编码了矩阵的大小,所以没有必要在构造函数中指定大小;比较MatrixXd m(3,3)
和Matrix3d m
。同样,我们有VectorXd
在左边(任意大小)和Vector3d
在右边(固定大小)。注意,这里向量v
的系数是直接在构造函数中设置的,不过也可以使用与左例相同的语法。
使用固定大小的矩阵和向量有两个优点。编译器会编译出更好(更快)的代码,因为它知道矩阵和向量的大小。在类型中指定大小还允许在编译时进行更严格的检查。例如,如果您试图将Matrix4d
(一个4×4
矩阵)与Vector3d
(一个大小为3的向量)相乘,编译器将会报错,但是,使用多种类型会增加编译时间和可执行文件的大小。在编译时也可能不知道矩阵的大小。根据经验,对于大小为4×4
或更小的矩阵,应该使用固定大小的矩阵。
下一步该去哪儿?
花点时间阅读_长篇教程_(long tutorial)是值得的。
然而,如果你认为你不需要它,你可以直接使用类文档和我们的_快速参考指南_(Quick reference guide)。
下一篇:The Matrix class(矩阵类)