Eigen : The Matrix Class
Eigen庫 矩陣類的使用:
(本文根據官方文件加上自己的一些理解修改製成)
首先關於Matrix的定義: Matrix<typename scalar, int RowsAtCompileTime, int ColsAtCompileTime> 這是常用的模板,一般為三個引數,也就是Matrix<矩陣變數型別,行數,列數>.
完整的可選模板引數定義如下
Matrix<typename Scalar, //標量型別
int RowsAtCompileTime, //行數
int ColsAtCompileTime,//列數
int Options = 0,//位域
int MaxRowsAtCompileTime = RowsAtCompileTime,//最大行數
int MaxColsAtCompileTime = ColsAtCompileTime> //最大列數
使用Eigen庫定義一個矩陣: Matrix<int,2,2> 這是一個int型別的2x2矩陣
Matrix<float,3,3>這是一個float型別的3x3矩陣
Eigen庫中簡化了部分以上定義的過程,一般用於固定尺寸的矩陣的定義。
例如: Matrix2f,Matrix3f,Matrix4f float型別的2x2,3x3,4x4的矩陣(可見結尾部分為f)
同理Matrix2d,Matrix3d,Matrix4d,Matrix2i,Matrix3i,Matrix4i,只需要看最後結尾的字母,即可明白變數型別(double,int)
Eigen還有個常用的概念,向量,向量是矩陣中的特殊情況,表現為有一行或一列,也就是行向量和列向量,最常見的情況是隻有一列,表現方式有:
typedef Matrix<float, 3, 1> Vector3f; //列向量
typedef Matrix<int, 1, 2> RowVector2i; //行向量
接下來說明它們各自的賦值方式以及特殊情況:
靜態矩陣下有如下的初始化方式: Vector2i myvector(1,2); 表示初始化一個列向量為或者 Vector2f myvector(1.0,2.0); 表示初始化一個列向量為
向量能這樣初始化,但Matrix就不行了,比如Matrix3f a(3,3)雖然編譯能夠通過,不會報錯,但是這樣的傳遞引數是無效的, 在官方文件有解釋:In order to offer a uniform API across fixed-size and dynamic-size matrices, it is legal to use these constructors on fixed-size matrices, even if passing the sizes is useless in this case. 意思是:為了在固定大小和動態大小的矩陣上提供統一的API,在固定大小的矩陣上使用這些建構函式是合法的,即使在這種情況下傳遞大小是無用的。
所以只能使用規定的輸入方法: Matrix3f a;
a<<1,2,3,
4,5,6,
7,8,9;
說完靜態矩陣,接下來說動態矩陣:typedef Matrix <double,Dynamic,Dynamic> MatrixXd;
看如下對比即可明白:
一般Matrix4f mymatrix; 等價於 float mymatrix[16];
一般MatrixXf mymatrix(rows,columns); 等價於 float *mymatrix = new float[rows*columns];
所以有 MatrixXf a(2,2) 即定義一個2x2的矩陣,而且Eigen庫有訪問矩陣中值的特定方式。
如,
訪問第一行第一個元素 即 a(0,1) ,也可 a(0,1) = x 為第一行第一個元素賦值。
如:
#include <iostream> #include <Eigen/Dense> using namespace Eigen; int main() { MatrixXd m(2,2); m(0,0) = 3; m(1,0) = 2.5; m(0,1) = -1; m(1,1) = m(1,0) + m(0,1); std::cout << "Here is the matrix m:\n" << m << std::endl; VectorXd v(2); v(0) = 4; v(1) = v(0) - 1; std::cout << "Here is the vector v:\n" << v << std::endl; }
Output:
Here is the matrix m: 3 -1 2.5 1.5 Here is the vector v: 4 3
像這類動態分配大小的矩陣,有 resize()方法,可以重新定義大小,這篇程式碼寫的很清楚:
#include <iostream> #include <Eigen/Dense> using namespace Eigen; int main() { MatrixXd m(2,5); m.resize(4,3); std::cout << "The matrix m is of size " << m.rows() << "x" << m.cols() << std::endl; std::cout << "It has " << m.size() << " coefficients" << std::endl; VectorXd v(2); v.resize(5); std::cout << "The vector v is of size " << v.size() << std::endl; std::cout << "As a matrix, v is of size " << v.rows() << "x" << v.cols() << std::endl; }
Output:
The matrix m is of size 4x3 It has 12 coefficients The vector v is of size 5 As a matrix, v is of size 5x1
然後說明一下固定尺寸與動態尺寸的選擇問題,也就是什麼時候靜態分配最好,什麼時候動態分配最好。
對於非常小的尺寸,儘可能使用Fixed(一般定義為小於等於16),這種情況下使用Fixed尺寸效能更加優越,因為它可以讓Eigen避免動態記憶體分配和展開迴圈,對於較大尺寸的話就使用dynamic尺寸,
當然,使用固定大小的侷限性在於,只有在編譯時知道大小時才有可能。另外,對於足夠大的尺寸,比如說大於或等於32的尺寸,使用固定尺寸的效能優勢就變得微不足道了。更糟糕的是,如果試圖在函式中使用固定大小建立一個非常大 的矩陣,可能會導致堆疊溢位,因為Eigen會嘗試將陣列作為一個區域性變數自動分配,而這通常是在堆疊中完成的。最後,根據不同的情況,當使用動態大小時,Eigen還可以更積極地嘗試向量化(使用SIMD指令)。
最後解釋一下上面說過的可選引數模板
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
從引數的定義名即可看出來其大概意思。
分析:Matrix<float,Dynamic,Dynamic,0,3,4> 一個不知道大小,但最大行數為3,最大列數為4的矩陣。
從官方文件對於Matrix Optional template parameters 的定義來看,這是一個確定了上限的固定記憶體矩陣,如果在編譯時不知道矩陣的確切大小,但在編譯時可以知道一個固定的上限,這樣做可以避免動態記憶體分配。
第三個引數是什麼意思呢,在我看來是一個優先順序的引數,比如一個矩陣是行優先還是列優先就是使用這個引數,0一般是預設值,不設定任何優先。
比如:Matrix<float,3,3,RowMajor> 就是一個行優先的矩陣。
補充Eigen便利型別定義:
Eigendefines the followingMatrixtypedefs:
- MatrixNt for Matrix<type, N, N>. For example, MatrixXi for Matrix<int, Dynamic, Dynamic>.
- VectorNt for Matrix<type, N, 1>. For example, Vector2f for Matrix<float, 2, 1>.
- RowVectorNt for Matrix<type, 1, N>. For example, RowVector3d for Matrix<double, 1, 3>.
Where:
- N can be any one of
2
,3
,4
, orX
(meaningDynamic
). - t can be any one of
i
(meaning int),f
(meaning float),d
(meaning double),cf
(meaning complex<float>), orcd
(meaning complex<double>). The fact that typedefs are only defined for these five types doesn't mean that they are the only supported scalar types. For example, all standard integer types are supported, seeScalar types.