0%

向量矩阵运算

这章介绍Eigen中矩阵、向量和标量之间的运算。对Matrix类,运算符重载仅支持线性代数运算,例如矩阵x矩阵,而像向量+标量的操作是不允许的。Array类则允许执行所有的数组操作,见 The Array Class and Coefficient-wise Operations

加减法

Eigen不会自动执行类型转换,所以左右维度和数值类型必须保证相同。可用的操作符包括:

  • 一元操作符 - : -a
  • 二元操作符 + - : a+b, a-b
  • 复合操作符 += -= : a+=b, a-=b

标量乘除

矩阵所有系数乘/除以该标量。可用操作符包括:

  • 二元操作符 * : matrix scalar, scalar matrix
  • 二元操作符 / : matrix / scalar
  • 复合操作符 *= /= : matrix *= scalar, matrix /= scalar

转置与共轭

  • transpose() : $\boldsymbol{a}^T$ (转置)
  • conjugate() : $\overline{\boldsymbol{a}}$ (共轭)
  • adjoint() : $\boldsymbol{a}^*$ (共轭转置)

显然,对实矩阵而言,conjugate() 并不执行什么操作,所以 adjoint() 完全等同于 transpose()

需要非常注意的一点是,基本运算符 transpose()adjoint() 仅仅返回一个代理对象,而并未进行实质的运算。例如,执行 b = a.transpose() 时,Eigen在将结果写入b的同时执行转置;从而引出另一个问题,如果执行 a = a.transpose() ,Eigen同样会在转置完成前开始将结果写入矩阵a,因此这句话不会像预期一样将a变为它的转置,反而会在执行过程中得到 aliasing issue

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
#include <iostream>
#include <eigen3/Eigen/Eigen>

using namespace std;

int main()
{
Eigen::Matrix3i a;
Eigen::Matrix3i b;
a << 1, 2, 3,
4, 5, 6,
7, 8, 9;
cout << "origin matrix a:\n"
<< a << endl;

b = a.transpose();
cout << "b as the transpose of matrix a:\n"
<< b << endl;

a = a.transpose(); // !!! DO NOT DO THIS !!!
cout << "Here is the result of the aliasing effect:\n"
<< a << endl;

return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
origin matrix a:
1 2 3
4 5 6
7 8 9
b as the transpose of matrix a:
1 4 7
2 5 8
3 6 9
main: /usr/include/eigen3/Eigen/src/Core/Transpose.h:378: static void Eigen::internal::checkTransposeAliasing_impl<Derived, OtherDerived, MightHaveTransposeAliasing>::run(const Derived&, const OtherDerived&) [with Derived = Eigen::Matrix<int, 3, 3>; OtherDerived = Eigen::Transpose<Eigen::Matrix<int, 3, 3> >; bool MightHaveTransposeAliasing = true]: Assertion `(!check_transpose_aliasing_run_time_selector <typename Derived::Scalar,blas_traits<Derived>::IsTransposed,OtherDerived> ::run(extract_data(dst), other)) && "aliasing detected during transposition, use transposeInPlace() " "or evaluate the rhs into a temporary using .eval()"' failed.
Aborted (core dumped)

所谓混叠问题,即 aliasing issue ,指的是在赋值表达式的左右两边存在矩阵的重叠区域,这种情况下,有可能得到非预期的结果,具体见 Aliasing

如果想执行就地转置(in-place transposition),调用 transposeInPlace() 方法:

1
2
3
a.transposeInPlace();
cout << "After being transposed:\n"
<< a << endl;

输出:

1
2
3
4
After being transposed:
1 4 7
2 5 8
3 6 9

同理,也有为复矩阵准备的就地共轭转置 adjointInPlace() 方法

矩阵-矩阵乘法乘法

向量是一种特殊的矩阵,因此矩阵-向量、向量-向量乘法也包括其中。可用操作符有:

  • 二元操作符 * : a*b
  • 复合操作符 *= : a*=b

执行操作 m = m * m 是安全的,因为Eigen将矩阵乘法当作一种特殊形式进行处理,会在编译时引入一个临时变量,所以不必担心会出现 aliasing issue

1
2
tmp = m * m;
m = tmp;

当然,如果你很清楚自己的矩阵乘法不会有引起 aliasing issue 的可能,也可以调用 noalias() 方法来避免引入临时变量:

1
c.noalias() += a*b;

向量点乘和叉乘

dot()corss() 方法分别实现向量点乘和叉乘,当然,点乘也可以通过 u.adjoint()*v 实现,只不过返回的结果是个1x1的矩阵:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <eigen3/Eigen/Eigen>

using namespace std;
using namespace Eigen;
int main()
{
Vector3d v(1, 2, 3);
Vector3d w(0, 1, 2);

cout << "Dot product: " << v.dot(w) << endl;
double dp = v.adjoint() * w; // automatic conversion of the inner product to a scalar
cout << "Dot product via a matrix product: " << dp << endl;
cout << "Cross product:\n"
<< v.cross(w) << endl;
return 0;
}

输出:

1
2
3
4
5
6
Dot product: 8
Dot product via a matrix product: 8
Cross product:
1
-2
1

需要非常注意的是,叉乘只能用于大小为3的向量 ;点乘则可用于任意大小的向量

基本算数归约运算

Eigen提供了一些归约运算(reduction operation),将给定的矩阵或向量归约为单个值,例如 sum() , prod() , maxCoeff() , minCoeff() , trace() 分别表示所有矩阵系数的和、积、最大值、最小值,以及矩阵的迹,矩阵的迹也可以通过 m.diagonal().sum() 求得。

通过向 maxCoeff()minCoeff() 中传入 std::ptrdiff_t 类型的参数指针,可以得到对应最大值/最小值的坐标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <eigen3/Eigen/Eigen>

using namespace std;
using namespace Eigen;
int main()
{
Matrix3f 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;
return 0;
}

输出:

1
2
3
4
5
6
7
8
Here is the matrix m:
0.680375 0.59688 -0.329554
-0.211234 0.823295 0.536459
0.566198 -0.604897 -0.444451
Its minimum coefficient (-0.604897) is at position (2,1)

Here is the vector v: 115899597 -48539462 276748203 -290373134
Its maximum coefficient (276748203) is at position 2

操作有效性

Eigen会在执行操作的时候检查有效性。

重要的编译错误信息会用大写突出出来。

但更多时候(例如检查动态大小的矩阵的操作),编译过程不会检查出错误。Eigen将在 debug mode 下使用 runtime assertions 让程序在运行过程中碰到非法操作的时候退出,同时报出错误信息。如果关掉assertions,程序很有可能会崩溃。