这一页介绍了Eigen的reductions,visitors,broadcasting (这三个词翻译后会失去味道,因此保留),以及如何在矩阵和向量中使用。
在Eigen里面,一个reductions,就是传入矩阵或向量,然后返回一个标量的函数。
最常用的一个reductions类型的函数是.sum()
,返回矩阵或是向量的所有元素的和。
#include <iostream>
#include <Eigen/Dense>
using namespace std;
int main()
{
Eigen::Matrix2d mat;
mat << 1, 2,
3, 4;
cout << "Here is mat.sum(): " << mat.sum() << endl;
cout << "Here is mat.prod(): " << mat.prod() << endl;
cout << "Here is mat.mean(): " << mat.mean() << endl;
cout << "Here is mat.minCoeff(): " << mat.minCoeff() << endl;
cout << "Here is mat.maxCoeff(): " << mat.maxCoeff() << endl;
cout << "Here is mat.trace(): " << mat.trace() << endl;
}
// output
Here is mat.sum(): 10
Here is mat.prod(): 24
Here is mat.mean(): 2.5
Here is mat.minCoeff(): 1
Here is mat.maxCoeff(): 4
Here is mat.trace(): 5
trace()
返回矩阵的迹,即矩阵的对角元素的和,这个和a.diagonal().sum()
是等价的。
译者加:关于范数可以参见:向量范数和矩阵范数
一个向量的2-范数(欧式距离)的平方可以通过squaredNorm()
计算得到。
它等价于向量自己和自己的点积,也就是各个元素平方和。
Eigen也提供了norm()
方法,获取squaredNorm()
返回值的平方根。
这些操作也可以对矩阵进行操作,比如,一个n-p矩阵可以被当作大小为n*p
的向量,
调用norm()
得到Frobenius或Hilbert-Schmidt范数。这里我们避免使用2-范数,
因为这两个是不同的概念。
如果你想到得到基于coefficient-wise的范数,可以使用lpNorm<p>()
方法。当是无穷范数的时候,
p可以是Infinity
,即获取元素中绝对值最大的值。
下面给出了示例:
#include <Eigen/Dense>
#include <iostream>
using namespace std;
using namespace Eigen;
int main()
{
VectorXf v(2);
MatrixXf m(2,2), n(2,2);
v << -1, 2;
m << 1, -2,
-3, 4;
cout << "v.squaredNorm() = " << v.squaredNorm() << endl;
cout << "v.norm() = " << v.norm() << endl;
cout << "v.lpNorm<1>() = " << v.lpNorm<1>() << endl;
cout << "v.lpNorm<Infinity>() = " << v.lpNorm<Infinity>() << endl;
cout << endl;
cout << "m.squaredNorm() = " << m.squaredNorm() << endl;
cout << "m.norm() = " << m.norm() << endl;
cout << "m.lpNorm<1>() = " << m.lpNorm<1>() << endl;
cout << "m.lpNorm<Infinity>() = " << m.lpNorm<Infinity>() << endl;
}
// output
v.squaredNorm() = 5
v.norm() = 2.23607
v.lpNorm<1>() = 3
v.lpNorm<Infinity>() = 2
m.squaredNorm() = 30
m.norm() = 5.47723
m.lpNorm<1>() = 10
m.lpNorm<Infinity>() = 4
关于矩阵1范数,无穷范数 可以通过下面的方式计算得到:
#include <Eigen/Dense>
#include <iostream>
using namespace std;
using namespace Eigen;
int main()
{
MatrixXf m(2,2);
m << 1,-2,
-3,4;
cout << "1-norm(m) = " << m.cwiseAbs().colwise().sum().maxCoeff() <<
" == " << m.colwise().lpNorm<1>().maxCoeff() << endl;
cout << "infy-norm(m) = " << m.cwiseAbs().rowwise().sum().maxCoeff() <<
" == " << m.rowwise().lpNorm<1>().maxCoeff() << endl;
}
// output
1-norm(m) = 6 == 6
infty-norm(m) = 7 == 7
下面会有更多表达式格式相关的介绍。
下面的reduction操作是针对布尔的。
all()
如果矩阵或数组中的所有元素都可以评估为true,那么返回trueany()
如果矩阵或数组中有一个元素可以评估为true,那么返回truecount()
返回矩阵或数组中评估为true的元素的个数
这些操作通常与数组提供的coefficient-wise的比较操作和相等比较操作同时使用。如,
array > 0
那么数组中所有大于零的元素的位置会被设置成true。因此,
(array > 0).all()
用来检测数组中是不是所有元素都大于零。见如下例子:
#include <Eigen/Dense>
#include <iostream>
using namespace std;
using namespace Eigen;
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;
}
// output
(a > 0).all() = 1
(a > 0).any() = 1
(a > 0).count() = 4
(a > 2).all() = 0
(a > 2).any() = 1
(a > 2).count() = 2<Paste>
TODO
在此期间可以看下DenseBase::redux
函数。
visitors可用获取元素在数组或矩阵中的位置。最简单的使用方式是:maxCoeff(&x,&y)
和minCoeff(&x,&y)
,这两个函数是用来找到矩阵或数组中最大元素和最小元素的位置。
传递给visitor的参数,存储行列位置的变量的指针。这些变量的类型应该是Index
,如下:
#include <Eigen/Dense>
#include <iostream>
using namespace std;
using namespace Eigen;
int main()
{
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;
}
// output
Max: 4, at: 1,1
Min: 1, at: 0,0
同时两个函数也分别返回了最大值和最小值。
部分reductions是针对矩阵或向量的行或列进行reductions操作,并且返回一个行或列向量。
部分reductions和colwise()
或者rowwise()
一起配合使用。
一个简单的获取矩阵每一列最大值元素,并将结果存储到一个行向量的例子如下:
#include <iostream>
#include <Eigen/Dense>
using namespace std;
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;
}
// output
Column's maximum:
3 2 7 9
rowwise()
的例子如下:
#include <iostream>
#include <Eigen/Dense>
using namespace std;
int main()
{
Eigen::MatrixXf mat(2,4);
mat << 1,2,6,9,
3,1,7,2;
cout << "Row's maximum: " << endl
<< mat.rowwise().maxCoeff() << endl;
}
// output
Row's maximum:
9
7
需要注意的是,column-wise
操作返回的是行向量,而row-wise
返回的是列向量。
这里也可以将部分reductions的结果进行进一步处理。下面是找到矩阵中列方向的元素和最大的列:
#include <Eigen/Dense>
#include <iostream>
using namespace std;
using namespace Eigen;
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: " << maxNorm << endl;
}
// output
Maximum sum at position 2
The corresponding vector is:
6
7
And its sum is: 13
前面的例子,使用colwise(),然后再使用sum(),进行降维,得到一个1x4大小的新矩阵。
因此,如果
那么:
maxCoeff()
就能够获取列和最大的位置。
broadcasting背后的概念和部分reductions比较类似,不同的地方是, broadcasting将一个向量(行向量或列向量)通过一个方向上的扩展构造成了一个矩阵。
一个简单的例子是将某个列向量加到矩阵的每一列上。如下:
#include <iostream>
#include <Eigen/Dense>
using namespace std;
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;
std::cout << "Broadcasting result: " << std::endl;
std::cout << mat << std::endl;
}
// output
Broadcasting result:
1 2 6 9
4 2 8 3
我们可以将mat.colwise() += v
这个指令,用两种方式进行理解。一种是,它将vector v加到矩阵的每一列。
另一种理解是,先将向量v在行方向上进行扩展,形成2x4的矩阵,然后在进行求和:
-=
,+
和-
操作符一样可以使用到column-wise和row-wise。针对数组,我们还可以使用
*=
,/=
,*
用来执行coefficient-wise的column-wise或row-wise的乘法和除法操作。
这些操作在矩阵上面是不适用的,因为矩阵上这样的操作对矩阵而言是不清晰的。
如果你想要将v(0)和mat矩阵的第0列相乘,v(1)和mat矩阵的第一列相乘,等等,那么可以使用mat = mat * v.asDiagonal()
。
值得注意的是能够被按column-wise或row-wise进行相加的必定是向量类型,而不能是矩阵。 如果使用矩阵,那么会得到编译器错误。这也意味这,broadcasting操作仅仅能用在向量而不能用在矩阵。 对于Array类,也是一样的,只能够用在ArrayXf,而不能用在多维数组。
对于行的扩展如下:
#include <iostream>
#include <Eigen/Dense>
using namespace std;
int main()
{
Eigen::MatrixXf mat(2,4);
Eigen::VectorXf v(4);
mat << 1,2,6,9,
3,1,7,2;
v << 0,1,2,3;
mat.rowwise() += v.transpose();
cout << "Broadcasting result: " << endl;
cout << mat << endl;
}
// output
Broadcasting result:
1 3 8 12
3 2 9 5
broadcasting操作也一样可以和其他操作配合使用,如Matrix或Array的操作, reductions或是部分reductions。
到目前为止,broadcasting,reductions和部分reductions都已经进行了介绍,下面给出一个
更加高阶的例子,寻找矩阵的各个列中和向量v最接近的列。此处,会使用欧几里德距离,
使用部分reduction函数squaredNorm()
进行计算欧式距离的平方。
#include <iostream>
#include <Eigen/Dense>
using namespace std;
using namespace Eigen;
int main()
{
MatrixXf m(2,4);
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;
}
主要完成任务的代码为:
(m.colwise() - v).colwise().squaredNorm().minCoeff(&index);
我们按照以下步骤对其进行理解:
- m.colwise() - v 是一个broadcasting操作,从m每一列中将v减掉。 得到的新的矩阵和原来的矩阵m有相同的大小:
- (m.colwise() - v).colwise().squaredNorm() 是一个局部reduction,用来计算列方向上的和的平方。 得到的结果是行向量,每个元素表示m的列和v之间的欧几里德距离的平方。
- 最后,minCoeff(&index)用来获取距离最小的列。