Eigen —— 参考指南

机器人的运动规划过程中会涉及大量的矩阵计算,而 EIGEN 库对于矩阵计算而言,其意义不言而喻。所以最近学习了一下 EIGEN 库的使用。并在此过程中记录自己的感悟和理解。本篇通过Eigen库的Quick reference guide来了解整个库。

文章目录

  • 1. Eigen模块和头文件(Modules and Header files)
  • 2. 数组、矩阵和向量类型(Array, matrix and vector types)
    • 2.1 基本矩阵运算(Basic matrix manipulation)
      • 2.1.1 构造函数
      • 2.1.2 初始化
      • 2.1.3 运行时可获取信息
      • 2.1.4 编译时对象类型信息
      • 2.1.5 调整大小
      • 2.1.6 数据获取
      • 2.1.7 赋值/Copy
    • 2.2 预定义矩阵(Predefined Matrices)
      • 2.2.1 固定维数的矩阵和向量
      • 2.2.2 动态维数的矩阵
      • 2.2.3 动态维数的向量
    • 2.3 映射外部数组(Mapping external arrays)
      • 2.3.1 连续存储
      • 2.3.2 strides的经典用法
  • 3. 算法运算符(Arithmetic Operators)
  • 4. 系数、数组运算符(Coefficient-wise & Array operators)
    • 4.1 算法运算符(Arithmetic operators)
    • 4.2 比较运算符(Comparisons)
    • 4.3 相关函数(Trigo, power, and misc functions and the STL-like variants)
  • 5. 简化函数(Reductions)
  • 6. 子矩阵(Sub-matrices)
  • 7. 其它操作(Miscellaneous operations)
    • 7.1 倒置(Reverse)
    • 7.2 复制(Replicate)
  • 8. 对角矩阵、三角矩阵和伴随矩阵(Diagonal, Triangular, and Self-adjoint matrices)
    • 8.1 对角矩阵(Diagonal matrices)
      • 8.1.1 将向量作为对角矩阵显示
      • 8.1.2 声明一个对象矩阵
      • 8.1.3 以向量形式读写对角元素
      • 8.1.4 优化的乘积和倒数
    • 8.2 三角形视图(Triangular views)
      • 8.2.1 带参引用
      • 8.2.2 向三角部分写数据
      • 8.2.3 乘法
      • 8.2.4 解方程
    • 8.3 对称/伴随矩阵(Symmetric/selfadjoint views)
  • 9. 参考资料

新学的知识,总喜欢自己动手整理一下,不知道是不是强迫症。整理完才发现,太长了,头疼。

1. Eigen模块和头文件(Modules and Header files)

EIGEN库其由核心模块和若干附属模块构成,每一模块都有对应的头文件供编程引用。其文件组织结构列表如下:

模组 头文件 说明
Core #include 矩阵(Matrix)和数据(Array)类,并涉及基本的线性代数和数组操作
Geometry #include 涉及变换、平移、缩放、2D和3D旋转、四元数、角轴(AngleAxis)
LU #include 逆运算、行列式、LU分解器 (FullPivLU, PartialPivLU)
Cholesky #include LLT and LDLT Cholesky 因式分解器
Householder #include Householder变换,也被其它线性代数模组所引用
SVD #include 基于最小二乘解算的SVD分解(JacobiSVD, BDCSVD)
QR #include QR分解器 (HouseholderQR, ColPivHouseholderQR, FullPivHouseholderQR)
Eigenvalues #include 特征值特征向量分解(EigenSolver, SelfAdjointEigenSolver, ComplexEigenSolver)
Sparse #include 稀疏矩阵存储和相关基础线性代数(SparseMatrix, SparseVector)
#include Includes Core, Geometry, LU, Cholesky, SVD, QR, and Eigenvalues header files
#include Includes Dense and Sparse header files (整个EIGEN库)

对于不涉及稀疏矩阵运算的,头文件使用#include 就可以满足要求,或者直接包含整个EIGEN库#include ,全面!任性!

2. 数组、矩阵和向量类型(Array, matrix and vector types)

Eigen库提供了矩阵和向量两种对象,都是由模版类进行表示的,矩阵用Matrix进行表示,而1维和2维数组则由模版类Array表示。Eigen库提供的模版类形式如下:

1
2
typedef Matrix< Scalar, RowsAtCompileTime, ColsAtCompileTime, Options > MyMatrixType;
typedef Array< Scalar, RowsAtCompileTime, ColsAtCompileTime, Options >  MyArrayType;
  • Scalar :是数据结构中标量元素的类型,例如 float, double, bool, int 等,即在模版类定义自定义数据类型时,其内部的元素只能是标量。
  • RowsAtCompileTime:指定编译时矩阵的行数,或用 Dynamic 参数,在运行时确定行数。
  • ColsAtCompileTime:指定矩阵的列数,或用 Dynamic 参数,在运行时确定行数。
  • Options:其值可以是 ColMajorRowMajor ,默认为 ColMajor

以下组合方式都是有效的:为了注释明显用%表示注释。

1
2
3
4
Matrix<double, 6, Dynamic>                  % Dynamic number of columns (heap allocation)
Matrix<double, Dynamic, 2>                  % Dynamic number of rows (heap allocation)
Matrix<double, Dynamic, Dynamic, RowMajor>  % Fully dynamic, row major (heap allocation)
Matrix<double, 13, 3>                       % Fully fixed (usually allocated on stack)

以上代码定义中,第一行定义的矩阵其行数是固定的,而列数是动态的,同理第二行定义。第三行则行列都是动态确定,最后一行则是指定了矩阵的固定行列数。这种方式,真是学到了。

而Eigen库为了使用上的方便,对常用的矩阵和数组类型进行了定义,如果不是个性化所需的数据类型,使用常用的已定义类型更加方便。矩阵类型中,已定义的有以下类型:

1
2
3
4
5
Matrix<float,Dynamic,Dynamic>   <=>   MatrixXf
Matrix<double,Dynamic,1>        <=>   VectorXd
Matrix<int,1,Dynamic>           <=>   RowVectorXi
Matrix<float,3,3>               <=>   Matrix3f
Matrix<float,4,1>               <=>   Vector4f

数组类型有以下已定义类型:

1
2
3
4
5
Array<float,Dynamic,Dynamic>    <=>   ArrayXXf
Array<double,Dynamic,1>         <=>   ArrayXd
Array<int,1,Dynamic>            <=>   RowArrayXi
Array<float,3,3>                <=>   Array33f
Array<float,4,1>                <=>   Array4f

<=> 后面表示定义的数据类型,就像double,float,int等一样,可以在包含头文件的代码中直接使用。例如:

1
2
3
4
5
6
7
8
using namespace Eigen;
MatrixXf variable_1;
VectorXd variable_2;
Matrix3f variable_3;
Vector4f variable_4;
ArrayXXf variable_5;

Eigen::Array33f variable_6;

在使用时要加命名空间,如果不想直接使用命名空间,则需要在数据类型之前加上作用域说明,如以上代码中的最后一行。同时矩阵和数组类型之间也有内在的转换机制。如下示例代码:

1
2
3
4
5
6
7
8
Array44f a1, a2;                  % 4 X 4 的二维数组
Matrix4f m1, m2;                  % 4 X 4 的矩阵
m1 = a1 * a2;                     % coeffwise product, 从数组到矩阵的隐式转换
a1 = m1 * m2;                     % matrix product, 从矩阵到数组的隐式转换
a2 = a1 + m1.array();             % 禁止混合数组和矩阵
m2 = a1.matrix() + m1;            % 如果需要混合运算,则需要显式转换
ArrayWrapper<Matrix4f> m1a(m1);   // m1a is an alias for m1.array(), they share the same coefficients
MatrixWrapper<Array44f> a1m(a1);

在以下的说明中,将使用*来强调特定对象的应用范围和领域:
* linear algebra matrix and vector only
* array objects only

2.1 基本矩阵运算(Basic matrix manipulation)

2.1.1 构造函数

对象定义可以用以下方式,类会自动调用构造函数,构造类对象。

1
2
3
4
5
Vector4d  v1;
Array3i   v2(x, y, z);        % 以x,y,z为元素构造类对象
Vector4d  v3(x, y, z, w);
Matrix4f  m1;
MatrixXf  m2;                 % 构造一个空对象

2.1.2 初始化

Eigen重载了<<运算符,可以直观的用数据为变量赋值,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Vector3f  v1;    
v1 << x, y, z;                 % 将x,y,z按顺利赋值给对应的位置

ArrayXf   v2(4);  
v2 << 1, 2, 3, 4;

Matrix3f  m1;  
m1 << 1, 2, 3,                 % 按行赋值给矩阵
      4, 5, 6,
      7, 8, 9;
     
int rows=5, cols=5;
MatrixXf m(rows,cols);
m << (Matrix3f() << 1, 2, 3, 4, 5, 6, 7, 8, 9).finished(),       % 以块的形式为矩阵赋值
     MatrixXf::Zero(3,cols-3),
     MatrixXf::Zero(rows-3,3),
     MatrixXf::Identity(rows-3,cols-3);
cout << m;

输出为:
        1 2 3 0 0
        4 5 6 0 0
        7 8 9 0 0
        0 0 0 1 0
        0 0 0 0 1

2.1.3 运行时可获取信息

使用类的优势之一就是提供了大量的成员函数,可以获取对象的信息,而Eigen中为矩阵类对象提供了丰富的接口:

1
2
3
4
5
6
7
8
9
10
11
vector.size();             % 获取向量的大小,即元素个数
vector.innerStride();      % 沿数据存储的方向移动一个位置,内存中需要移动的位置数
vector.data();             % 获取向量的数据

matrix.rows();             % 获取矩阵的行数
matrix.cols();             % 获取矩阵的列数
matrix.innerSize();        % 按列存储的矩阵返回其行数,接行存储的矩阵返回其列数
matrix.outerSize();        % 按列存储的矩阵返回其列数,按行存储的矩阵返回其行数
matrix.innerStride();      % 返回内部实际存储时相邻元素之间的指针增量
matrix.outerStride();      % 返回按逻辑顺序存储时的指针增量
matrix.data();             % 获取矩阵的数据指针

2.1.4 编译时对象类型信息

1
2
3
4
5
6
ObjectType::Scalar              
ObjectType::RowsAtCompileTime
ObjectType::RealScalar          
ObjectType::ColsAtCompileTime
ObjectType::Index              
ObjectType::SizeAtCompileTime

2.1.5 调整大小

1
2
3
4
5
6
7
8
vector.resize(size);                         % 调整具有动态尺寸的向量大小
vector.resizeLike(other_vector);             % 按other_vector的尺寸调整具有动态尺寸的向量大小
vector.conservativeResize(size);
matrix.resize(nb_rows, nb_cols);             % 调整成具有指定行列数的矩阵
matrix.resize(Eigen::NoChange, nb_cols);     % 只调整列
matrix.resize(nb_rows, Eigen::NoChange);     % 只调整行
matrix.resizeLike(other_matrix);             % 按other_matrix的大小调整矩阵
matrix.conservativeResize(nb_rows, nb_cols);

使用有风险,应用需要谨慎。

2.1.6 数据获取

带有范围检查的数据获取方式如下:

1
2
3
4
5
vector(i)     vector.x()
vector[i]     vector.y()
              vector.z()
              vector.w()
matrix(i,j)

不带有数据范围检查的获取方式:

1
2
3
4
vector.coeff(i)
vector.coeffRef(i)
matrix.coeff(i,j)
matrix.coeffRef(i,j)

2.1.7 赋值/Copy

1
2
object = expression;
object_of_float = expression_of_double.cast<float>();

如果可能,左侧为动态矩阵时,会被自动调整为与右侧相同的维数形式。

2.2 预定义矩阵(Predefined Matrices)

2.2.1 固定维数的矩阵和向量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef {Matrix3f|Array33f} FixedXD;
FixedXD x;

x = FixedXD::Identity();                   % 构造一个单位阵临时对象,并赋值给x
x = FixedXD::Zero();                       % 构造一个元素全是0的临时对象,并赋值给x
x = FixedXD::Ones();                       % 构造一个元素全是1的临时对象,并赋值给x
x = FixedXD::Constant(value);              % 构造一个元素全是value的临时对象,并赋值给x
x = FixedXD::Random();                     % 构造一个元素是随机值的临时对象,并赋值给x
x = FixedXD::LinSpaced(size, low, high);   % 构造一个元素数值在[low,high]区间的临时对象,并赋值给x
 
x.setIdentity();                           % 设置对象为单位阵
x.setZero();                               % 设置对象的元素值全为0
x.setOnes();                               % 设置对象的元素值全为1
x.setConstant(value);                      % 设置对象的元素值全为 value
x.setRandom();                             % 设置对象的元素值为随机值
x.setLinSpaced(size, low, high);           % 设置对象的元素数值在范围区间内

Vector3f::UnitX() // 1 0 0
Vector3f::UnitY() // 0 1 0
Vector3f::UnitZ() // 0 0 1

2.2.2 动态维数的矩阵

与上面一部分类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef {MatrixXf|ArrayXXf} Dynamic2D;
Dynamic2D x;

x = Dynamic2D::Zero(rows, cols);
x = Dynamic2D::Ones(rows, cols);
x = Dynamic2D::Constant(rows, cols, value);
x = Dynamic2D::Random(rows, cols);
 
x.setZero(rows, cols);
x.setOnes(rows, cols);
x.setConstant(rows, cols, value);
x.setRandom(rows, cols);

x = Dynamic2D::Identity(rows, cols);
x.setIdentity(rows, cols);

2.2.3 动态维数的向量

与以上相比,多了个尺寸参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef {VectorXf|ArrayXf} Dynamic1D;
Dynamic1D x;
 
x = Dynamic1D::Zero(size);
x = Dynamic1D::Ones(size);
x = Dynamic1D::Constant(size, value);
x = Dynamic1D::Random(size);
x = Dynamic1D::LinSpaced(size, low, high);
 
x.setZero(size);
x.setOnes(size);
x.setConstant(size, value);
x.setRandom(size);
x.setLinSpaced(size, low, high);

VectorXf::Unit(size,i)
VectorXf::Unit(4,1) == Vector4f(0,1,0,0)
                    == Vector4f::UnitY()

2.3 映射外部数组(Mapping external arrays)

以下两部分看官方给的例子,自己平时用得少。Map就是将数据以矩阵形式重新组织的过程。

2.3.1 连续存储

1
2
3
4
5
float data[] = {1,2,3,4};
Map<Vector3f> v1(data);       // uses v1 as a Vector3f object
Map<ArrayXf>  v2(data,3);     // uses v2 as a ArrayXf object
Map<Array22f> m1(data);       // uses m1 as a Array22f object
Map<MatrixXf> m2(data,2,2);   // uses m2 as a MatrixXf object

2.3.2 strides的经典用法

1
2
3
4
5
float data[] = {1,2,3,4,5,6,7,8,9};
Map<VectorXf,0,InnerStride<2> >  v1(data,3);                      // = [1,3,5]
Map<VectorXf,0,InnerStride<> >   v2(data,3,InnerStride<>(3));     // = [1,4,7]
Map<MatrixXf,0,OuterStride<3> >  m2(data,2,3);                    // both lines     |1,4,7|
Map<MatrixXf,0,OuterStride<> >   m1(data,2,3,OuterStride<>(3));   // are equal to:  |2,5,8|

3. 算法运算符(Arithmetic Operators)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mat3 = mat1 + mat2;           mat3 += mat1;                              % 加
mat3 = mat1 - mat2;           mat3 -= mat1;                              % 减
mat3 = mat1 * s1;             mat3 *= s1;           mat3 = s1 * mat1;    % 数乘 scalar product
mat3 = mat1 / s1;             mat3 /= s1;
col2 = mat1 * col1;                                                      % 乘 matrix/vector products *
row2 = row1 * mat1;           row1 *= mat1;
mat3 = mat1 * mat2;           mat3 *= mat1;

mat1 = mat2.transpose();      mat1.transposeInPlace();                   % 转置   原地转置
mat1.conjugate();                                                        % 共轭
mat1 = mat2.adjoint();        mat1.adjointInPlace();                     % 共轭转置  原地共轭转置
scalar = vec1.dot(vec2);                                                 % 点乘
scalar = col1.adjoint() * col2;
scalar = (col1.adjoint() * col2).value();                                % inner product * 
mat = col1 * col2.transpose();                                           % outer product *
scalar = vec1.norm();         scalar = vec1.squaredNorm();               % norm
vec2 = vec1.normalized();     vec1.normalize(); // inplace               % normalization *
vec3 = vec1.cross(vec2);                                                 % 叉乘 *

mat1.sum();                                                              % 所有元素求和
mat1.prod();                                                             % 所有元素乘积
mat1.mean();                                                             % 所有元素求平均
mat1.trace();                                                            % 迹,对角元素的和

4. 系数、数组运算符(Coefficient-wise & Array operators)

除了以上提到的运算,Eigen库还提供大量系数性运算和函数。大部分在数组相关的计算中都有其存在意义。以下算子在数组类对象之间可以使用,或通过 .array() 将向量和矩阵转换为数组类对象使用:

4.1 算法运算符(Arithmetic operators)

1
2
array1 * array2     array1 / array2     array1 *= array2    array1 /= array2
array1 + scalar     array1 - scalar     array1 += scalar    array1 -= scalar

4.2 比较运算符(Comparisons)

1
2
3
4
array1 < array2     array1 > array2     array1 < scalar     array1 > scalar
array1 <= array2    array1 >= array2    array1 <= scalar    array1 >= scalar
array1 == array2    array1 != array2    array1 == scalar    array1 != scalar
array1.min(array2)  array1.max(array2)  array1.min(scalar)  array1.max(scalar)

4.3 相关函数(Trigo, power, and misc functions and the STL-like variants)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
array1.abs2()
array1.abs()                  abs(array1)
array1.sqrt()                 sqrt(array1)
array1.log()                  log(array1)
array1.log10()                log10(array1)
array1.exp()                  exp(array1)
array1.pow(array2)            pow(array1,array2)
array1.pow(scalar)            pow(array1,scalar)
                              pow(scalar,array2)
array1.square()
array1.cube()
array1.inverse()
 
array1.sin()                  sin(array1)
array1.cos()                  cos(array1)
array1.tan()                  tan(array1)
array1.asin()                 asin(array1)
array1.acos()                 acos(array1)
array1.atan()                 atan(array1)
array1.sinh()                 sinh(array1)
array1.cosh()                 cosh(array1)
array1.tanh()                 tanh(array1)
array1.arg()                  arg(array1)
 
array1.floor()                floor(array1)
array1.ceil()                 ceil(array1)
array1.round()                round(aray1)
 
array1.isFinite()             isfinite(array1)
array1.isInf()                isinf(array1)
array1.isNaN()                isnan(array1)

以下系数方面的函数对于所有的数据形式都适用 (matrices, vectors, and arrays), 对复数也有效:

1
2
3
4
Eigen's API             STL-like APIs*           Comments
mat1.real()             real(array1)             // read-write, no-op for real expressions
mat1.imag()             imag(array1)             // read-only for real, read-write for complexes
mat1.conjugate()        conj(array1)             // no-op for real expressions

通过cwise* 的方法一些运算可以方便的应用于矩阵和向量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
=============Matrix API * ==============

mat1.cwiseMin(mat2)         mat1.cwiseMin(scalar)
mat1.cwiseMax(mat2)         mat1.cwiseMax(scalar)
mat1.cwiseAbs2()
mat1.cwiseAbs()
mat1.cwiseSqrt()
mat1.cwiseInverse()
mat1.cwiseProduct(mat2)
mat1.cwiseQuotient(mat2)
mat1.cwiseEqual(mat2)       mat1.cwiseEqual(scalar)
mat1.cwiseNotEqual(mat2)

=============Via Array conversions=============
mat1.array().min(mat2.array())    mat1.array().min(scalar)
mat1.array().max(mat2.array())    mat1.array().max(scalar)
mat1.array().abs2()
mat1.array().abs()
mat1.array().sqrt()
mat1.array().inverse()
mat1.array() * mat2.array()
mat1.array() / mat2.array()
mat1.array() == mat2.array()      mat1.array() == scalar
mat1.array() != mat2.array()

以上两种API的不同之处在于,矩阵类型的API返回矩阵类型的表达,而数组类型的API,返回的是数组结构的结果。 .array() 只是改变了可用的API和对数据的解释,所以并没有额外的消耗。

应用用户自定义的程序通过使用 DenseBase::unaryExpr 和 std::ptr_fun (c++03), std::ref (c++11), or lambdas (c++11)来完成:

1
2
3
mat1.unaryExpr(std::ptr_fun(foo));
mat1.unaryExpr(std::ref(foo));
mat1.unaryExpr([](double x) { return foo(x); });

5. 简化函数(Reductions)

Eigen 提供了若干简化函数,例如: minCoeff() , maxCoeff() , sum() , prod() , trace() *, norm() *, squaredNorm() *, all() , and any() 。所有的函数操作都可以按照矩阵形式(matrix-wise), 列行式(column-wise) 或 行行式(row-wise)操作,例子如下:

1
2
3
4
5
6
7
8
9
10
      5 3 1
mat = 2 7 8
      9 4 6
mat.minCoeff();             输出: 1          % 求矩阵的最小元素

mat.colwise().minCoeff();   输出: 2 3 1      % 求每列的最小元素,所以输出为行

mat.rowwise().minCoeff();   输出:  1         % 求每行的最小元素,所以输出为列
                                   2
                                   4

特殊形式的minCoeff and maxCoeff ,也可以说是特殊应用形式

1
2
3
int i, j;
s = vector.minCoeff(&i);        // s == vector[i]
s = matrix.maxCoeff(&i, &j);    // s == matrix(i,j)

all()和any()函数的经典用法:

1
2
if((array1 > 0).all()) ...      // if all coefficients of array1 are greater than 0 ...
if((array1 < array2).any()) ... // if there exist a pair i,j such that array1(i,j) < array2(i,j) ...

6. 子矩阵(Sub-matrices)

矩阵或数组的行列读写访问:

1
2
mat1.row(i) = mat2.col(j);
mat1.col(j1).swap(mat1.col(j2));

子向量的读写访问:

1
2
3
4
5
6
7
|===========================|================================|=================================================|
|        默认形式           |     编译时已知维数的优化形式      |                 Description                     |
|===========================|================================|=================================================|
|      vec1.head(n)         |       vec1.head<n>()           | the first n coeffs                              |
|      vec1.tail(n)         |       vec1.tail<n>()           | the last n coeffs                               |
|      vec1.segment(pos,n)  |       vec1.segment<n>(pos)     | the n coeffs in the range [pos : pos + n - 1]   |
|===========================|================================|=================================================|

子矩阵的读写访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
|===================================|=====================================|===========================================|
|              默认形式             |         编译时已知维数的优化形式       |              Description                  |
|===================================|=====================================|===========================================|
| mat1.block(i,j,rows,cols)         | mat1.block<rows,cols>(i,j)          | 以(i,j)作为起始位置的rows行和cols列的子矩阵  |
| mat1.topLeftCorner(rows,cols)     | mat1.topLeftCorner<rows,cols>()     | 以左上角为起始位置的由rows和cols定义的子矩阵  |
| mat1.topRightCorner(rows,cols)    | mat1.topRightCorner<rows,cols>()    | 以右上角为起始位置的由rows和cols定义的子矩阵  |
| mat1.bottomLeftCorner(rows,cols)  | mat1.bottomLeftCorner<rows,cols>()  | 以左下角为起始位置的由rows和cols定义的子矩阵  |
| mat1.bottomRightCorner(rows,cols) | mat1.bottomRightCorner<rows,cols>() | 以右下角为起始位置的由rows和cols定义的子矩阵  |
| mat1.topRows(rows)                | mat1.topRows<rows>()                | 由矩阵的前几行构成的子矩阵                   |
| mat1.bottomRows(rows)             | mat1.bottomRows<rows>()             | 由矩阵的后几行构成的子矩阵                   |
| mat1.leftCols(cols)               | mat1.leftCols<cols>()               | 由矩阵的前几列构成的子矩阵                   |
| mat1.rightCols(cols)              | mat1.rightCols<cols>()              | 由矩阵的后几列构成的子矩阵                   |
|===================================|=====================================|===========================================|

7. 其它操作(Miscellaneous operations)

7.1 倒置(Reverse)

向量或矩阵的行和列都可以进行倒置,如果说矩阵转置是按左对角线进行的,那么倒置则是沿右对角线进行(see DenseBase::reverse(), DenseBase::reverseInPlace(), VectorwiseOp::reverse())。

1
2
3
4
vec.reverse()          
mat.colwise().reverse()  
mat.rowwise().reverse()
vec.reverseInPlace()

7.2 复制(Replicate)

向量或矩阵的行和列可以在任意方向上进行复制:

1
2
3
4
vec.replicate(times)                                          vec.replicate<Times>
mat.replicate(vertical_times, horizontal_times)               mat.replicate<VerticalTimes, HorizontalTimes>()
mat.colwise().replicate(vertical_times, horizontal_times)     mat.colwise().replicate<VerticalTimes, HorizontalTimes>()
mat.rowwise().replicate(vertical_times, horizontal_times)     mat.rowwise().replicate<VerticalTimes, HorizontalTimes>()

8. 对角矩阵、三角矩阵和伴随矩阵(Diagonal, Triangular, and Self-adjoint matrices)

本部分的操作和运算只适用于矩阵:

8.1 对角矩阵(Diagonal matrices)

8.1.1 将向量作为对角矩阵显示

1
mat1 = vec1.asDiagonal();

返回一个以向量元素作为对角矩阵对角元素的伪对角矩阵。这个操作只适用于向量,行向量或列向量都可以。例如通过以下代码可明白其使用方式:

1
2
3
4
5
6
cout << Matrix3i(Vector3i(2,5,6).asDiagonal()) << endl;

输出为:  
      2 0 0
      0 5 0
      0 0 6

8.1.2 声明一个对象矩阵

1
2
DiagonalMatrix<Scalar,SizeAtCompileTime> diag1(size);
diag1.diagonal() = vector;

8.1.3 以向量形式读写对角元素

1
2
3
4
5
vec1 = mat1.diagonal();        mat1.diagonal() = vec1;      // main diagonal
vec1 = mat1.diagonal(+n);      mat1.diagonal(+n) = vec1;    // n-th super diagonal
vec1 = mat1.diagonal(-n);      mat1.diagonal(-n) = vec1;    // n-th sub diagonal
vec1 = mat1.diagonal<1>();     mat1.diagonal<1>() = vec1;   // first super diagonal
vec1 = mat1.diagonal<-2>();    mat1.diagonal<-2>() = vec1;  // second sub diagonal

8.1.4 优化的乘积和倒数

1
2
3
4
mat3  = scalar * diag1 * mat1;
mat3 += scalar * mat1 * vec1.asDiagonal();
mat3 = vec1.asDiagonal().inverse() * mat1
mat3 = mat1 * diag1.inverse()

8.2 三角形视图(Triangular views)

Triangular views给出了矩阵的三角形视图,并且允许执行优化的操作,而另一三角部分将不会被访问,可以用来存储其它数据。详细信息需要参考The template and typename keywords in C++。

8.2.1 带参引用

1
2
3
4
m.triangularView<Xxx>()

Xxx参数可以是以下几种:
Xxx = Upper, Lower, StrictlyUpper, StrictlyLower, UnitUpper, UnitLower

8.2.2 向三角部分写数据

1
m1.triangularView<Eigen::Lower>() = m2 + m3

转换成矩阵,并将另一部分设置为0:

1
m2 = m1.triangularView<Eigen::UnitUpper>()

8.2.3 乘法

1
2
m3 += s1 * m1.adjoint().triangularView<Eigen::UnitUpper>() * m2
m3 -= s1 * m2.conjugate() * m1.adjoint().triangularView<Eigen::Lower>()

8.2.4 解方程

分别对应以下方程:

M2:=L1?1M2M_2 := L_1^{-1} M_2

M2?:=L1?1?M2?

M3:=L1??1M3M_3 := {L_1^*}^{-1} M_3

M3?:=L1???1M3?

M4:=M4U1?1M_4 := M_4 U_1^{-1}

M4?:=M4?U1?1?

1
2
3
L1.triangularView<Eigen::UnitLower>().solveInPlace(M2)
L1.triangularView<Eigen::Lower>().adjoint().solveInPlace(M3)
U1.triangularView<Eigen::Upper>().solveInPlace<OnTheRight>(M4)

8.3 对称/伴随矩阵(Symmetric/selfadjoint views)

就像三角矩阵一样,可以引用方矩阵的任何三角形部分以将其视为自伴矩阵并执行特殊的优化操作。 同样,相对的三角形部分不会引用,可用于存储其他信息。

1
2
3
4
5
6
Conversion to a dense matrix:
m2 = m.selfadjointView<Eigen::Lower>();      
                               
Product with another general matrix or vector:
m3  = s1 * m1.conjugate().selfadjointView<Eigen::Upper>() * m3;    
m3 -= s1 * m3.adjoint() * m1.selfadjointView<Eigen::Lower>();

Rank 1 and rank K update:

upper(M1)+=s1M2M2?upper(M_1) \mathrel{{+}{=}} s_1 M_2 M_2^*

upper(M1?)+=s1?M2?M2??

lower(M1)?=M2?M2lower(M_1) \mathbin{{-}{=}} M_2^* M_2

lower(M1?)?=M2??M2?

1
2
M1.selfadjointView<Eigen::Upper>().rankUpdate(M2,s1);  
M1.selfadjointView<Eigen::Lower>().rankUpdate(M2.adjoint(),-1);

Rank 2 update:

M+=suv?+svu?M \mathrel{{+}{=}} s u v^* + s v u^*

M+=suv?+svu?

1
M.selfadjointView<Eigen::Upper>().rankUpdate(u,v,s);

解线性方程:

M2:=M1?1M2M_2 := M_1^{-1} M_2

M2?:=M1?1?M2?

1
2
3
4
// via a standard Cholesky factorization
m2 = m1.selfadjointView<Eigen::Upper>().llt().solve(m2);
// via a Cholesky factorization with pivoting
m2 = m1.selfadjointView<Eigen::Lower>().ldlt().solve(m2);

9. 参考资料

[1] Eigen Quick reference guide
[2] Eigen: C++开源矩阵计算工具——Eigen的简单用法
[3] Eigen矩阵库使用说明
[4] Eigen矩阵运算开源库使用完全指南
[5] Eigen介绍及简单使用
[6] C++矩阵库 Eigen 简介
[7] Eigen库使用指南