Eigen 是开源的C++线性代数库,常用在计算机图形学中,之前我们记录了 安装使用方法,本文记录常用功能使用方法。
动态矩阵、静态二维矩阵 {#动态矩阵、静态二维矩阵}
- Eigen 官方代码支持的最高维度为二维矩阵,后文的矩阵、数组均为二维。
- Eigen 在编译期间确定尺寸的矩阵为静态矩阵,运行期间确定尺寸的为动态矩阵(数据类型中带有X)
- 选用原则:
- 对于非常小尺寸的矩阵,尽可能使用固定尺寸,特别是小于(大约)16的尺寸,使用固定尺寸对性能非常有益,因为它允许 Eigen 避免动态内存分配和展开循环; 对于小尺寸在内部,一个固定大小的特征矩阵只是一个普通的数组。
- 对于较大尺寸,或者在必须使用动态尺寸的地方,尽量使用动态尺寸。当矩阵尺寸大于(大约)32时,静态矩阵的性能收益变得可以忽略,而且对于动态矩阵,Eigen 更倾向于尝试使用 SIMD 指令集加速运算。
模板类 {#模板类}
- Eigen 中有几个基础数据结构模板类
Matrix类 {#Matrix类}
- 所有矩阵和向量都是
Matrix
模板类的对象,Matrix
类有6个模板参数,主要使用前三个,剩下的使用默认值。 MaxRowsAtCompileTime
和MaxColsAtCompileTime
在已知动态矩阵的尺寸上界时是可以提升工作效率的。
-
默认构造时,指定大小的矩阵,只分配相应大小的空间,不进行初始化。动态大小的矩阵,则未分配空间。
-
[]
操作符可以用于向量元素的获取,但不能用于matrix
。 -
matrix
的大小可以通过rows()
,cols()
,size()
获取,resize()
可以重新调整矩阵大小。 -
Matrix
定义的矩阵为静态矩阵,在编译时确定尺寸、分配内存,随机初始化:
-
MatrixX
开头的为动态矩阵,两个维度都可以变化,本质为Matrix<Type, Dynamic, Dynamic>
定义的类型例如:
MatrixXd
为 double 类型的动态矩阵 -
静态矩阵运算很快,但是有 128k 的堆栈尺寸限制,常用的还是动态矩阵类型
-
仅变化一个维度的动态矩阵为动态向量
typedef Matrix<float, Dynamic, 1> VectorXf
,使用方法类似
Array类 {#Array类}
Array
是类模板,前三个参数必须指定,后三个参数可选。
-
类似于
Matrix
类,Array
默认仍会产生静态数组 -
一维动态数组为
ArrayX
开头,二维动态数组为ArrayXX
开头
Array 和 Martix 的区别 {#Array-和-Martix-的区别}
- Martix 表示的是矩阵,运算为矩阵运算,运算时尺寸需要遵循矩阵运算规则
- Array 和 Matrix 数据组成相同,但运算规则为逐元素运算,需要相同尺寸数据进行运算
Array 和 Martix 的转换 {#Array-和-Martix-的转换}
Matrix
对象------>Array
对象:.array()
函数Array
对象------>Matrix
对象:.matrix()
函数
初始化 {#初始化}
建议矩阵数据都要初始化,不然是十分危险的。
默认初始化 {#默认初始化}
- 默认初始化为随机数:
赋值初始化 {#赋值初始化}
- 赋值初始化
对象初始化 {#对象初始化}
- 可以用其他对象初始化新的相同内容对象
逗号初始化 {#逗号初始化}
- 为矩阵元素赋值,顺序是从左到右,从上到下,数目必须匹配。
特殊矩阵初始化 {#特殊矩阵初始化}
静态矩阵 {#静态矩阵}
一下几个函数均为静态矩阵调用的初始化函数,动态矩阵调用会报错:
- 零阵:类静态成员函数
Zero()
- 常量矩阵:
Constant(rows, cols, value)
- 随机矩阵:
Random()
- 单位矩阵:
Identity()
动态矩阵 {#动态矩阵}
| 函数 | 含义 |
|-----------------|--------|
| setZero()
| 矩阵归零 |
| setConstant
() | 矩阵归常数 |
| setIdentity
() | 矩阵归单位阵 |
| setOnes
() | 矩阵归一 |
| setRandom
() | 矩阵随机数 |
动态向量函数 {#动态向量函数}
- 仅能在向量类型数据中使用
| 函数 | 含义 |
|------------------|-----------------|
| setLinSpaced()
| 填充线性间隔的数据 |
| setUnit()
| 指定向量位置数据置1,其余为0 |
索引数据 {#索引数据}
单个数据 {#单个数据}
-
主要数据的存取和修改都是通过重载的括号运算符完成的
-
对于二维矩阵,下标顺序为 row col
-
对于向量,则只有向量下标需要传入
块操作 {#块操作}
- 语法:
| 块 | 动态矩阵 | 静态矩阵 |
|------------------------|--------------------------|---------------------------|
| 尺寸 (p, q) 左上角坐标 (i, j) | matrix.block(i,j,p,q);
| matrix.block<p,q>(i,j);
|
- 示例:
行列操作 {#行列操作}
- 语法:
| 操作 | 方法 |
|-------|------------------|
| 第 i 行 | matrix.row(i);
|
| 第 i 列 | matrix.col(j);
|
- 示例:
角操作 {#角操作}
- 语法:
| 块操作 | 动态矩阵语法 | 静态矩阵语法 |
|--------------|----------------------------------|------------------------------------|
| 左上角 p 行 q 列 | matrix.topLeftCorner(p,q);
| matrix.topLeftCorner<p,q>();
|
| 左下角 p 行 q 列 | matrix.bottomLeftCorner(p,q);
| matrix.bottomLeftCorner<p,q>();
|
| 右上角 p 行 q 列 | matrix.topRightCorner(p,q);
| matrix.topRightCorner<p,q>();
|
| 右下角 p 行 q 列 | matrix.bottomRightCorner(p,q);
| matrix.bottomRightCorner<p,q>();
|
| 前 q 行 | matrix.topRows(q);
| matrix.topRows<q>();
|
| 后 q 行 | matrix.bottomRows(q);
| matrix.bottomRows<q>();
|
| 前 p 列 | matrix.leftCols(p);
| matrix.leftCols<p>();
|
| 后 p 列 | matrix.rightCols(q);
| matrix.rightCols<q>();
|
| 第 i 列开始取 q 列 | matrix.middleCols(i,q);
| matrix.middleCols<q>(i);
|
| 第 i 行开始取 q 行 | matrix.middleRows(i,q);
| matrix.middleRows<q>(i);
|
- 示例:
向量块操作 {#向量块操作}
| 块操作 | 动态矩阵语法 | 静态矩阵语法 |
|-------------------|------------------------|-------------------------|
| 向量前 n 个数据 | vector.head(n);
| vector.head<n>();
|
| 向量后 n 个数据 | vector.tail(n);
| vector.tail<n>();
|
| 向量从下标 i 开始的 n 个数据 | vector.segment(i,n);
| vector.segment<n>(i);
|
常用操作 {#常用操作}
- 大多数情况下,Eigen 要求操作的数据类型一致
布尔归约 {#布尔归约}
| 操作 | 语法 | 示例 |
|--------------------------|----------------|-----------------|
| 转置 | .transpose()
| v.transpose()
|
| 所有元素为 true(非0),返回 bool 值 | all()
| m.all()
|
| 存在元素为 true(非0),返回 bool 值 | any()
| m.any()
|
| 统计 true(非0) 的个数 | count()
| m.count()
|
数据类型转换 {#数据类型转换}
| 操作 | 语法 | 示例 |
|----------------|-------------------|------------------------|
| 数据类型转换为 double | .cast<double>()
| A.cast<double>()
|
| 数据类型转换为 float | .cast<float>()
| A.cast<float>()
|
| 数据类型转换为 int | .cast<int>()
| A.cast<int>()
|
| 数据类型转换为实部 | .real()
| A.real()
|
| 数据类型转换为虚部 | .imag()
| A.imag()
|
| 内存数据转 Eigen | Map<>()
| Map<Matrix3i>(array)
|
- 内存数据转 Eigen:
Vector 向量操作 {#Vector-向量操作}
| 操作 | 语法 | 示例 |
|-------|------------------|-------------------|
| 点乘 | dot()
| v.dot(w)
|
| 叉乘 | .cross()
| v.cross(w)
|
| 元素个数 | .size()
| v.size()
|
| 生成对角阵 | .asDiagonal()
| v.asDiagonal()
|
| 向量平方和 | .squaredNorm()
| v.squaredNorm()
|
| 向量模长 | .norm()
| v.norm()
|
Matrix 矩阵操作 {#Matrix-矩阵操作}
| 操作 | 语法 | 示例 |
|-------|----------------|--------------------------------------------------|
| 转置 | .transpose()
| v.transpose()
|
| 共轭 | .conjugate()
| a.conjugate()
|
| 共轭转置 | .adjoint()
| a.adjoint()
|
| 元素个数 | .size()
| a.size()
|
| 行数 | .rows()
| a.rows()
|
| 列数 | .rols()
| a.rols()
|
| 填充 | .fill()
| a.fill(6)
|
| 交换行/列 | .swap()
| m.block(0, 0, 2, 2).swap(n.block(2, 2, 2, 2));
|
| 获取对角线 | .diagonal()
| a.diagonal()
|
| 列向操作 | .colwise()
| m.colwise().sum()
|
| 元素颠倒 | .reverse()
| m.reverse()
|
-
赋值经过优化:
行可以给列赋值,这个其实还是挺可怕的,需要格外小心。
-
列向操作相当于 numpy 中的
axis=1
,只对列方向做某种操作:
Matrix 矩阵运算 {#Matrix-矩阵运算}
| 操作 | 语法 | 示例 |
|-----------|---------------------|-----------------------------------|
| 矩阵相加 | + | a + b
|
| 矩阵相减 | - | a - b
|
| 负号 | - | - a
|
| 复合算子加 | += | a += b
|
| 复合算子减 | -= | a -= b
|
| 标量乘法 | * | matrix*scalar
/ scalar*matrix
|
| 标量除法 | / | matrix/scalar
|
| 复合算子乘 | *= | matrix*=scalar
|
| 复合算子除 | /= | matrix/=scalar
|
| 矩阵\向量乘法 | * | matrix*matrix, matrix*vector
|
| 矩阵求和 | .sum()
| mat.sum()
|
| 所有元素乘积 | .prod()
| mat.prod()
|
| 矩阵均值 | .mean()
| mat.mean()
|
| 矩阵最小值 | .minCoeff()
| mat.minCoeff()
|
| 矩阵最大值 | .maxCoeff()
| mat.maxCoeff()
|
| 矩阵最小值,带位置 | .minCoeff(&r, &c)
| mat.minCoeff(&r, &c)
|
| 矩阵最大值,带位置 | .maxCoeff(&r, &c)
| mat.maxCoeff(&r, &c)
|
| 迹 | .trace()
| mat.trace()
|
| 逐元素绝对值 | .cwiseAbs()
| mat.cwiseAbs()
|
| 逐元素相乘 | .cwiseProduct()
| mat = mat.cwiseProduct(mat)
|
| 逐元素相除 | .cwiseQuotient()
| mat = mat.cwiseQuotient(mat)
|
| 逐元素取倒数 | .cwiseInverse()
| mat.cwiseInverse()
|
| 逐元素开根号 | .cwiseSqrt()
| mat.cwiseSqrt()
|
| 逐元素最大值 | .cwiseMax(m)
| mat.cwiseMax(mat2)
|
| 逐元素最小值 | .cwiseMin(m)
| mat.cwiseMin(mat2)
|
| 元素平方和 | .squaredNorm()
| mat.squaredNorm()
|
| 矩阵二阶范数 | .norm()
| mat.norm()
|
| p 阶范数 | .lpNorm<p>()
| mat.lpNorm<3>()
|
-
最大值、最小值
返回最大、最小值,同时定位位置
Array 数组运算 {#Array-数组运算}
| 操作 | 语法 | 示例 |
|-----------|----------------------|-------------------------------|
| 逐元素相乘 | * | a * b
|
| 逐元素相除 | / | a * b
|
| 矩阵相加 | + | a + b
|
| 矩阵相减 | - | a - b
|
| 负号 | - | - a
|
| 复合算子加 | += | a += b
|
| 复合算子减 | -= | a -= b
|
| 逐元素比较 | <, >, >=, <=, == | a < b
|
| 逐元素标量计算 | +, -, *, / | a + 3
|
| 逐元素标量复合计算 | +=, -=, *=, /= | a /= 3
|
| 逐元素取倒数 | .inverse()
| a.inverse()
|
| 逐元素 sin | .sin()
| a.sin()
|
| 逐元素 cos | .cos()
| a.cos()
|
| 逐元素乘方 | .pow(s)
| a.pow(3)
|
| 逐元素平方 | .square()
| a.square()
|
| 逐元素立方 | .cube()
| a.cube()
|
| 逐元素开根号 | .sqrt()
| a.sqrt()
|
| 逐元素计算自然指数 | .exp()
| a.exp()
|
| 逐元素计算自然对数 | .log()
| a.log()
|
| 逐元素最小值 | .min()
| a.min(b)
|
| 逐元素最大值 | .max()
| a.max(b)
|
| 逐元素绝对值 | .abs()
| a.abs()
|
| 逐元素选择 | .select()
| (R.array() < s).select(P,Q)
|
-
逐元素比较:
-
逐元素选择:
矩阵分解与解线性方程组 {#矩阵分解与解线性方程组}
矩阵分解 {#矩阵分解}
| 分解方法 | 语法 | 输出 |
|------------|-----------|-------------------------------------------------|
| 柯列斯基分解 | .ldlt()
| .matrixL() and .matrixD()
|
| LLT 分解 | .llt()
| .matrixL()
|
| 部分旋转的 LU分解 | .lu()
| .matrixL() and .matrixU()
|
| QR 分解 | .qr()
| .matrixQ() and .matrixR()
|
| SVD 分解 | .svd()
| .matrixU(), .singularValues(), and .matrixV()
|
求解线性方程组 {#求解线性方程组}
- 对应不同矩阵分解方式,有不同的解方程组的方法
| 语法 | 描述 |
|---------------------------|-------------------------------------------|
| x = A.ldlt().solve(b));
| A sym. p.s.d. #include <Eigen/Cholesky>
|
| x = A.llt() .solve(b));
| A sym. p.d. #include <Eigen/Cholesky>
|
| x = A.lu() .solve(b));
| Stable and fast. #include <Eigen/LU>
|
| x = A.qr() .solve(b));
| No pivoting. #include <Eigen/QR>
|
| x = A.svd() .solve(b));
| Stable, slowest. #include <Eigen/SVD>
|
特征值特征向量 {#特征值特征向量}
-
特征值:
-
特征向量:
混淆问题 {#混淆问题}
- 使用
eval()
函数解决把右值赋值为一个临时矩阵,再赋给左值时可能有造成的混淆。如:
- 原地操作的一类函数:
| 普通函数 | inplace函数 | |-------------------------|--------------------------------| | MatrixBase::adjoint() | MatrixBase::adjointInPlace() | | DenseBase::reverse() | DenseBase::reverseInPlace() | | LDLT::solve() | LDLT::solveInPlace() | | LLT::solve() | LLT::solveInPlace() | | TriangularView::solve() | TriangularView::solveInPlace() | | DenseBase::transpose() | DenseBase::transposeInPlace() |
- 当相同的矩阵或array出现在等式左右时,容易出现混淆
- 当确定不会出现混淆时,可以使用
noalias()
- 混淆出现时,可以使用
eval()
和xxxInPlace()
函数解决
参考资料 {#参考资料}
- https://www.jianshu.com/p/931dff3b1b21?tdsourcetag=s_pctim_aiomsg
- https://eigen.tuxfamily.org/dox/group__TutorialMatrixClass.html
- https://eigen.tuxfamily.org/dox/unsupported/eigen_tensors.html
- https://www.cnblogs.com/jast/p/4244610.html
文章链接:
https://www.zywvvd.com/notes/coding/cpp/eigen/eigen-usage/