介绍

Eigen 3 是一个以纯泛型编写的 C++ 矩阵运算库,它的授权是 MPL2,以源代码的形式提供给用户,所以只要把它的代码 include 进自己的程序就可以使用,不需要链接 DLL,也没有任何除 iostream 以外的依赖项。

实用链接

例程

下面是一个比较全面的示例,我写了较多的注释来澄清一些上手时容易误会的问题,旨在帮助你在一个小时内学会 Eigen 的常用操作并开始工作,也补足了被 AsciiQuickReference.txt 忽视的一些要点。

// 必须的库:
#include <iostream>
#include <Eigen/Core>

// 本例涉及到的功能还需要:
#include <Eigen/LU>
#include <Eigen/Geometry>

// 使用的命名空间:
using namespace Eigen;
using namespace std;

int main()
{
    /* --------------------------------------------------------------- */
    /* 1. 矩阵声明 */

    Matrix4d A;         // 声明 4x4 的 double 矩阵 A,“4d”中 4 代表
                        // 阶数,d 代表元素的数据类型
    
    MatrixXd B(4, 12);  // 声明动态大小的 double 矩阵 B,并指定尺寸为
                        // 4 行 12 列(4x12)
    
    MatrixXd C, D;      // 声明动态大小、未定尺寸的 double 矩阵 C、D

    Matrix<int, 2, 3> E;// 声明 2x3 的 int 矩阵 E

    VectorXd vA, vB;    // 声明动态大小、未定尺寸的 double 列向量

    RowVectorXd rvB;    // 声明动态大小、未定尺寸的 double 行向量 rvB

    /// NOTE: 动态大小的矩阵,如果未指定尺寸,则在内存中初始大小为 0x0,
    /// 可以后续用矩阵尺寸变换来更改。
    /// NOTE: 未进行初始化的矩阵,其元素被用随机数填充。

    /* --------------------------------------------------------------- */
    /* 2. 矩阵初始化 */

    A << 1, 2, 3, 4,
         5, 6, 7, 8,
         9, 10, 11, 12,
         13, 14, 15, 16;
    /// NOTE: 方法 1,用移位运算符按先行后列对矩阵初始化(符合自然书写)。
    /// WARNING: 数据不足或超出矩阵元素数,都会导致程序闪退,返回值 3。
    cout << "\nA:\n" << A << endl;    // 直接移位打印输出矩阵

    B << A, A, A;
    /// NOTE: 方法 2,用移位运算符将多个矩阵排入左值矩阵中,将按照自然
    /// 书写的顺序设法将右值列表排入,如本例中 B 将由 3 个 A 横向排列构
    /// 成;如果 B 是 12x4 矩阵,执行本句后 B 将由 3 个 A 纵向排列构成。
    /// WARNING: 数据不足或超出矩阵元素数,都会导致程序闪退,返回值 3;
    /// 因此用移位运算符对一个未定大小(0x0 大小)或元素总数与右值各矩阵
    /// 元素总数不匹配的左值进行初始化,程序也会闪退。
    cout << "\nB:\n" << B << endl;

    A << (Matrix2i() << 1, 2, 3, 4).finished().cast<double>(),
          MatrixXd::Zero(2, 2),
          Matrix2d::Zero(),
          MatrixXd::Identity(2, 2);
    /// NOTE: 方法 3,用多种方法形成子矩阵,把它们排入左值矩阵中。
    /// 其中:
    ///     第一个子矩阵用 finished() 将已初始化的匿名对象返回,再用
    ///         cast<type>() 方法转换为 double 矩阵(纯粹为了演示);
    ///     第二个子矩阵用 MatrixXd 类的 Zero() 返回指定大小的零阵;
    ///     第三个子矩阵与前一子矩阵的表达方式类似,而 Matrix2d 类已有
    ///         尺寸,无需再规定尺寸;
    ///     第四个子矩阵用 MatrixXd 类的 Identity() 返回单位阵。
    cout << "\nA:\n" << A << endl;

    C = A;
    /// NOTE: 方法 4,用等号赋值,将右值拷贝到左值中,左值会根据右值的
    /// 尺寸进行隐式的相似塑形(见下文),前提是左值必须为动态大小,且能
    /// 够变为右值的尺寸;否则,程序闪退,返回值 3。
    C = A + MatrixXd::Ones(A.rows(), A.cols());
    /// NOTE: 在用等号赋值时,右值也可以是表达式。本例用 MatrixXd 类的
    /// Ones() 静态方法返回与 A 同尺寸的全 1 阵,
    cout << "\nC:\n" << C << endl;

    A.fill(2);
    /// NOTE: 方法 5,用 fill() 方法直接将矩阵用某个数值填充。
    cout << "\nA (fill):\n" << A << endl;

    A.setRandom();
    /// NOTE: 方法 6,用 set____() 族方法将矩阵置为某种特殊矩阵:
    ///     setRandom()     (-1, 1) 之间的随机数矩阵
    ///     setIdentity()   单位阵(非方阵时仅主对角线置 1,其他补 0)
    ///     setZero()       零阵
    ///     setOnes()       全 1 阵
    ///     
    ///     setLinSpaced(size, low, high)
    ///                     向量专用,与 MATLAB 中 linespace() 类似
    cout << "\nA (setRandom):\n" << A << endl;

    /* --------------------------------------------------------------- */
    /* 3. 矩阵尺寸变换 */

    B.resize(12, 4);
    /// NOTE: 硬塑形,将 B 大小设置为 12 行 4 列,该方法将原矩阵的所有
    /// 元素按列排成向量,再将其按列赋予新矩阵(观察运行结果)。
    /// WARNING: 如果原矩阵的元素总数与新矩阵元素总数不等,则所有数据丢
    /// 失,新矩阵用随机值填充。
    /// WARNING: 所有尺寸变换操作,都只能对动态大小的矩阵,或某些维度上
    /// 尺寸为动态的矩阵进行;对诸如 Matrix3d 等静态大小的矩阵进行尺寸
    /// 变换将导致程序闪退,返回值 3。
    cout << "\nB (resize):\n" << B << endl;

    D.resize(3, 3);
    /// NOTE: 硬塑形的用例:D 未被初始化,声明时也未指定尺寸,在准备用移
    /// 位运算符给 D 赋值前必须用 resize() 指定大小,否则存在前述元素数
    /// 不匹配问题,引起闪退。
    /// NOTE: 如果某个尺寸保持不变,仅变动另一尺寸,可将不作改变的尺寸以
    /// Eigen::NoChange 代替(后续有例子);此时所有数据亦丢失。
    D << 1, 2, 3,
         4, 5, 6,
         7, 8, 9;
    cout << "\nD:\n" << D << endl;

    vA.resize(5);
    /// NOTE: 向量的硬塑形,只需规定长度即可。
    vA << 1, 2, 3, 4, 5;
    cout << "\nvA:\n" << vA << endl;

    D.conservativeResize(2, Eigen::NoChange);
    /// NOTE: 软塑形,将 D 大小设置为 2 行 ~ 列,该方法试图不改变原矩阵
    /// 原有的元素,并变更其尺寸。缩小尺寸时将对矩阵进行简单剪裁,扩大尺
    /// 寸时多出的元素用随机值填充。使用该方法改变矩阵尺寸时能保留数据。
    /// NOTE: 向量也可进行类似操作。
    cout << "\nD (conservativeResize):\n" << D << endl;

    D.resizeLike(A);
    /// NOTE: 相似塑形,将 D 的尺寸设置为与参数矩阵的尺寸一致。该方法的
    /// 行为类似硬塑形,即当新旧矩阵元素数不一致时,所有数据丢失;否则,
    /// 将用与硬塑形完全一样的方法将原矩阵的元素赋予新矩阵。
    /// NOTE: 向量也可进行类似操作。
    cout << "\nD (resizeLike A):\n" << D << endl;

    /* --------------------------------------------------------------- */
    /* 4. 矩阵访问 */

    cout << "\nC:\n" << C << endl;
    cout << "\nC(2, 1) = " << C(1, 0) << endl;
    /// NOTE: 元素访问,语法与 MATLAB 一致,但由于 MATLAB 和自然书写习
    /// 惯是从 1 起,而 C++ 的语法是从 0 起的,因此访问时实际编号要减 1。

    cout << "\nC(2, :):\n" << C.row(1) << endl;
    cout << "\nC(:, 3):\n" << C.col(2) << endl;
    /// NOTE: 片段访问,语法与 MATLAB 的对应关系如下:
    /// Eigen                                 Matlab
    /// x.head(n)                          // x(1: n)
    /// x.tail(n)                          // x(end-n+1: end)
    /// x.segment(i, n)                    // x(i+1: i+n)
    /// P.block(i, j, rows, cols)          // P(i+1:i+rows, j+1:j+cols)
    /// P.row(i)                           // P(i+1, :)
    /// P.col(j)                           // P(:, j+1)
    /// R.row(i) = P.col(j)                // R(i, :) = P(:, j)
    /// P.leftCols(cols)                   // P(:, 1:cols)
    /// P.middleCols(j, cols)              // P(:, j+1:j+cols)
    /// P.rightCols(cols)                  // P(:, end-cols+1:end)
    /// P.topRows(rows)                    // P(1:rows, :)
    /// P.middleRows(i, rows)              // P(i+1:i+rows, :)
    /// P.bottomRows(rows)                 // P(end-rows+1:end, :)
    /// P.topLeftCorner(rows, cols)        // P(1:rows, 1:cols)
    /// P.bottomLeftCorner(rows, cols)     // P(end-rows+1:end, 1:cols)
    ///   ______Corner(rows, cols)...

    /* --------------------------------------------------------------- */
    /* 5. 矩阵操作 */

    D.resize(3, 3);
    D << 2, 1, 0,
         0, 2, 0,
         0, 0, 2;
    cout << "\nD = \n" << D << endl;
    cout << "\nD.' = \n" << D.transpose() << endl;
    cout << "\nD' = \n" << D.adjoint() << endl;
    /// NOTE: 矩阵的转置、共轭转置操作。

    cout << "\nsum(D(:)) = \n" << D.sum() << endl;
    cout << "\nsum(D) = \n" << D.colwise().sum() << endl;
    cout << "\nsum(D')' = \n" << D.rowwise().sum() << endl;
    cout << "\nprod(D(:)) = \n" << D.prod() << endl;
    cout << "\nprod(D) = \n" << D.colwise().prod() << endl;
    cout << "\nprod(D')' = \n" << D.rowwise().prod() << endl;
    cout << "\nmean(D(:)) = \n" << D.mean() << endl;
    cout << "\nmean(D) = \n" << D.colwise().mean() << endl;
    cout << "\nmean(D')' = \n" << D.rowwise().mean() << endl;
    cout << "\ntrace(D) = \n" << D.trace() << endl;
    /// NOTE: 矩阵的求和、连乘、均值、求迹操作与 MATLAB 语法的对应。

    cout << "\nD + E = \n" << D + Matrix3d::Identity() << endl;
    cout << "\nD - E = \n" << D - Matrix3d::Identity() << endl;
    cout << "\nD * E = \n" << D * Matrix3d::Identity() << endl;
    /// NOTE: 矩阵的加、减、乘操作,均与 MATLAB 语法一致。

    C = D.array() * 2;
    cout << "\nD .* 2 = \n" << C << endl;
    C = C.array().sqrt();
    cout << "\nsqrt(D.*2) = \n" << C << endl;
    /// NOTE: 矩阵的元素运算操作,均可通过 matrix.array() 及相关方法或
    /// 运算符实现。
    /// 提供的方法包括:sin() cos() sqrt() abs() abs2() log() exp()
    /// pow(n) square() cube() 以及行为类似 MATLAB 中 max/min 函数的
    /// max(other_matrix.array()) 和 min(other_matrix.array())。

    cout << "\ninv(D) = \n" << D.inverse() << endl;
    cout << "\n|D| = " << D.determinant() << endl;  // MATLAB: det(D)
    /// NOTE: 矩阵的求逆、求行列式操作,需要 #include <Eigen/LU>

    vA.resize(3); vB.resize(3);
    vA << 1, 4, 9;
    vB << 1, 1, 1;
    cout << "\nvA = (" << vA.transpose() << ")'" << endl;
    cout << "\nvB = (" << vB.transpose() << ")'" << endl;
    cout << "\nmean(vA) = " << vA.mean() << endl;
    cout << "\nnorm(vB) = " << vB.norm() << endl;
    cout << "\nvA dot vB = " << vA.dot(vB) << endl;
    Vector3d v3A, v3B;
    v3A = vA; v3B = vB;
    cout << "\nvA cross vB = (" << v3A.cross(v3B) << ")'" << endl;
    /// NOTE: 向量的内积(点乘)、外积(叉乘)、均值和范数,
    /// 其中计算外积需要 #include <Eigen/Geometry>,且被操作向量必须为
    /// Vector3_ 类,对动态大小的向量求叉乘会出现编译错误。    system("pause");
    return 0;
}

请编译运行这个程序,并对照运行结果理解代码。