首页 关于
树枝想去撕裂天空 / 却只戳了几个微小的窟窿 / 它透出天外的光亮 / 人们把它叫做月亮和星星
目录

6. Reductions, visitors和broadcasting

本章介绍Eigen的Reductions, visitors和broadcasting(不知道该怎么翻译了:),以及Matrix和Array如何使用这些功能。

6.1 Reductions

在Eigen中reduction是一种Matrix或者是Array的函数,他们把一个Matrix或者Array映射为一个数。 其中最常用的reduction的操作是.sum(),它将返回Matrix或者Array中所有元素的和。下面的例程中列举了一些常用的reduction操作。

        #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;
	    } 

输出结果如下:

    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

6.1.1 范数运算

函数squaredNorm()可以用来求取一个向量的二范数(\(\ell^2\),Euclidean,欧氏距离),数学上为向量自己对自己的点积, 即向量各个元素的平方和。Eigen还提供.norm(),返回向量二范数的平方根。这一操作,也可以用于矩阵, 对于一个\(m\)行\(n\)列的矩阵可以看做一个具有\(m \times n\)的向量,所以.norm()返回的是"Frobenius"范数。

Eigen还提供了函数lpNorm<p>()来求\(\ell^p\)范数。如果模板参数p是Infinity的话,求取的则是\(\ell^\infty\)范数, 它返回的是所有元素中最大的绝对值。

        #include <iostream>
        #include <Eigen/Dense>
        
        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() = " << v.lpNorm() << 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() = " << m.lpNorm() << endl;
        } 

输出结果如下:

    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

6.1.3 关于逻辑的reduction操作

Eigen提供如下的一些用于逻辑判断的reduction操作:

由于Array的coefficient-wise运算返回的是一个与原Array相同大小的Array,例如array > 0的运算结果就是一个Array, 其中对应于array中正数的元素的值为true。因此我们可以用形如(array > 0).all()的形式判断Array中的所有元素是否全为正数。

        #include <iostream>
        #include <Eigen/Dense>
        
        using namespace std;
        using namespace Eigen;
        int main()
        {
            Array22f a;
            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;

            Matrix2f b;
            b << 0,2,
                3,4;
            cout << "b.all() = " << b.all() << endl;
            cout << "b.any() = " << b.any() << endl;
            cout << "b.count() = " << b.count() << endl;
        } 

输出结果如下:

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

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

    b.all() = 0
    b.any() = 1
    b.count() = 3
    

6.1.4 局部reduction

局部reduction是指对Matrix或者Array按行或者按列进行约减,借助colwise()或者rowwise()实现。 参考如下的代码,需要注意的是按行约减得到的是一个列向量,按列约减得到的是一个行向量。

        #include <iostream>
        #include <Eigen/Dense>
        
        using namespace std;
        using namespace Eigen;
        int main()
        {
            MatrixXf m(2,4);

            m << 1, 2, 6, 9,
                3, 1, 7, 0;

            cout << "Column's maximum: " << endl
                << m.colwise().maxCoeff() << endl;
            cout << "Row's maximum: " << endl
                << m.rowwise().maxCoeff() << endl;
            cout << "Column's all: " << endl
                << m.colwise().all() << endl;
        } 

输出结果如下:

    Column's maximum:
    3 2 7 9
    Row's maximum:
    9
    7
    Column's all:
    1 1 1 0

我们也可以把局部约减的结果拿来做进一步的处理。 下面这个例子可以用来求取元素和最大的列:

        #include <iostream>
        #include <Eigen/Dense>
        
        using namespace std;
        using namespace Eigen;
        int main()
        {
            MatrixXf m(2,4);

            m << 1, 2, 6, 9,
                3, 1, 7, 0;
            MatrixXf::Index maxIndex;
            float maxNorm = m.colwise().sum().maxCoeff(&maxIndex);

            cout << "Maximum sum at column " << maxIndex << endl;
            cout << "The corresponding vector is: " << endl;
            cout << m.col(maxIndex) << endl;
            cout << "and its sum is: " << maxNorm << endl;
        } 

输出结果如下:

    Maximum sum at column 2
    The corresponding vector is: 
    6
    7
    and its sum is: 13

在这个例子中,先通过colwise()访问器按行求取每一列的元素的和得到一个\(1\times4\)的行向量[4, 3, 13, 9]。 然后利用maxCoeff求取其中的最大值及其位置索引。

6.2 visitors

Visitor一般被用来获取Matrix或者Array中元素的位置索引。 一个最简单的例子就是maxCoeff(&x, &y)和minCoeff(&x, &y), 它们在求得Matrix或者Array中的最大和最小元素的同时还将通过x,y返回元素的位置索引。 Visitor的参数是Index类型的指针。

        #include <iostream>
        #include <Eigen/Dense>
        
        using namespace std;
        using namespace Eigen;
        int main()
        {
            MatrixXf m(2,2);

            m << 1, 2,
                3, 4;

            MatrixXf::Index maxRow, maxCol;
            float max = m.maxCoeff(&maxRow, &maxCol);

            MatrixXf::Index minRow, minCol;
            float min = m.minCoeff(&minRow, &minCol);

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

输出结果如下:

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

6.3 Broadcasting

Broadcasting与局部约简的概念类似,按行或者列对Matrix或者Array进行操作。 所不同的是Broadcasting是按行或者列的形式与一个向量进行运算, 局部约简则是对Matrix或者Array自身的运算。

下面是一个简单的例程。把一个矩阵的每一列都与一个向量相加, 把一个Array的每一列的元素都与一个向量中对应位置上的元素相乘。

        #include <iostream>
        #include <Eigen/Dense>
        
        using namespace std;
        using namespace Eigen;
        int main()
        {
            MatrixXf m(2,4);
            VectorXf v(2);
            ArrayXXf a(2,4);

            m << 1, 2, 6, 9,
                 3, 1, 7, 2;
            a << 1, 2, 6, 9,
                 3, 1, 7, 2;
            v << 0,
                 1;

            m.colwise() += v;
            a.colwise() *= v.array();

            cout << "m: " << endl;
            cout << m << endl;
            cout << "a: " << endl;
            cout << a << endl;
        } 

输出结果如下:

    m: 
    1 2 6 9
    4 2 8 3
    a:
    0 0 0 0
    3 1 7 2

对于Array,可以应用运算符[*]和[/],对每一列或者行中的元素分别与向量的对应元素相乘或相除。 但对于Matrix则不可以应用这两个运算符作Broadcasting, 因为Matrix或者Vector本身对于乘除运算有数学上的定义,这样做会产生歧义。

有一点需要注意的是,用于Broadcasting的向量一定是Vector类型的数据,不可以是Matrix。

Broadcasting运算之后也可以直接进行其他形式的运算。 下面这个例子包含了前面介绍的约简以及部分约简的运算, 最终求出一个矩阵中与一向量欧式距离最近的一列。

        #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 i;
            (m.colwise() - v).colwise().squaredNorm().minCoeff(&i);
            cout << "The nearest neighbour is column " << i << ":" << endl;
            cout << m.col(i) << endl;
        } 

输出结果如下:

    The nearest neighbour is column 0:
    1
    3



Copyright @ 高乙超. All Rights Reserved. 京ICP备16033081号-1