• Eigen-Matrix矩阵


    一、概述

    在Eigen中,所有矩阵和向量都是矩阵模板类的对象。向量只是矩阵的一种特殊情况,要么有一行,要么有一列。矩阵就是一个二维数表,可以有多行多列。

    二、矩阵的前三个模板参数

    Matrix类有六个模板参数,但现在只需要了解前三个参数就足够了。剩下的三个参数都有默认值,我们暂时不碰它们,我们将在下面讨论它们。

    Matrix的三个必备模板参数为:

    Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
    
    • 1
    • 参数Scalar, 标量是标量类型,即系数的类型。也就是说,如果你想要一个浮点数的矩阵,在这里选择float。有关所有支持的标量类型的列表,以及如何扩展对新类型的支持,请参阅标量类型。
    • RowsAtCompileTime和ColsAtCompileTime是在编译时已知的矩阵的行数和列数(如果在编译时不知道该数,请参见下文)。

    Eigen提供了许多方便的类型来涵盖通常的情况。例如,Matrix4f是一个4x4的浮点数矩阵。Eigen是这样定义的:

    typedef Matrix<float, 4, 4> Matrix4f;
    
    • 1

    三、向量

    如上所述,在Eigen中,向量只是矩阵的一种特殊情况,要么有一行,要么有一列。它们只有一列的情况是最常见的;这样的向量称为列向量,通常简称为向量。在另一种情况下,它们只有一行,它们被称为行向量。

    例如,方便的类型pedef Vector3f是一个3个浮点数的(列)向量。Eigen对其定义如下:

    typedef Matrix<float, 3, 1> Vector3f;
    
    • 1

    我们还为行向量提供了方便的类型,例如:

    typedef Matrix<int, 1, 2> RowVector2i;
    
    • 1

    四、动态维度参数

    当然,Eigen并不局限于在编译时已知维数的矩阵。RowsAtCompileTime和ColsAtCompileTime模板参数可以采用特殊值Dynamic,这表示在编译时大小未知,因此必须作为运行时变量处理。

    在Eigen学术语中,这样的尺寸被称为动态尺寸;而在编译时已知的大小称为固定大小。例如,方便类型pedef MatrixXd,表示具有动态大小的双精度矩阵,定义如下:

    typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
    
    • 1

    同样,我们定义了一个自解释的类型pedef VectorXi如下:

    typedef Matrix<int, Dynamic, 1> VectorXi;
    
    • 1

    你可以完美地拥有固定数量的行和动态数量的列,如下所示:

    Matrix<float, 3, Dynamic>
    
    • 1

    五、构造函数

    默认构造函数始终可用,从不执行任何动态内存分配,也从不初始化矩阵系数。你可以这样做:

    Matrix3f a;
    MatrixXf b;
    
    • 1
    • 2

    在这里,

    • a 是一个3 × 3矩阵,其系数为未初始化的浮点[9]数组;
    • b 是一个动态大小的矩阵,其大小目前为0 × 0,其系数数组还没有被分配。

    构造函数也可以接受大小。对于矩阵,总是先传递行数。对于向量,只需传递向量的大小。它们用给定的大小分配系数数组,但不初始化系数本身:

    MatrixXf a(10,15);
    VectorXf b(30);
    
    • 1
    • 2

    在这里,

    • a 是一个10x15的动态大小矩阵,具有已分配但当前未初始化的系数。
    • b 是一个大小为30的动态大小向量,具有已分配但当前未初始化的系数。

    为了在固定大小和动态大小的矩阵之间提供统一的API,在固定大小的矩阵上使用这些构造函数是合法的,即使在这种情况下传递大小是无用的。所以这是合法的:

    Matrix3f a(3,3);
    
    • 1

    这是一个无操作。

    矩阵和向量也可以从系数列表初始化。在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);
    
    • 1
    • 2
    • 3

    如果启用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}
    
    • 1
    • 2
    • 3

    在一般情况下,无论是固定大小还是运行时大小的矩阵和向量,系数必须按行分组,并作为初始化列表的初始化列表传递:

    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},
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对于列向量或行向量,允许隐式转置。这意味着可以从单行初始化列向量:

    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
    
    • 1
    • 2

    六、索引访问器

    在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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出:

    Here is the matrix m:
      3  -1
    2.5 1.5
    Here is the vector v:
    4
    3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    请注意,语法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

    输出

    1 2 3
    4 5 6
    7 8 9
    
    • 1
    • 2
    • 3

    八、矩阵维度调整

    矩阵的当前大小可以通过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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    输出

    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
    
    • 1
    • 2
    • 3
    • 4

    如果实际矩阵大小没有改变,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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出

    The matrix m is of size 4x4
    
    • 1

    九、赋值和调整大小

    赋值是使用操作符=将一个矩阵复制到另一个矩阵的操作。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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出

    a is of size 2x2
    a is now of size 3x3
    
    • 1
    • 2

    当然,如果左侧是固定大小,则不允许调整其大小。

    如果您不希望发生这种自动调整大小(例如为了调试目的),您可以禁用它,请参阅此页。

    十、固定尺寸vs.动态尺寸

    什么时候应该使用固定大小(例如Matrix4f),什么时候应该使用动态大小(例如MatrixXf)?简单的答案是:对于非常小的尺寸尽可能使用固定大小,对于较大的尺寸或必须使用动态大小。对于较小的大小,特别是小于(大约)16的大小,使用固定大小对性能非常有益,因为它允许Eigen避免动态内存分配并展开循环。在内部,固定大小的特征矩阵只是一个普通数组,即做

    Matrix4f mymatrix; 
    
    • 1

    其实就是去做

    float mymatrix[16]; 
    
    • 1

    这真的是零运行费用。相比之下,动态大小矩阵的数组总是在堆上分配,所以这样做

    MatrixXf mymatrix(rows,columns); 
    
    • 1

    等于做某事

    float *mymatrix = new float[rows*columns]; 
    
    • 1

    除此之外,MatrixXf对象将其行数和列数存储为成员变量。

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

    十一、可选模板参数

    我们在本页开头提到,Matrix类接受六个模板参数,但到目前为止我们只讨论了前三个。其余三个参数为可选参数。下面是模板参数的完整列表:

    Matrix<typename Scalar,
           int RowsAtCompileTime,
           int ColsAtCompileTime,
           int Options = 0,
           int MaxRowsAtCompileTime = RowsAtCompileTime,
           int MaxColsAtCompileTime = ColsAtCompileTime>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • Options是位字段。在这里,我们只讨论一个比特:RowMajor。它指定这种类型的矩阵使用行为主存储顺序;默认情况下,存储顺序是以列为主的。请参阅存储订单页面。例如,此类型表示行为主的3x3矩阵:
    Matrix<float, 3, 3, RowMajor>
    
    • 1
    • MaxRowsAtCompileTime和MaxColsAtCompileTime在需要指定时非常有用,即使在编译时不知道矩阵的确切大小,但在编译时知道一个固定的上限。这样做的最大原因可能是为了避免动态内存分配。例如,下面的矩阵类型使用12个浮点数的普通数组,没有动态内存分配:
    Matrix<float, Dynamic, Dynamic, 0, 3, 4>
    
    • 1

    十二、方便预定义

    Eigen定义了以下矩阵类型:

    都是用的 Matrix 不同模板参数,预定义为不同类型。

    • MatrixNt :Matrix.
      例如, MatrixXi 实际上是 :Matrix.
    • MatrixXNt :Matrix.
      例如, MatrixX3i 实际上是 :Matrix.
    • MatrixNXt :Matrix.
      例如, Matrix4Xd 实际上是 :Matrix.
    • VectorNt :Matrix.
      例如, Vector2f 实际上是 :Matrix.
    • RowVectorNt :Matrix.
      例如, RowVector3d 实际上是 :Matrix.

    注意:

    • N 可以是2、3、4或者X(表示动态 Dynamic ) 中的任意一个。
    • t 可以是I (int)、f (float)、d (double)、cf (complex)或CD (complex)中的任意一个。虽然只为这五种类型定义了类型定义,但这并不意味着它们是唯一受支持的标量类型。例如,支持所有标准整数类型,请参阅标量类型。
  • 相关阅读:
    Java分布式系统和云计算教程
    Linux 网络通信
    深入浅出【图卷积神经网络GCN】从 邻接矩阵、特征值矩阵、单位阵、度矩阵 入手,深刻理解融合邻居节点(信息) | GCN从公式到代码实现 全过程 | 在Cora数据集上实现节点分类任务
    C语言 变量的存储和引用,内部和外部函数
    联邦模块(概述,实战应用,基本原理,未来展望)
    python中的异常处理try-except-else-finally
    还在用Xshell?推荐这个更现代的终端连接工具
    K8S:K8S自动化运维容器Docker集群
    从0开始写一个简单的vite hmr 插件
    LeetCode中等题之递增子序列
  • 原文地址:https://blog.csdn.net/qq_43680827/article/details/136348889