Eigen库从入门到放弃(3. The Matrix class)

本文详细介绍了Eigen库中Matrix类的模板参数,探讨了Scalar、RowsAtCompileTime和ColsAtCompileTime的作用,以及动态和固定大小矩阵的使用场景、初始化方式和注意事项,包括storageorders和SIMD优化的考虑。
摘要由CSDN通过智能技术生成

在eigen中,所有的矩阵和向量都是Matrix的模板类对象,Matrix类接收6个模板参数,有三个是有默认值的,另外三个需要强制我们手动设置,我们先研究一下它的这三个参数,三个强制设置的参数分别是:

Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>

其中Scalar是变量类型,比如可以float,Scalar types列出了所有支持的数据类型;RowsAtCompileTime和ColsAtCompileTime是在编译期间就已经确定好的行和列,我们后面讨论如何来设置一些动态shape的矩阵(动态的意思就是编译期间不能确定)
eigen对于一些常见的shape和类型进行了typedef,比如Matrix4f就是4x4 float矩阵的简写,具体定义如下:

typedef Matrix<float, 4, 4> Matrix4f;

对于vector,也是一样,也就是一行或一列的矩阵,我们也进行了简化的定义,列向量和行向量分别定义如下:

typedef Matrix<float, 3, 1> Vector3f;  // 列向量
typedef Matrix<int, 1, 2> RowVector2i;  // 行向量

还有一个问题没有解决就是,我们的shape是动态的,比如我的矩阵的行数是根据一个vector.size()来确定的,这种在编译的时候,编译器无法知道具体的形状,所以eigen还支持你将行列设置为Dynamic,这种我们称为dynamic size,而对于那种你设置为(3, 3)(4, 4)形状的我们称之为fixed size,比如我们之前遇到过的MatrixXd就是dynamic size,定义如下:

typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
// 下面是定义成int动态vector
typedef Matrix<int, Dynamic, 1> VectorXi;
// 同理,你可以像下面这样,定义一个只有列是动态的类型
Matrix<float, 3, Dynamic>

有一些需要注意的点,对于我们的动态形状的矩阵,它的size是0*0的,且没有分配任何空间,但是如果你使用fixed size,size是有的,空间也是被分配好的。

Matrix3f a;  // 3*3矩阵,有9个float的空间被申请,但是里面的coefficients没有被初始化
MatrixXf b;  //  动态矩阵,size目前是0*0,里面没有申请内存,也没有被初始化

构造函数,对于矩阵先传行,而且他们的coefficients也就是每个位置的值,都没有被初始化:

MatrixXf a(10,15);
VectorXf b(30);

还有如下的初始化方法也支持:

Vector2d a(5.0, 6.0);
Vector3d b(5.0, 6.0, 7.0);
Vector4d c(5.0, 6.0, 7.0, 8.0);   // 最多到4个

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矩阵都是列优先,但是也可以设置成行优先,具体可以参考Storage orders.
对于vector中的基于索引的访问,也可以使用操作符[],但请记住,c++不允许操作符[]接受多个参数。我们将运算符[]限制为向量,因为c++语言中可能会把matrix[i,j]编译成与matrix[j]相同的东西!
还有一种叫做逗号初始化语法,可以方便地设置矩阵和向量系数。

Matrix3f m;
m << 1, 2, 3,
     4, 5, 6,
     7, 8, 9;
std::cout << m;

矩阵的当前大小可以通过rows()、cols()和size()来获取。这些方法分别返回行数、列数和系数数。通过resize()方法调整动态大小矩阵的大小。注意,resize()是改变大小的,不是shape

#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);
  std::cout << "The vector v is of size before " << v.size() << std::endl;
  std::cout << "The vector v is of size before " << v.size() << std::endl;
  std::cout << "As a matrix, v is of size "
            << v.rows() << "x" << v.cols() << std::endl;
  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;
}

在这里插入图片描述
如果resize()后的大小没变化,那数据也不会变化,但是如果大小变化,那resize()操作会破坏原始数据,如果不想破坏原始数据,使用conservativeResize()
但是对于固定尺寸的矩阵,其实相当于没啥用,因为尺寸一致啥都没干,尺寸不一致会报错,所以对于fixed size的矩阵或者向量就不要用了。
另外,对于=,其实也有一个resize()的效果在里面,=是将一个矩阵拷贝给另一个矩阵,这样就会把被赋值那个矩阵的形状也改变了,但是也是仅限于dynamic size的,对于fixed size,不可以将矩阵赋值给shape不一样的对象。

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];,几乎是不花费任何时间,但是对于dynamic size矩阵,需要在堆上申请空间,比如你做MatrixXf mymatrix(rows,columns);相当于在底层float *mymatrix = new float[rows*columns];,除此之外,MatrixXf对象将其行数和列数存储为成员变量。

当然,使用固定大小的限制是,只有在编译时知道大小时才行。此外,对于足够大的大小,例如大于(大约)32的大小,使用固定大小的性能优势变得可以忽略不计。更糟糕的是,尝试在函数内部使用固定大小创建一个非常大的矩阵可能会导致堆栈溢出,因为Eigen会尝试将数组自动分配为局部变量,而这通常是在堆栈上完成的。最后,根据具体情况,当使用动态大小时,Eigen也可以更积极地尝试向量化(使用SIMD指令),请参阅向量化

开头我们说过,Matrix类一共有6个模板参数,但是我们只讨论了3个,剩下的3个是可选的,下面是完整的参数:

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>,也就是说最开始的时候就申请好了,后面不会动态的申请了,又支持了动态,有限制了内存分配。
    还有一些typedefs,具体如下:

    MatrixNt : Matrix<type, N, N>. 如 Matrix<int, Dynamic, Dynamic>,MatrixXi
    MatrixXNt: Matrix<type, Dynamic, N>. 如 Matrix<int, Dynamic, 3>,MatrixX3i
    MatrixNXt: Matrix<type, N, Dynamic>. 如 Matrix<d, 4, Dynamic>,Matrix4Xd
    VectorNt:Matrix<type, N, 1>. 如 Matrix<float, 2, 1>,Vector2f
    RowVectorNt: Matrix<type, 1, N>. 如  Matrix<double, 1, 3>,RowVector3d
    

上面的N可以是2,3,4或者X(对应其中的dynamic 那个维度)
t可以是i,f,d,cf,cd,分别是int、float、double、complex<float>、complex<double>,其实不仅是这五种,还有很多,参考Scalar types

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值