在Eigen中,所有矩阵和向量都是Matrix模板类的对象。向量只是行数或者列数为1
的特殊矩阵。
template<typename Scalar_, int Rows_, int Cols_, int Options_, int MaxRows_, int MaxCols_>
class Eigen::Matrix< Scalar_, Rows_, Cols_, Options_, MaxRows_, MaxCols_ >
Matrix前三个模板参数
Matrix类有6
个模板参数,但现在了解前3个参数就足够了,剩下的3个参数有默认值,以后再讨论他们。
Matrix类3个强制模板参数是:
Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
Scalar
是标量的类型,例如系数的类型。如果你想要一个float
类型的矩阵,那么你可以在这里填写float
。有关所有支持的标量类型以及如何添加新类型,请参阅标量类型。RowsAtCompileTime
和ColsAtCompileTime
是矩阵的行数和列数,这是在编译时就知道的。如果在编译时不知道矩阵大小,请参阅下文“动态的特殊值”一节。
我们提供了很多常用的类型。例如,Matrix4f
是4*4
的float
矩阵,这在Eigen中定义为:
typedef Matrix<float, 4, 4> Matrix4f;
下文会讨论这些常用类型的定义。
向量
如上所述,在Eigen中,向量是一种特殊的矩阵,把只有1
列的矩阵叫做列向量
,通常把列向量称为向量
。行数为1
的矩阵叫做行向量
。
例如,Vector3f
是有3个float
元素的列向量。在Eigen中这样定义:
typedef Matrix<float, 3, 1> Vector3f;
也提供了行向量的类型定义,如下表示有2个int
元素的行向量:
typedef Matrix<int, 1, 2> RowVector2i;
动态的特殊值
当然,Eigen不局限于处理编译时维度已知的矩阵。模板参数RowsAtCompileTime
和ColsAtCompileTime
可以是一个动态值,这表明在编译时矩阵大小是未知的,必须当作运行时的变量进行处理。在Eigen的术语中,这叫做动态大小;在编译期间就知道大小叫做固定大小。例如,MatrixXd
是double
类型的动态大小矩阵,定义如下:
typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
类似的,定义int
类型的动态大小向量VectorXi
如下:
typedef Matrix<int, Dynamic, 1> VectorXi;
也可以定义一个固定行数和一个动态列数的矩阵,如下:
Matrix<float, 3, Dynamic>
构造函数
默认的构造函数始终可用,它不执行任何动态内存分配,也不初始化矩阵元素。例如:
Matrix3f a;
MatrixXf b;
a
是一个3*3
的矩阵,实际上是一个float[9]
的数组,而且没有初始化。b
是一个动态大小的矩阵,它的初始大小是0*0,而且没有开辟内存。
也有指定大小的构造函数。对于矩阵来说,第一个参数总是行,第二个参数是列,对于向量来说,只需要传入向量的大小即可。构造函数按照给定的大小开辟内存,但不会初始化内存。
MatrixXf a(10,15);
VectorXf b(30);
a
是一个动态大小为10*15
的矩阵,开辟了内存但是没有初始化。b
是一个动态大小为30
的向量,同样开辟了内存但是没有初始化。
为了提供动态大小和固定大小矩阵统一的API,可以向固定大小矩阵的构造函数传递大小,虽然这是无效的,但是合法的:
Matrix3f a(3,3);
这并没有任何操作。
Matrix3f
的定义是typedef Matrix<float, 3, 3> Matrix3f;
,如果传入构造函数的大小不符合该定义就会报错,如Matrix3f a(2, 3);
矩阵和向量也可以从列表初始化。在C++11之前,此功能仅限于固定大小且不超过 4
的向量,如:
Vector2d a(5.0, 6.0);
Vector3d b(5.0, 6.0, 7.0);
Vector4d c(5.0, 6.0, 7.0, 8.0);
如果使用C++11编译,则可以通过列表来初始化任意固定大小的列或行向量,如:
Vector2i a(1, 2); // 包含 {1, 2} 的列向量
Matrix<int, 5, 1> b {
1, 2, 3, 4, 5}; // 包含 {1, 2, 3, 4, 5} 的行向量
Matrix<int, 1, 5> c = {
1, 2, 3, 4, 5}; // 包含 {1, 2, 3, 4, 5} 的列向量
初始化固定大小或者运行时大小固定的矩阵和向量时,必须将元素按照行进行分组,编译器会使用该分组一行一行的初始化矩阵,如下:
MatrixXi a {
// 构造一个 2*2 的矩阵
{
1, 2}, // 第一行
{
3, 4} // 第二行
};
Matrix<double, 2, 3> b {
// 构造一个 2*3 的矩阵
{
2, 3, 4},
{
5, 6, 7},
};
对于列或者行向量,允许隐式的转置。这意味着可以使用一个行分组对一个列向量初始化。
VectorXd a {
{
1.5, 2.5, 3.5}}; // 包含3个元素的列向量,行向量隐式转换成列向量
RowVectorXd b {
{
1.0, 2.0, 3.0, 4.0}}; // 包含4个元素的行向量,本来就是行向量
访问元素
在Eigen中主要的元素访问与修改方法是重载括号运算符。对于矩阵,行索引总是优先传递的。对于向量,只需要传递一个索引,索引从0开始。如下:
#include <iostream>
#include <Eigen/Dense>
int main()
{
Eigen::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;
Eigen::VectorXd v(2);
v(0) = 4;
v(1) = v(0) - 1;
std::cout << "Here is the vector v:\n" << v << std::endl;
}
输出:
Here is the matrix m:
3 -1
2.5 1.5
Here is the vector v:
4
3
请注意 m(index)
不只用于向量,也可用于普通矩阵,这意味在元素数组中访问是基于索引的。但是,索引取决于矩阵的存储顺序。在Eigen中,矩阵默认列优先进行存储,也可以更改为行优先,详情查阅 存储顺序。
运算符[]
同样也被重载用于基于索引的向量访问,但C++不允许运算符[]
传入多个参数。所以Eigen限制了运算符[]
只能用于vector
,因为C++语言的笨拙,会把 matrix[i,j]
编译成 matrix[j]
。
逗号初始化
可以使用所谓的逗号初始化
语法初始化矩阵和向量。如下:
Matrix3f m;
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
std::cout << m;
输出:
1 2 3
4 5 6
7 8 9
重置大小
矩阵的当前大小可以通过 rows()、cols() 和 size() 来检索。这些方法分别返回行数、列数和元素个数。调整动态大小矩阵的大小可以使用 resize() 方法。
#include <iostream>
#include <Eigen/Dense>
int main()
{
Eigen::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;
Eigen::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;
}
输出:
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
如果矩阵的大小没有改变,那么 resize()
方法是空操作。否则,该方法会破坏当前的矩阵,矩阵的元素可能会改变。如果你不想改变矩阵的系数,可以使用resize()的变体 conservativeResize()。
这里进一步解释一下,如果矩阵的大小没有改变,
resize()
不执行内存分配并且保持矩阵元素值不变,如果矩阵的大小改变了(行数、列数、元素数至少任意一个改变) ,数据被重新分配并且丢失矩阵所有值初始化为0,如下:#include <iostream> #include <Eigen/Dense> int main() { Eigen::MatrixXd m{ { 1,2,3,4},{ 5,6,7,8},{ 9,10,11,12},{ 13,14,15,16}}; std::cout << "Before resize, the matrix m is: \n" << m << std::endl; m.resize(3, 4); std::cout << "After resize, the matrix m is: \n" << m << std::endl; }
输出:
Before resize, the matrix m is: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 After resize, the matrix m is: 0 0 0 0 0 0 0 0 0 0 0 0
为了 API 统一,所有这些方法在固定大小的矩阵上仍然可用。当然,实际上无法调整固定大小的矩阵。尝试将固定大小更改为实际不同的值将触发断言失败,但代码在语法上是合法的,如下:
#include <iostream>
#include <Eigen/Dense>
int main()
{
Eigen::Matrix4d m;
m.resize(4,4); // no operation
std::cout << "The matrix m is of size "
<< m.rows() << "x" << m.cols() << std::endl;
}
输出:
The matrix m is of size 4x4
赋值和重置大小
赋值是使用操作符 =
将一个矩阵复制到另一个矩阵的操作。Eigen会自动调整 =
左边的矩阵大小以便与和 =
右边的矩阵大小相匹配,例如:
MatrixXf a(2,2);
std::cout << "a is of size " << a.rows() << "x" << a.cols() << std::endl;
MatrixXf b(3,3);
a = b;
std::cout << "a is now of size " << a.rows() << "x" << a.cols() << std::endl;
输出:
a is of size 2x2
a is now of size 3x3
当然,如果 =
左边的矩阵大小是固定大小,重置大小是不被允许的。
如果不想这种自动调整大小的操作发生,可以禁用它,详情参考this page。
固定大小与动态大小
什么时候应该使用固定大小(例如Matrix4f
),什么时候应该使用动态大小(例如MatrixXf
)?
简单的答案是:尽可能对非常小的矩阵使用固定大小,对较大的矩阵必须使用动态尺寸。对于小矩阵,尤其是小于(大约)16
的矩阵,使用固定尺寸对性能非常有利,因为它允许Eigen避免动态内存分配和展开循环。在内部,固定大小的Eigen矩阵是一个普通数组,例如:
Matrix4f mymatrix
等价于
float mymatrix[16]
,
所以这没有没有运行成本。而动态矩阵需要在堆中分配空间,例如:
MatrixXf mymatrix(rows,columns)
等价于
float *mymatrix = new float[rows*columns]
,
除此之外,MatrixXf
对象还存储了其行数和列数。
使用固定大小的限制就是要求在编译的时候就知道大小,而且对于大矩阵,例如大于32
的矩阵,使用固定大小的矩阵带来的优势可以忽略不计。更糟糕的是,试图在函数内部使用固定大小的矩阵创建一个非常大的矩阵可能会导致栈溢出,因为Eigen会尝试将数组自动分配为局部变量,而这通常是在栈上完成的。最后,根据情况,当使用动态大小时, Eigen也可以更积极地尝试向量化(使用 SIMD 指令),请参阅矢量化。
可选模板参数
在开始的时候,我们提及Matrix
类有6个模板参数,但是,目前只讨论了前三个,剩下的三个参数是可供选择的。下面是完整的模板参数:
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
Options
是一个位域。这里以RowMajor
为例,它指定了矩阵按照行优先进行存储。默认情况下,存储顺序是按照列存储的。例如Matrix<float, 3, 3, RowMajor>
是指一个行优先的3*3
矩阵。Options
是一个枚举,具体见 Eigen::StorageOptions 。MaxRowsAtCompileTime
和MaxColsAtCompileTime
是矩阵大小的上界,这让即使在编译时不知道矩阵的确切大小,但已知固定的上限,可以避免动态内存分配。例如,Matrix<float, Dynamic, Dynamic, 0, 3, 4>
会在编译时使用一个大小为12
的float
类型的数组,而不需要动态分配内存。
其他常用Matrix类型
类型 | 类型别名 | 示例 |
---|---|---|
Matrix | MatrixNt | MatrixXi -> Matrix |
Matrix | MatrixXNt | MatrixX3i -> Matrix |
Matrix | MatrixNXt | Matrix4Xd -> Matrix |
Matrix | VectorNt | Vector2f -> Matrix |
Matrix | RowVectorNt | RowVector3d -> Matrix |
N
可以是2
,3
,4
或X
(意思是Dynamic
) 中的任何一个。t
可以是i(int)
、f(float)
、d(double)
、cf(complex<float>)
或cd(complex<double>)
中的任何一个。虽然这里只定义了五种类型,但并不意味只支持这五种。例如,还支持所有标准整数类型,请参阅标量类型。