在 Eigen 中,所有矩阵和向量都是 Matrix 模板类的对象。向量只是矩阵的一种特殊情况,有 1 行或 1 列。
Matrix 的前三个模板参数
Matrix 类采用六个模板参数,但现在了解前三个参数就足够了。其余三个参数具有默认值,现在我们将保持不变,我们将在下面讨论。
Matrix 的三个强制模板参数是:
Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
- Scalar 是标量类型,即系数的类型。也就是说,如果您想要一个 floats 矩阵,在此处选择 float。
RowsAtCompileTime
和ColsAtCompileTime
是在编译时已知的矩阵的行数和列数(如果在编译时不知道该数字,请参见下文)。
我们提供了很多方便的 typedef 来涵盖常见的情况。例如,Matrix4f 是一个 4x4 浮点矩阵。这是 Eigen 定义的方式:
typedef Matrix<float, 4, 4> Matrix4f ;
我们在下面讨论这些方便的 typedef。
向量 (Vectors)
如上所述,在 Eigen 中,向量只是矩阵的一种特殊情况,有 1 行或 1 列。他们有 1 列的情况是最常见的;
这样的向量称为列向量,通常缩写为向量。在另一种情况下,它们有 1 行,它们被称为行向量。
例如,方便的 typedef Vector3f
是 3 个浮点数的(列)向量。它由 Eigen 定义如下:
typedef Matrix<float, 3, 1> Vector3f;
我们还为行向量提供了方便的 typedef,例如:
typedef Matrix<int, 1, 2> RowVector2i;
特殊值 (Dynamic)
Dynamic 在 Eigen 中是个特殊字
当然,Eigen 并不局限于在编译时维度已知的矩阵。模板参数RowsAtCompileTime
和ColsAtCompileTime
可以接收 Dynamic,它表示编译时大小是未知的,所以必须作为运行时变量处理。在 Eigen 术语中,这样的大小称为动态大小(dynamic size);而在编译时已知的大小称为固定大小(fixed size)。例如,方便的 typedef 表示具有动态大小的双精度矩阵,定义如下:
typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
同样,我们定义一个不言自明的 typedef VectorXi
如下:
typedef Matrix<int, Dynamic, 1> VectorXi ;
您可以完美地拥有固定数量的行和动态数量的列,如:
Matrix<float, 3, Dynamic>
构造函数
默认构造函数始终可用,从不执行任何动态内存分配,并且从不初始化矩阵系数。你可以做:
Matrix3f a;
MatrixXf b;
其中:
a 是一个 3×3 矩阵,只是一个单纯的 float[9] 数组,并没有对其中的数据进行初始化
b 是一个动态大小的矩阵,其大小当前为 0×0,还没有对 b 进行内存分配
也可以使用获取大小的构造函数。对于矩阵,总是首先传递行数。对于向量,只需传递向量大小。他们分配给定大小的系数数组,但不初始化系数本身:
MatrixXf a(10,15);
VectorXf b(30);
这里:
a 是一个 10x15 动态大小的矩阵,具有已分配但当前未初始化的系数。
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 <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) 不限于向量,它也可用于一般矩阵,这意味着在系数数组中进行基于索引的访问。然而,这取决于矩阵的存储顺序。所有特征矩阵默认为列优先存储顺序,但这可以更改为行优先,请参阅存储顺序。
向量中基于索引的operator[]
访问也重载了,但请记住,C++ 不允许operator[]
采用多个参数。我们限制operator[]
为向量,因为 C++ 语言的笨拙会使matrix[i,j]
编译为与matrix[j]
。
逗号初始化
矩阵和向量系数可以使用所谓的_逗号初始化_语法方便地设置。现在,知道这个例子就足够了:
Matrux3f 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()。
为了 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
分配和调整大小
赋值是将一个矩阵复制到另一个矩阵的动作,使用 operator=. 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
当然,如果左侧是固定大小的,则不允许调整大小。
如果您不希望发生这种自动调整大小(例如出于调试目的),您可以禁用它,请参阅此页面。
固定大小 vs. 动态大小
什么时候应该使用固定尺寸(例如 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 指令),请参阅 Vectorization。
可选模板参数
我们在本页开头提到Matrix
类有六个模板参数,但到目前为止我们只讨论了前三个。其余三个参数是可选的。以下是模板参数的完整列表:
矩阵 < 类型名标量,
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
Options
是一个位字段。在这里,我们只讨论一点:RowMajor
,它指定这种类型的矩阵使用行优先存储顺序;默认情况下,存储顺序为列优先。
例如,这种类型表示行优先的 3x3 矩阵:
Matrix<float, 3, 3, RowMajor>
MaxRowsAtCompileTime
和MaxColsAtCompileTime
想指定在编译时已知的固定上限时是有用的(即便矩阵的确切大小在编译时不知道)。这样做的最主要的原因是避免动态内存分配。例如,以下矩阵类型使用 12 个浮点数的普通数组,没有动态内存分配:
Matrix<float, Dynamic, Dynamic, 0, 3, 4>
方便的 typedef
Eigen 定义了以下 Matrix 类型定义:
MatrixNt is Matrix<type, N, N> // For example, MatrixXi for Matrix<int, Dynamic, Dynamic>.
MatrixXNt is Matrix<type, Dynamic, N> // For example, MatrixX3i for Matrix<int, Dynamic, 3>.
MatrixNXt is Matrix<type, N, Dynamic> // For example, Matrix4Xd for Matrix<d, 4, Dynamic>.
VectorNt is Matrix<type, N, 1> // For example, Vector2f for Matrix<float, 2, 1>.
RowVectorNt is Matrix<type, 1, N> // For example, RowVector3d for Matrix<double, 1, 3>.
这里:
-
N 可以是任意数,意味着动态
-
t 可以是 i(meaning int)、f(meaning float)、d(meaning double)、cf(meaning complex)或 cd(meaning complex)中的任何一个。s 仅针对这五种类型定义的事实 typedef 并不意味着它们是唯一受支持的标量类型。