0%

归约、迭代器和广播机制

本章介绍Eigen中的归约( Reductions ),迭代器( Visitors ) 和广播( Broadcasting )机制,以及它们是如何应用与矩阵和数组的。

归约

归约指的是一类以矩阵或数组作为输入,返回一个标量值的函数。

常用归约函数

.sum() .prod() .mean() .minCoeff() .maxCoeff() .trace()

范数计算

  • .norm() : L-2 范数(所有系数平方和开根号)
  • .squaredNorm() : L-2 范数的平方(所有系数平方和)
  • .lpNorm<p>() : P范数(所有系数绝对值的p次幂之和的p次根),当 p=Infinity 时表示所有系数绝对值的最大值

逻辑归约函数

  • .all() : Matrix 或 Array 的所有系数均为 true 时返回 true
  • .any() : Matrix 或 Array 存在某个系数为 true 时返回 true
  • .count() : 返回 Matrix 或 Array 中为 true 的系数总个数
1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
ArrayXXf a(2,2);
a << 1,2,
3,4;
cout << "(a > 0).all() = " << (a > 0).all() << endl;
cout << "(a > 0).any() = " << (a > 0).any() << endl;
cout << "(a > 0).count() = " << (a > 0).count() << endl;
cout << endl;
cout << "(a > 2).all() = " << (a > 2).all() << endl;
cout << "(a > 2).any() = " << (a > 2).any() << endl;
cout << "(a > 2).count() = " << (a > 2).count() << endl;
}

输出:

1
2
3
4
5
6
7
(a > 0).all()   = 1
(a > 0).any() = 1
(a > 0).count() = 4

(a > 2).all() = 0
(a > 2).any() = 1
(a > 2).count() = 2

部分归约

部分归约是可以对矩阵或数组按列或行进行操作的归约,对每个列或行应用归约运算,然后返回具有相应值的列或行向量。部分归约由函数 .colwise()rowwise() 实现:

1
2
3
4
5
6
7
8
9
10
int main()
{
Eigen::MatrixXf mat(2,4);
mat << 1, 2, 6, 9,
3, 1, 7, 2;
cout << "Column's maximum: " << endl
<< mat.colwise().maxCoeff() << endl;
cout << "Row's maximum: " << endl
<< mat.rowwise().maxCoeff() << endl;
}

输出:

1
2
3
4
5
Column's maximum: 
3 2 7 9
Row's maximum:
9
7

显然:列操作返回行向量,行操作返回列向量

将部分归约与其他操作结合

例:寻找系数和最大的列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
MatrixXf mat(2,4);
mat << 1, 2, 6, 9,
3, 1, 7, 2;

MatrixXf::Index maxIndex;
float maxNorm = mat.colwise().sum().maxCoeff(&maxIndex);

cout << "Maximum sum at position " << maxIndex << endl;

cout << "The corresponding vector is: " << endl;
cout << mat.col( maxIndex ) << endl;
cout << "And its sum is is: " << maxNorm << endl;
}

输出:

1
2
3
4
5
Maximum sum at position 2
The corresponding vector is:
6
7
And its sum is is: 13

迭代器

最常用的就是利用visitor机制获取矩阵/数组中最大值/最小值的位置,交给一个visitor的参数是指向存储行列位置变量的指针,变量类型为 Index

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main()
{
Eigen::MatrixXf m(2,2);

m << 1, 2,
3, 4;

//get location of maximum
MatrixXf::Index maxRow, maxCol;
float max = m.maxCoeff(&maxRow, &maxCol);

//get location of minimum
MatrixXf::Index minRow, minCol;
float min = m.minCoeff(&minRow, &minCol);

cout << "Max: " << max << ", at: "
<< maxRow << "," << maxCol << endl;
cout << "Min: " << min << ", at: "
<< minRow << "," << minCol << endl;
}

输出:

1
2
Max: 4, at: 1,1
Min: 1, at: 0,0

广播

广播机制类似于部分规约,区别在于广播构造了一个表达式,其中向量被解释成了一个矩阵,这个矩阵由向量在行/列方向上复制自身形成。

加减(+、-、+=、-=)

只能用于Vector类型的对象!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main()
{
Eigen::MatrixXf mat(2,4);
Eigen::VectorXf v(2);
mat << 1, 2, 6, 9,
3, 1, 7, 2;
v << 0,
1;

//add v to each column of m
mat.colwise() += v;

cout << "Broadcasting result: " << endl;
cout << mat << endl;

Eigen::VectorXf w(4);
w << 0,1,2,3;

//add w to each row of m
mat.rowwise() += v.transpose();

cout << "Broadcasting result: " << endl;
cout << mat << endl;
}

输出:

1
2
3
4
5
6
Broadcasting result: 
1 2 6 9
4 2 8 3
Broadcasting result:
1 3 8 12
4 3 10 6

在执行 mat.colwise() += v 时,相当于进行了两步操作,首先将向量 v 在行方向上复制了4次形成了一个新的4x2的矩阵,再将这个矩阵和矩阵 mat 进行加法操作:

只有向量才能进行按行列加减,不能是矩阵,否则会报编译错误;

Array类同理,只能对 ArrayXf 执行这种操作

乘除(、/、\=、/=)

按行/按列执行系数级乘法除法运算。

只能用于 Array 类型的对象!!!

如果想让矩阵第 i 列乘上向量的第 i 个系数,应该写成 mat = mat * v.asDiagonal()

将广播与其他操作结合

例:在矩阵 m 中找到与向量 v 距离最近的列向量

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

using namespace std;
using namespace Eigen;

int main()
{
Eigen::MatrixXf m(2,4);
Eigen::VectorXf v(2);

m << 1, 23, 6, 9,
3, 11, 7, 2;

v << 2,
3;

MatrixXf::Index index;
// find nearest neighbour
(m.colwise() - v).colwise().squaredNorm().minCoeff(&index);

cout << "Nearest neighbour is column " << index << ":" << endl;
cout << m.col(index) << endl;
}

输出:

1
2
3
Nearest neighbour is column 0:
1
3