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/
51工具盒子