跳到主要内容

Eigen 代码结构

目录结构

Eigen 的目录结构分为两大部分 src头文件,如下所示。头文件按照具体功能划分,使用时只需将其包含在内即可,例如想使用特征值计算,那么就直接 #include<Eigen/Eigenvalues> 即可;而 src 目录则是对应的实现。

Eigen$ tree -L 1
├── src
├── Cholesky
├── CholmodSupport
├── Core
├── Dense
├── Eigen
├── Eigenvalues
├── Geometry
├── Householder
├── IterativeLinearSolvers
├── Jacobi
├── KLUSupport
├── LU
├── MetisSupport
├── OrderingMethods
├── PardisoSupport
├── PaStiXSupport
├── QR
├── QtAlignedMalloc
├── Sparse
├── SparseCholesky
├── SparseCore
├── SparseLU
├── SparseQR
├── SPQRSupport
├── StdDeque
├── StdList
├── StdVector
├── SuperLUSupport
├── SVD
└── UmfPackSupport

src 虽然是对应的实现,但是却没有一个 cpp/cc 文件,而是与上一级头文件一一对应的子目录,各子目录是对应的实现(都是 .h 文件)。

$ ls Eigen/src/
Cholesky Jacobi PaStiXSupport SPQRSupport
CholmodSupport KLUSupport plugins StlSupport
Core LU QR SuperLUSupport
Eigenvalues MetisSupport SparseCholesky SVD
Geometry misc SparseCore UmfPackSupport
Householder OrderingMethods SparseLU
IterativeLinearSolvers PardisoSupport SparseQR

模块和头文件

下表列出常用的一些模块和它所对应的头文件,以及该模块包含的功能。

模块头文件内容
Core<Eigen/Core>矩阵和数组(向量)类(Matrix、Array),基于线性代数还有数组操作
Geometry<Eigen/Geometry>变换、平移、缩放、2D 旋转和 3D 旋转(包括四元数和角轴)
LU<Eigen/LU>使用求解器进行求逆,行列式,LU 分解操作
Cholesky<Eigen/Cholesky>使用求解器进行 LLT、LT、Cholesky 分解
Householder<Eigen/Householder>Householder 变换;被用作几个线性代数模块
SVD<Eigen/SVD>SVD 分解与最小二乘求解器
QR<Eigen/QR>QR 分解
Eigenvalues<EIgen/Eigenvalues>特征值,特征向量分解
Sparse<Eigen/Sparse>稀疏矩阵存储以及相关的基本线性代数
Dense<Eigen/Dense>包括 Core、Geometry、LU、Cholesky、SVD、QR、Eigenvalues 的头文件
Eigen<Eigen/Eigen>包括 Dense 和 Sparse 的头文件

那么这么多头文件怎么组织好包含关系呢?Eigen 给出的答案是,在对外头文件中按照依赖关系排列头文件顺序。

Core模块为例,该模块定义了Eigen核心数据结构和基本功能。Eigen/src/Matrix.h定义了矩阵,其定义如下:

template<typename Scalar_, int Rows_, int Cols_, int Options_, int MaxRows_, int MaxCols_>
class Matrix
: public PlainObjectBase<Matrix<Scalar_, Rows_, Cols_, Options_, MaxRows_, MaxCols_> >

但是我们发现,Matrix.h 并没有直接 #include <PlainObjectBase.h>。打开 Eigen/Core,我们就可以发现如下结构

#include "src/Core/DenseCoeffsBase.h"
#include "src/Core/DenseBase.h"
#include "src/Core/MatrixBase.h"
#include "src/Core/EigenBase.h"

#include "src/Core/Product.h"
#include "src/Core/CoreEvaluators.h"
#include "src/Core/AssignEvaluator.h"

#ifndef EIGEN_PARSED_BY_DOXYGEN // work around Doxygen bug triggered by Assign.h r814874
// at least confirmed with Doxygen 1.5.5 and 1.5.6
#include "src/Core/Assign.h"
#endif

#include "src/Core/ArrayBase.h"
#include "src/Core/util/BlasUtil.h"
#include "src/Core/DenseStorage.h"
#include "src/Core/NestByValue.h"

// #include "src/Core/ForceAlignedAccess.h"

#include "src/Core/ReturnByValue.h"
#include "src/Core/NoAlias.h"
#include "src/Core/PlainObjectBase.h"
#include "src/Core/Matrix.h"
#include "src/Core/Array.h"

当用户将 Eigen/Core 包含在自己的文件中,编译时头文件的代码一经展开,PlainObjectBase 便是已经声明并定义好的了,所以客户端代码并不会发生链接问题。需要注意的是,如果客户只 include 其中一个文件比如 Matrix.h,此时就会发生链接问题,因为找不到 PlainObjectBase。这样做的好处是减少了各文件之间的编译依赖关系。