Eigen入門之密集矩陣 6 - Reductions, visitors and broadcasting

簡介

本文介紹一下Dense Matrix的3中操作: reduction, visitor, broadcasting.

歸約計算reduction.

Eigen的歸約計算是這樣的一類計算,它是對矩陣或者數組進行的計算,並返回一個標量。可能其中最常用的就是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;
}

其結果爲:

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

範式計算 (Norm comptation)

以下列出常用的向量範數和矩陣範數的定義,以及對應的MATLAB的函數:

1、向量範數

  • 1-範數:,即向量元素絕對值之和,matlab調用函數norm(x, 1) 。
  • 2-範數:,Euclid範數(歐幾里得範數,常用計算向量長度),即向量元素絕對值的平方和再開方,matlab調用函數norm(x, 2)。
  • ∞-範數:,即所有向量元素絕對值中的最大值,matlab調用函數norm(x, inf)。
  • -∞-範數:,即所有向量元素絕對值中的最小值,matlab調用函數norm(x, -inf)。
  • p-範數:,即向量元素絕對值的p次方和的1/p次冪,matlab調用函數norm(x, p)。

2、矩陣範數

  • 1-範數:, 列和範數,即所有矩陣列向量絕對值之和的最大值,matlab調用函數norm(A, 1)。
  • 2-範數:,譜範數,即A’A矩陣的最大特徵值的開平方。matlab調用函數norm(x, 2)。
  • ∞-範數:,行和範數,即所有矩陣行向量絕對值之和的最大值,matlab調用函數norm(A, inf)。
  • F-範數:,Frobenius範數,即矩陣元素絕對值的平方和再開平方,matlab調用函數norm(A, ’fro‘)。

在Eigen中,提供了計算範式的一些方法。

  • squaredNorm 向量的平方範式,2-範式,2\ell^2的範式,可以使用方法squaredNorm(),即所有係數的平方之和,數學上是一個向量與自身的點乘。
  • norm 即稱歐拉範式,squaredNorm結果再開平方,得到平方根。

當這些計算作用於矩陣時, 一個 nXp的矩陣,可以當做一個大小爲(n*p)的向量,比如"Frobenius" or "Hilbert-Schmidt" norm

我們避免提矩陣的2\ell^2的範式(歐拉平方範式)運算,這樣會導致歧義。

Eigen也額外提供了一些對係數進行歐拉範式操作方法,如p\ell^p範式,可以使用lpNorm<p>()方法。如果要計算\ell^\infty 範式,模板參數p取特殊值Infinity
即可,這得到係數絕對值的最大值。

示例:

//matrix_norm1.cpp

#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;
  cout << "vector v: "<<endl<<v<<endl;

  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;

  m << 1,-2,
       -3,4;
  cout << "matrix m: "<<endl<<m<<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;
}

執行結果:

$ g++   -I /usr/local/include/eigen3 matrix_norm1.cpp -o matrix_norm1
$ ./matrix_norm1 
vector v: 
-1
 2
v.squaredNorm() = 5
v.norm() = 2.23607
v.lpNorm<1>() = 3
v.lpNorm<Infinity>() = 2

matrix m: 
 1 -2
-3  4
m.squaredNorm() = 30
m.norm() = 5.47723
m.lpNorm<1>() = 10
m.lpNorm<Infinity>() = 4

boolean 歸約

Eigen提供了一些針對Boolean運算的歸約操作。

  • all() : 如果係數全部滿足Boolean計算結果,則結果爲true;否則爲false。
  • any() : 如果係數有任何滿足Boolean計算的結果,則結果爲true;否則爲false。
  • count() : 返回滿足條件的係數的數。

給一個示例:

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

結果:

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

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

訪問子 visitor

訪問子visitor用於訪問矩陣或數組內的係數及係數的位置。最簡單的是前面曾經使用到的maxCoeff(&x,&y) , minCoeff(&x,&y)

示例請看下面的Partial reduction中的例子程序。

Partial reductions 局部歸約

Eigen提供了針對矩陣的列或者行的歸約操作,它們對矩陣的每行/每列進行操作,並返回結果向量。
對應的函數爲colwise() , rowwise()
Note colwise() 返回的是一個行向量,而rowwise()返回的是一個列向量。

示例:

//matrix_norm2.cpp

#include <iostream>
#include <Eigen/Dense>

using namespace std;

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

  std::cout << "Row's maximum: " << std::endl
   << mat.rowwise().maxCoeff() << std::endl;

}

執行:

$ g++   -I /usr/local/include/eigen3 matrix_norm2.cpp -o matrix_norm2
$ ./matrix_norm2
Column's maximum: 
3 2 7 9
Row's maximum: 
9
7

broadcasting

廣播broadcasting比Partial reduction多了一個操作。其把一個向量賦值多次,結合成一個矩陣,可以和以及矩陣進行運算。

下面是一個簡單的示例程序:

//matrix_broadcast.cpp
#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 result1: " << std::endl;
    std::cout << mat << std::endl;

    // -------------
    Eigen::MatrixXf mm(2,2);
    mm<<1,2,3,4;

    v << 1,1;
    mm.rowwise() += v.transpose();

    std::cout << "Broadcasting result2: " << std::endl;
    std::cout << mm << std::endl;

}

執行的情況類似:
[ \begin{bmatrix} 1 & 2 & 6 & 9 \ 3 & 1 & 7 & 2 \end{bmatrix} + \begin{bmatrix} 0 & 0 & 0 & 0 \ 1 & 1 & 1 & 1 \end{bmatrix} = \begin{bmatrix} 1 & 2 & 6 & 9 \ 4 & 2 & 8 & 3 \end{bmatrix}. ]

[ \begin{bmatrix} 1 & 2 \ 3 & 4 \end{bmatrix} + \begin{bmatrix} 1 & 1 \ 1 & 1 \end{bmatrix} = \begin{bmatrix} 2 & 3 \ 4 & 5 \end{bmatrix}. ]

執行結果:

$ g++   -I /usr/local/include/eigen3 matrix_broadcast.cpp -o matrix_broadcast
$
$ ./matrix_broadcast 
Broadcasting result1: 
1 2 6 9
4 2 8 3
Broadcasting result2: 
2 3
4 5

綜合: 結合broadcasting和其他的操作及運算

//matrix_broadcast2.cpp
#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 << "Nearest column vector: " << endl << m.col(index) << endl;
}

  • 計算m.colwise() - v
    [ \begin{bmatrix} 1 & 23 & 6 & 9 \ 3 & 11 & 7 & 2 \end{bmatrix} - \begin{bmatrix} 2 & 2 & 2 & 2 \ 3 & 3 & 3 & 3 \end{bmatrix} = \begin{bmatrix} -1 & 21 & 4 & 7 \ 0 & 8 & 4 & -1 \end{bmatrix}]

  • 計算(m.colwise() - v).colwise().squaredNorm():

(m.colwise() - v).colwise().squaredNorm() = [ \begin{bmatrix} 1 & 505 & 32 & 50 \end{bmatrix} ]

  • minCoeff(&index) 用來獲得在m矩陣內,最小的係數位置索引,該列向量與向量v具有最短歐拉距離。

執行結果:

$ g++   -I /usr/local/include/eigen3 matrix_broadcast2.cpp -o matrix_broadcast2
$ ./matrix_broadcast2
Nearest neighbour is column 0:
Nearest column vector: 
1
3

[ \begin{bmatrix} 4 & 3 & 13 & 11 \ 4 & 3 & 13 & 11\4 & 3 & 13 & 11 \end{bmatrix} ]

[ \begin{bmatrix} 4 & 3 & 13 & 11 \end{bmatrix} ]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章