前言

由于项目需要,需采用C实现数以百万量级的数据运算,原本已经采用C单文件实现了所有客户算法需求,但性能较低,在优化的过程中找到了该矩阵运算库Eigen3,手记一下。

本文档翻译整理自:《Overview》及《Getting started

概览Overview

这是Eigen3的API文档。你可以下载它作为tgz档案离线阅读。

你已经是Eigen2用户了?这里有一个从Eigen2到Eigen3指南来帮助移植你的应用程序。

如果你是第一次接触Eigen,你最好先看一下入门页面的文档页Getting started(译者按:即本文的下一部分),它向您展示了如何使用Eigen编写和编译您的第一个程序。

然后,快速参考页面以一种非常简洁的格式为您提供了一个非常完整的API描述,这对于回忆特定功能的语法或快速查看API非常有用。它们目前包括以下两个功能集,将来还会有更多的功能集:

你是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的变量,并指定它是一个22列的矩阵(条目没有初始化)。语句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()方法初始化的,随机值在-11之间。下一行应用一个线性映射,其值介于10110之间。函数调用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矩阵类