C++版NumPy-Eigen库快速⼊门
Eigen库的使⽤
零、前⾔
如果你是⼀个pythoner,⼀定知道NumPy操作维度数组和矩阵⾮常⽅便,我也在实际开发过程中使⽤过NumPy来进⾏过各种算术运算,真是⼤⼤地提⾼了⼯作效率;曾经⼀直羡慕pythoner能这么⽅便的使⽤NumPy进⾏各种维度和矩阵运算,直到遇到了Eigen库,它堪称
C++版的NumPy,虽然在使⽤的过程中发现它和NumPy⽐还有⼀些不⾜,但已经很好了,希望它越来越强⼤。⽹上虽然已经有了很多有关Eigen的介绍使⽤,但是⾃⼰在使⽤过程中还是有很多疑惑,于是根据官⽅提供的⽂档整理⽽成这篇⽂章,希望能够帮助初学者快速⼊门。
⼀、Eigen矩阵类
在Eigen中,所有矩阵和向量都是Matrix模板类的对象。向量只是矩阵的⼀种特殊情况,具有1⾏或1列。
1、矩阵类模板参数
该矩阵类需要六个模板参数,平时我们主要⽤到的就前三个参数,剩下的三个参数默认即可。
1>必选模板参数:
Matrix的三个必需模板参数是:
Matrix <typename Scalar,int RowsAtCompileTime,int ColsAtCompileTime>
Scalar是标量类型,即系数的类型,如果要使⽤浮点数矩阵,这⾥就传⼊float。
RowsAtCompileTime和ColsAtCompileTime是在编译时已知的矩阵的⾏数和列数。
例如:Matrix4f是⼀个4x4的浮点矩阵,Eigen定义的⽅式:
typedef Matrix <float,4,4> Matrix4f;
2>可选模板参数
Matrix类其余三个参数是可选的,模板参数的完整列表:
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
Options是位字段,⽤来指定是⾏优先存储还是列优先存储,默认情况下,矩阵和向量的存储⽅式是“列优先存储”,可以传值RowMajor和ColMajor。
例如,⾏优先的3x3矩阵:
<float,3、3,RowMajor>
MaxRowsAtCompileTime和MaxColsAtCompileTime⽤来指定矩阵⼤⼩的上限,即使在编译时不知道矩阵的确切⼤⼩,在编译时也知道固定的上限,这样做的最⼤原因是避免动态内存分配。
例如,下⾯矩阵类型使⽤12个浮点数的普通数组,⽽没有动态内存分配:
Matrix<float, Dynamic, Dynamic, 0, 3, 4>
2、向量
在Eigen中,向量只是具有1⾏或1列的矩阵的⼀种特殊情况,只有1列的向量称为列向量,只有1⾏的称为⾏向量。
例如,typedef Vector3f是3个浮点数的(列)向量。Eigen定义如下:
typedef Matrix <float,3,1> Vector3f;
⾏向量的定义:
typedef Matrix <int,1,2> RowVector2i;
3、特值动态
Eigen不仅限于其尺⼨在编译时已知的矩阵,在RowsAtCompileTime和ColsAtCompileTime模板参数可以采取特殊值Dynamic这表明⼤⼩在编译时是未知的,所以必须作为运⾏时变量来处理。这种⼤⼩称为动态 ⼤⼩;⽽在编译时已知的⼤⼩称为固定 ⼤⼩。
例如typedef MatrixXd定义为具有动态⼤⼩的双精度矩阵,其定义如下:
typedef Matrix <double,Dynamic,Dynamic> MatrixXd;
定义固定数量的⾏和动态的列数,例如:
矩阵<float,3,Dynamic>
4、矩阵和向量的初始化
默认构造函数始终可⽤,从不执⾏任何动态内存分配,并且从不初始化矩阵系数。可以这样定义:
Matrix3f a;
MatrixXf b;
这⾥:
a 是⼀个3×3矩阵,具有未初始化系数的普通float [9]数组,
b 是⼀个动态⼤⼩的矩阵,其⼤⼩当前为0 x 0,并且其系数数组尚未分配。
也可以提供采⽤⼤⼩的构造函数。对于矩阵,总是先传递⾏数。对于⽮量,只需传递⽮量⼤⼩即可。他们分配给定⼤⼩的系数数组,但不⾃⾏初始化系数:
MatrixXf a(10,15);
VectorXf b(30);
这⾥:
a 是10x15动态尺⼨矩阵,具有已分配但当前尚未初始化的系数。
b 是⼤⼩为30的动态⼤⼩向量,具有已分配但当前尚未初始化的系数。
为了在固定⼤⼩和动态⼤⼩的矩阵之间提供统⼀的API,合法的是在固定⼤⼩的矩阵上使⽤这些构造函数,即使在这种情况下传递⼤⼩都没有⽤。所以这是合法的:
Matrix3f a(3,3);
构造函数来初始化⽮量的系数:
Vector2d a(5.0,6.0);
Vector3d b(5.0,6.0,7.0);
Vector4d c(5.0,6.0,7.0,8.0);
5、矩阵和向量的存取
Eigen中的主要系数访问器和变异器是重载的括号运算符。对于矩阵,总是先传递⾏索引。对于向量,只需传递⼀个索引。编号从0开始。⽰例:
#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;
}
程序输出:
Here is the matrix m:3-12.51.5
Here is the vector v:43
语法m(index)不限于⽮量,它也可⽤于⼀般矩阵,这意味着在系数数组中基于索引的访问。但是,这取决于矩阵的存储顺序。所有Eigen矩阵默认为列优先存储顺序,但是可以将其更改为⾏优先。
6、逗号初始化
Eigen矩阵和⽮量⽀持逗号初始化的语法:
例:
Matrix3f m;
m <<1,2,3,
4,5,6,
7,8,9;
std::cout << m;
输出结果:
123456789
7、调整⼤⼩
矩阵的当前⼤⼩可以通过rows(),cols()和size()检索。这些⽅法分别返回⾏数,列数和系数数。调整动态⼤⼩矩阵的⼤⼩是通过resize()⽅法完成的。
例:
#include<iostream>
#include<Eigen/Dense>
using namespace Eigen;
int main()
{
MatrixXd m(2,5);
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;
}
输出结果:
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()⽅法为空操作。否则具有破坏性:系数的值可能会更改。
为了API的统⼀性,所有这些⽅法仍然可以在固定⼤⼩的矩阵上使⽤。实际上不能调整固定⼤⼩的矩阵的⼤⼩。尝试将固定⼤⼩更改为实际不同的值将触发断⾔失败。但是以下代码是合法的:
例:
#include<iostream>
#include<Eigen/Dense>
using namespace Eigen;
int main()
{
Matrix4d m;
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 2x2a is now of size 3x3
固定尺⼨与动态尺⼨
什么时候应该使⽤固定尺⼨(例如Matrix4f),什么时候应该使⽤动态尺⼨(例如MatrixXf)?
简单的答案是:将固定尺⼨⽤于可以使⽤的⾮常⼩的尺⼨,将动态尺⼨⽤于较⼤的尺⼨或必须使⽤的尺⼨。对于⼩尺⼨,特别是对于⼩于(⼤约)16的尺⼨,使⽤固定尺⼨对性能有极⼤的好处,因为它使Eigen避免动态内存分配。
在内部,固定⼤⼩的Eigen矩阵只是⼀个简单的数组,即
Matrix4f mymatrix;
等于float mymatrix[16];
MatrixXf对象还将其⾏数和列数存储为成员变量。
当然,使⽤固定⼤⼩的限制是,只有当您在编译时知道⼤⼩时,才有可能这样做。对于⾜够⼤的尺⼨,例如,对于⼤于(⼤约)32的尺⼨,使⽤固定尺⼨的性能优势变得可以忽略不计。糟糕的是,尝试使⽤函数内部的固定⼤⼩创建⾮常⼤的矩阵可能会导致堆栈溢出,因为Eigen会尝试将数组⾃动分配为局部变量,⽽这通常是在堆栈上完成的。
⼆、矩阵和向量算法
Eigen通过常见的C ++算术运算符(例如+,-,*)的重载,或通过诸如dot(),cross()等特殊⽅法的⽅式提供矩阵/向量算术运算。对于Matrix类(矩阵和向量),运算符仅重载以⽀持线性代数运算。
1、加减
左侧和右侧必须具有相同数量的⾏和列,还必须具有相同的标量类型,因为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 =
35
48
a -
b =
-1-1
20
Doing a += b;
Now a =
35
48
-v + w - v =
-1namespace是干嘛的
-4
-
6
2、标量乘法和除法
标量的乘法和除法也⾮常简单,操作如下:
⼆进制运算符如 matrix scalar
⼆进制运算符如 scalar matrix
⼆元运算符/如 matrix/scalar
复合运算符* =如 matrix*=scalar
复合运算符/ =如 matrix/=scalar
例:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。