本文最后更新于:2024年5月7日 下午
Eigen 是开源的C++线性代数库,常用在计算机图形学中,之前我们记录了 安装使用方法,本文记录常用功能使用方法。
动态矩阵、静态二维矩阵
- Eigen 官方代码支持的最高维度为二维矩阵,后文的矩阵、数组均为二维。
- Eigen 在编译期间确定尺寸的矩阵为静态矩阵,运行期间确定尺寸的为动态矩阵(数据类型中带有X)
- 选用原则:
- 对于非常小尺寸的矩阵,尽可能使用固定尺寸,特别是小于(大约)16的尺寸,使用固定尺寸对性能非常有益,因为它允许 Eigen 避免动态内存分配和展开循环; 对于小尺寸在内部,一个固定大小的特征矩阵只是一个普通的数组。
- 对于较大尺寸,或者在必须使用动态尺寸的地方,尽量使用动态尺寸。当矩阵尺寸大于(大约)32时,静态矩阵的性能收益变得可以忽略,而且对于动态矩阵,Eigen 更倾向于尝试使用 SIMD 指令集加速运算。
模板类
- Eigen 中有几个基础数据结构模板类
Matrix类
- 所有矩阵和向量都是
Matrix
模板类的对象,Matrix
类有6个模板参数,主要使用前三个,剩下的使用默认值。 MaxRowsAtCompileTime
和MaxColsAtCompileTime
在已知动态矩阵的尺寸上界时是可以提升工作效率的。
1 |
|
-
默认构造时,指定大小的矩阵,只分配相应大小的空间,不进行初始化。动态大小的矩阵,则未分配空间。
-
[]
操作符可以用于向量元素的获取,但不能用于matrix
。 -
matrix
的大小可以通过rows()
,cols()
,size()
获取,resize()
可以重新调整矩阵大小。 -
Matrix
定义的矩阵为静态矩阵,在编译时确定尺寸、分配内存,随机初始化:
1 |
|
-
MatrixX
开头的为动态矩阵,两个维度都可以变化,本质为Matrix<Type, Dynamic, Dynamic>
定义的类型例如:
MatrixXd
为 double 类型的动态矩阵1
2
3
4
5
6
7MatrixXd a(3, 3);
cout << a;
-->
0 0 0
0 0 0
0 0 0 -
静态矩阵运算很快,但是有 128k 的堆栈尺寸限制,常用的还是动态矩阵类型
-
仅变化一个维度的动态矩阵为动态向量
typedef Matrix<float, Dynamic, 1> VectorXf
,使用方法类似
Array类
Array
是类模板,前三个参数必须指定,后三个参数可选。
1 |
|
-
类似于
Matrix
类,Array
默认仍会产生静态数组1
2
3
4
5
6
7Array<int, 3, 3> a;
cout << a;
-->
-412990784 32758 1
32758 31 0
-412995223 0 0 -
一维动态数组为
ArrayX
开头,二维动态数组为ArrayXX
开头1
2
3
4
5
6
7ArrayXXi a(3, 3);
cout << a;
-->
6357107 7471220 4522044
7929971 6422621 6029371
7667805 7209066 7471185
Array 和 Martix 的区别
- Martix 表示的是矩阵,运算为矩阵运算,运算时尺寸需要遵循矩阵运算规则
- Array 和 Matrix 数据组成相同,但运算规则为逐元素运算,需要相同尺寸数据进行运算
Array 和 Martix 的转换
Matrix
对象——>Array
对象:.array()
函数Array
对象——>Matrix
对象:.matrix()
函数
初始化
建议矩阵数据都要初始化,不然是十分危险的。
默认初始化
- 默认初始化为随机数:
1 |
|
赋值初始化
-
赋值初始化
1
2
3
4
5
6
7Matrix<int, 5, 1> b {1, 2, 3, 4, 5};
Matrix<double, 2, 3> b {
{2, 3, 4},
{5, 6, 7},
};
VectorXd a {{1.5, 2.5, 3.5}};
RowVectorXd b {{1.0, 2.0, 3.0, 4.0}};
对象初始化
-
可以用其他对象初始化新的相同内容对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
MatrixXf n(m);
m.block(0, 0, 2, 2).swap(n.block(2, 2, 2, 2));
cout << m;
-->
11 12 3 4
15 16 7 8
9 10 11 12
13 14 15 16
逗号初始化
- 为矩阵元素赋值,顺序是从左到右,从上到下,数目必须匹配。
1 |
|
特殊矩阵初始化
静态矩阵
一下几个函数均为静态矩阵调用的初始化函数,动态矩阵调用会报错:
1
YOU CALLED A_FIXED SIZE METHOD ON A DYNAMIC SIZE MATRIX OR VECTOR
- 零阵:类静态成员函数
Zero()
- 常量矩阵:
Constant(rows, cols, value)
- 随机矩阵:
Random()
- 单位矩阵:
Identity()
1 |
|
动态矩阵
函数 | 含义 |
---|---|
setZero() |
矩阵归零 |
setConstant () |
矩阵归常数 |
setIdentity () |
矩阵归单位阵 |
setOnes () |
矩阵归一 |
setRandom () |
矩阵随机数 |
1 |
|
动态向量函数
- 仅能在向量类型数据中使用
函数 | 含义 |
---|---|
setLinSpaced() |
填充线性间隔的数据 |
setUnit() |
指定向量位置数据置1,其余为0 |
1 |
|
索引数据
单个数据
-
主要数据的存取和修改都是通过重载的括号运算符完成的
-
对于二维矩阵,下标顺序为 row col
-
对于向量,则只有向量下标需要传入
1
2
3
4
5
6
7
8
9
10MatrixXd 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);
cout << m;
-->
3 -1
2.5 1.5
块操作
- 语法:
块 | 动态矩阵 | 静态矩阵 |
---|---|---|
尺寸 (p, q) 左上角坐标 (i, j) | matrix.block(i,j,p,q); |
matrix.block<p,q>(i,j); |
-
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
m.block<2, 2>(0, 0) = m.block<2, 2>(0, 0) * 3;
m.block<2, 2>(2, 2).setConstant(0);
cout << m << endl << endl;
-->
18 21 3 4
30 33 7 8
9 10 0 0
13 14 0 0
行列操作
- 语法:
操作 | 方法 |
---|---|
第 i 行 | matrix.row(i); |
第 i 列 | matrix.col(j); |
-
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20Eigen::MatrixXf m(3, 3);
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
cout << "Here is the matrix m:" << endl << m << endl;
cout << "2nd Row: " << m.row(1) << endl;
m.col(2) += 3 * m.col(0);
cout << "After adding 3 times the first column into the third column, the matrix m is:\n";
cout << m << endl;
-->
Here is the matrix m:
1 2 3
4 5 6
7 8 9
2nd Row: 4 5 6
After adding 3 times the first column into the third column, the matrix m is:
1 2 6
4 5 18
7 8 30
角操作
- 语法:
块操作 | 动态矩阵语法 | 静态矩阵语法 |
---|---|---|
左上角 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); |
-
示例:
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
26Eigen::Matrix4f m;
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10,11,12,
13,14,15,16;
cout << "m.leftCols(2) =" << endl << m.leftCols(2) << endl << endl;
cout << "m.bottomRows<2>() =" << endl << m.bottomRows<2>() << endl << endl;
m.topLeftCorner(1,3) = m.bottomRightCorner(3,1).transpose();
cout << "After assignment, m = " << endl << m << endl;
-->
m.leftCols(2) =
1 2
5 6
9 10
13 14
m.bottomRows<2>() =
9 10 11 12
13 14 15 16
After assignment, m =
8 12 16 4
5 6 7 8
9 10 11 12
13 14 15 16
向量块操作
块操作 | 动态矩阵语法 | 静态矩阵语法 |
---|---|---|
向量前 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:
1
2
3
4
5
6
7
8
9
10int array[9];
for (int i = 0; i < 9; ++i) {
array[i] = i;
}
cout << Map<Matrix3i>(array).transpose() << endl;
-->
0 1 2
3 4 5
6 7 8
Vector 向量操作
操作 | 语法 | 示例 |
---|---|---|
点乘 | dot() |
v.dot(w) |
叉乘 | .cross() |
v.cross(w) |
元素个数 | .size() |
v.size() |
生成对角阵 | .asDiagonal() |
v.asDiagonal() |
向量平方和 | .squaredNorm() |
v.squaredNorm() |
向量模长 | .norm() |
v.norm() |
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() |
-
赋值经过优化:
行可以给列赋值,这个其实还是挺可怕的,需要格外小心。
1
2
3
4
5
6
7
8
9
10
11
12
13Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
m.row(0) = m.col(3);
cout << m;
-->
4 8 12 16
5 6 7 8
9 10 11 12
13 14 15 16 -
列向操作相当于 numpy 中的
axis=1
,只对列方向做某种操作:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
cout << "Here is the matrix m:" << endl << m << endl;
cout << "Here is the sum of each column:" << endl << m.colwise().sum() << endl;
cout << "Here is the maximum absolute value of each column:"
<< endl << m.cwiseAbs().colwise().maxCoeff() << endl;
-->
Here is the matrix m:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
Here is the sum of each column:
28 32 36 40
Here is the maximum absolute value of each column:
13 14 15 16
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>() |
-
最大值、最小值
返回最大、最小值,同时定位位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23Matrix3f m = Matrix3f::Random();
std::ptrdiff_t i, j;
float minOfM = m.minCoeff(&i,&j);
cout << "Here is the matrix m:\n" << m << endl;
cout << "Its minimum coefficient (" << minOfM
<< ") is at position (" << i << "," << j << ")\n\n";
RowVector4i v = RowVector4i::Random();
int maxOfV = v.maxCoeff(&i);
cout << "Here is the vector v: " << v << endl;
cout << "Its maximum coefficient (" << maxOfV
<< ") is at position " << i << endl;
-->
Here is the matrix m:
-0.997497 0.617481 -0.299417
0.127171 0.170019 0.791925
-0.613392 -0.0402539 0.64568
Its minimum coefficient (-0.997497) is at position (0,0)
Here is the vector v: 8080 -10679 11761 6897
Its maximum coefficient (11761) is at position 2
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) |
-
逐元素比较:
1
2
3
4
5
6
7
8
9
10
11
12
13Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
cout << (m.array() > m.reverse().array());
-->
0 0 0 0
0 0 0 0
1 1 1 1
1 1 1 1 -
逐元素选择:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Eigen::MatrixXf m(4, 4);
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;
MatrixXf n(4, 4), p(4, 4);
n.setZero();
p = (m.array() < 10).select(m, n);
cout << p;
-->
1 2 3 4
5 6 7 8
9 0 0 0
0 0 0 0
矩阵分解与解线性方程组
矩阵分解
分解方法 | 语法 | 输出 |
---|---|---|
柯列斯基分解 | .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> |
特征值特征向量
-
特征值:
1
A.eigenvalues();
-
特征向量:
1
eig.eigenvectors();
混淆问题
- 使用
eval()
函数解决把右值赋值为一个临时矩阵,再赋给左值时可能有造成的混淆。如:
1 |
|
- 原地操作的一类函数:
普通函数 | 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/
“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”
微信支付
支付宝支付