原文:Eigen官網-Matrix and vector arithmetic
本節內容主要介紹Eigen中關於矩陣、向量、標量之間的數學運算。
1. 引言
Eigen提供了matrix/vector的運算操作,既包括重載了c++的算術運算符+/-/*,也引入了一些特殊的運算比如點乘dot()、叉乘cross()等。
對於Matrix類(matrix和vectors)這些操作只支持線性代數運算,比如:matrix1*matrix2表示矩陣相乘,vetor+scalar是不允許的。如果你想執行非線性代數操作,請看下一篇(暫時放下)。
2. 加法和減法
左右兩側的變量都必須具有相同的大小(行和列)和數據類型(Eigen不會自動進行類型轉化)。操作符如下:
- 二元運算符+ 如a+b
- 二元運算符- 如a-b
- 一元運算符- 如-a
- 複合運算符+= 如a+=b
- 複合運算符-= 如a-=b
示例如下:
#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
int main()
{
Matrix2d a;
a << 1, 2,
3, 4;
MatrixXd b(2,2);
b << 2, 3,
1, 4;
std::cout << "a + b =\n" << a + b << std::endl;
std::cout << "a - b =\n" << a - b << std::endl;
std::cout << "Doing a += b;" << std::endl;
a += b;
std::cout << "Now a =\n" << a << std::endl;
Vector3d v(1,2,3);
Vector3d w(1,0,0);
std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
}
結果如下:
a + b =
3 5
4 8
a - b =
-1 -1
2 0
Doing a += b;
Now a =
3 5
4 8
-v + w - v =
-1
-4
-6
3. 標量乘法和除法
矩陣或者向量與標量進行乘法或除法操作是比較簡單的。操作符如下:
- 二元運算 * 如matrix*scalar
- 二元運算 * 如scalar*matrix
- 二元運算 / 如matrix/scalar
- 複合運算 *= 如matrix*=scalar
- 複合運算 /= 如matrix/=scalar
4. 轉置和共軛
表示轉置(transpose);
表示共軛(conjugate);
表示共軛轉置(伴隨矩陣)(adjoint)。
上述三個操作分別通過transpose(), conjugate(),以及adjoint()函數實現。
示例如下:
#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
int main()
{
Matrix2d a;
a << 1, 2,
3, 4;
std::cout << "Here is the matrix a\n" << a << std::endl;
std::cout << "Here is the transpose of a\n" << a.transpose() << std::endl;
std::cout << "Here is the conjugate of a\n" << a.conjugate() << std::endl;
std::cout << "Here is the adjoint of a\n" << a.adjoint() << std::endl;
return 0;
}
結果如下:
Here is the matrix a
1 2
3 4
Here is the transpose of a
1 3
2 4
Here is the conjugate of a
1 2
3 4
Here is the adjoint of a
1 3
2 4
對於實數矩陣,conjugate不執行任何操作,adjoint等價於transpose。
transpose和adjoint會簡單的返回一個代理對象並不做真正的轉置。如果執行 b=a.transpose() ,a不變,轉置結果被賦值給b。如果執行 a=a.transpose() Eigen在轉置結束之前結果會開始寫入a,所以a的最終結果不一定等於a的轉置。這就是所謂的別名問題。在“調試模式”中,也就是說,當斷言沒有被禁用時,這種常見的陷阱會被自動檢測到。
對於a = a.transpose()
這種操作,可以使用transposeInPlace()
解決,類似的還有adjointInPlace()
。
示例如下:
#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
int main()
{
Matrix2d a;
a << 1, 2,
3, 4;
std::cout << "Here is the matrix a\n" << a << std::endl;
//a = a.transpose(); //!!!do Not do this!!!在這裏無法編譯通過
//std::cout << "the result of the aliasing effect: \n" << a << std::endl;
Matrix2d b;
b << 1, 2,
3, 4;
b.transposeInPlace();
std::cout << " b after being transposed:\n" << b << std::endl;
return 0;
}
結果如下:
Here is the matrix a
1 2
3 4
b after being transposed:
1 3
2 4
5. 矩陣-矩陣的乘法和矩陣-向量的乘法
因爲向量是一種特殊的矩陣,因此本質上都是矩陣-矩陣的乘法。運算符如下:
- 二元運算* 如a*b
- 複合運算 *= 如a*=b (這種右乘操作,a*=b等價於a=a*b)
m=m*m
並不會導致別名問題,Eigen在這裏做了特殊處理,引入了臨時變量。實質將編譯爲:
tmp = m*m
m = tmp
如果你確定矩陣乘法是安全的(並沒有別名問題),你可以使用noalias()函數來避免臨時變量。
c.noalias() += a*b
6. 點乘和叉乘
dot()執行點積,cross()執行叉積,點運算得到1*1的矩陣。當然,點運算也可以用u.adjoint()*v來代替。
#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
using namespace std;
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;
}
結果如下:
Dot product: 8
Dot product via a matrix product: 8
Cross product:
1
-2
1
注意:點積只對三維vector有效。對於複數,Eigen的點積是第一個變量共軛和第二個變量的線性積。
7. 基礎的歸約操作
Eigen提供了一些歸約函數把一個給定的矩陣或向量化爲一個值,比如
- 求和(sum());
- 累積乘積(prod()),矩陣中所有元素的乘積;
- 平均值(mean()),矩陣中所有元素的平均值;
- 最大值(maxCoeff()),矩陣中所有元素的最大值,也可以返回最大值的位置;
- 最小值(minCoeff()),矩陣中所有元素的最小值,也可以返回最小值的位置;
- 跡(trace()),一個矩陣中對角線元素的和,也可以用a.diagonal().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;
ptrdiff_t i,j;
double minOfMat = mat.minCoeff(&i, &j);
cout << "Its minimum coefficient (" << minOfMat << ") is at position (" << i << "," << j << ")\n\n";
Eigen::RowVector4i v = Eigen::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;
}
結果如下:
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
Its minimum coefficient (1) is at position (0,0)
Here is the vector v: 730547559 -226810938 607950953 640895091
Its maximum coefficient (730547559) is at position 0
8. 操作的有效性
Eigen會檢測執行操作的有效性,在編譯階段Eigen會檢測它們,錯誤信息是繁冗的,但錯誤信息會大寫字母突出,比如:
Matrix3f m;
Vector4f v;
v = m*v; // Compile-time error: YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES
當然動態尺寸的錯誤要在運行時發現,如果在debug模式,assertions會觸發後,程序將崩潰。
MatrixXf m(3,3);
VectorXf v(4);
v = m * v; // Run-time assertion failure here: "invalid matrix product"