在Eigen中,所有矩阵和向量都是矩阵模板类的对象。向量只是矩阵的一种特殊情况,要么有一行,要么有一列。矩阵就是一个二维数表,可以有多行多列。
Matrix类有六个模板参数,但现在只需要了解前三个参数就足够了。剩下的三个参数都有默认值,我们暂时不碰它们,我们将在下面讨论它们。
Matrix的三个必备模板参数为:
Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
Eigen提供了许多方便的类型来涵盖通常的情况。例如,Matrix4f是一个4x4的浮点数矩阵。Eigen是这样定义的:
typedef Matrix<float, 4, 4> Matrix4f;
如上所述,在Eigen中,向量只是矩阵的一种特殊情况,要么有一行,要么有一列。它们只有一列的情况是最常见的;这样的向量称为列向量,通常简称为向量。在另一种情况下,它们只有一行,它们被称为行向量。
例如,方便的类型pedef Vector3f是一个3个浮点数的(列)向量。Eigen对其定义如下:
typedef Matrix<float, 3, 1> Vector3f;
我们还为行向量提供了方便的类型,例如:
typedef Matrix<int, 1, 2> RowVector2i;
当然,Eigen并不局限于在编译时已知维数的矩阵。RowsAtCompileTime和ColsAtCompileTime模板参数可以采用特殊值Dynamic,这表示在编译时大小未知,因此必须作为运行时变量处理。
在Eigen学术语中,这样的尺寸被称为动态尺寸;而在编译时已知的大小称为固定大小。例如,方便类型pedef MatrixXd,表示具有动态大小的双精度矩阵,定义如下:
typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
同样,我们定义了一个自解释的类型pedef VectorXi如下:
typedef Matrix<int, Dynamic, 1> VectorXi;
你可以完美地拥有固定数量的行和动态数量的列,如下所示:
Matrix<float, 3, Dynamic>
默认构造函数始终可用,从不执行任何动态内存分配,也从不初始化矩阵系数。你可以这样做:
Matrix3f a;
MatrixXf b;
在这里,
构造函数也可以接受大小。对于矩阵,总是先传递行数。对于向量,只需传递向量的大小。它们用给定的大小分配系数数组,但不初始化系数本身:
MatrixXf a(10,15);
VectorXf b(30);
在这里,
为了在固定大小和动态大小的矩阵之间提供统一的API,在固定大小的矩阵上使用这些构造函数是合法的,即使在这种情况下传递大小是无用的。所以这是合法的:
Matrix3f a(3,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); // A column vector containing the elements {1, 2}
Matrix<int, 5, 1> b {1, 2, 3, 4, 5}; // A row-vector containing the elements {1, 2, 3, 4, 5}
Matrix<int, 1, 5> c = {1, 2, 3, 4, 5}; // A column vector containing the elements {1, 2, 3, 4, 5}
在一般情况下,无论是固定大小还是运行时大小的矩阵和向量,系数必须按行分组,并作为初始化列表的初始化列表传递:
MatrixXi a { // construct a 2x2 matrix
{1, 2}, // first row
{3, 4} // second row
};
Matrix<double, 2, 3> b {
{2, 3, 4},
{5, 6, 7},
};
对于列向量或行向量,允许隐式转置。这意味着可以从单行初始化列向量:
VectorXd a {{1.5, 2.5, 3.5}}; // A column-vector with 3 coefficients
RowVectorXd b {{1.0, 2.0, 3.0, 4.0}}; // A row-vector with 4 coefficients
在eigen中,主要的系数访问器和变量是重载括号操作符。对于矩阵,总是先传递行索引。对于向量,只传递一个下标。编号从0开始。这个例子不言自明:
#include
#include
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)并不局限于向量,它也可用于一般矩阵,这意味着在系数数组中基于索引的访问。然而,这取决于矩阵的存储顺序。所有特征矩阵默认为列为主的存储顺序,但可以将其更改为行为主,请参阅存储顺序。
对于vector中的基于索引的访问,操作符 [ ] 也被重载,但请记住,c++不允许操作符 [ ] 接受多个参数。我们将运算符 [ ] 限制为向量,只有向量支持使用 [ ] 符号索引,因为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
#include
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(),更多细节请参阅本页。
为了API的一致性,所有这些方法在固定大小的矩阵上仍然可用。当然,您实际上无法调整固定大小的矩阵的大小。尝试将固定大小更改为实际不同的值将触发断言失败;但是下面的代码是合法的:
例如:输出:
#include
#include
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
当然,如果左侧是固定大小,则不允许调整其大小。
如果您不希望发生这种自动调整大小(例如为了调试目的),您可以禁用它,请参阅此页。
什么时候应该使用固定大小(例如Matrix4f),什么时候应该使用动态大小(例如MatrixXf)?简单的答案是:对于非常小的尺寸尽可能使用固定大小,对于较大的尺寸或必须使用动态大小。对于较小的大小,特别是小于(大约)16的大小,使用固定大小对性能非常有益,因为它允许Eigen避免动态内存分配并展开循环。在内部,固定大小的特征矩阵只是一个普通数组,即做
Matrix4f mymatrix;
其实就是去做
float mymatrix[16];
这真的是零运行费用。相比之下,动态大小矩阵的数组总是在堆上分配,所以这样做
MatrixXf mymatrix(rows,columns);
等于做某事
float *mymatrix = new float[rows*columns];
除此之外,MatrixXf对象将其行数和列数存储为成员变量。
当然,使用固定大小的限制是,只有在编译时知道大小时才有可能。此外,对于足够大的大小,例如大于(大约)32的大小,使用固定大小的性能优势变得可以忽略不计。更糟糕的是,尝试在函数内部使用固定大小创建一个非常大的矩阵可能会导致堆栈溢出,因为Eigen会尝试将数组自动分配为局部变量,而这通常是在堆栈上完成的。最后,根据具体情况,当使用动态大小时,Eigen也可以更积极地尝试向量化(使用SIMD指令),请参阅向量化。
我们在本页开头提到,Matrix类接受六个模板参数,但到目前为止我们只讨论了前三个。其余三个参数为可选参数。下面是模板参数的完整列表:
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
Matrix<float, 3, 3, RowMajor>
Matrix<float, Dynamic, Dynamic, 0, 3, 4>
Eigen定义了以下矩阵类型:
都是用的 Matrix 不同模板参数,预定义为不同类型。
注意: