51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Eigen 使用教程

Eigen 是开源的C++线性代数库,常用在计算机图形学中,之前我们记录了 安装使用方法,本文记录常用功能使用方法。

动态矩阵、静态二维矩阵 {#动态矩阵、静态二维矩阵}

  • Eigen 官方代码支持的最高维度为二维矩阵,后文的矩阵、数组均为二维。
  • Eigen 在编译期间确定尺寸的矩阵为静态矩阵,运行期间确定尺寸的为动态矩阵(数据类型中带有X)
  • 选用原则:
    • 对于非常小尺寸的矩阵,尽可能使用固定尺寸,特别是小于(大约)16的尺寸,使用固定尺寸对性能非常有益,因为它允许 Eigen 避免动态内存分配和展开循环; 对于小尺寸在内部,一个固定大小的特征矩阵只是一个普通的数组。
    • 对于较大尺寸,或者在必须使用动态尺寸的地方,尽量使用动态尺寸。当矩阵尺寸大于(大约)32时,静态矩阵的性能收益变得可以忽略,而且对于动态矩阵,Eigen 更倾向于尝试使用 SIMD 指令集加速运算。

模板类 {#模板类}

  • Eigen 中有几个基础数据结构模板类

Matrix类 {#Matrix类}

  • 所有矩阵和向量都是Matrix模板类的对象,Matrix类有6个模板参数,主要使用前三个,剩下的使用默认值。
  • MaxRowsAtCompileTimeMaxColsAtCompileTime 在已知动态矩阵的尺寸上界时是可以提升工作效率的。
  • 默认构造时,指定大小的矩阵,只分配相应大小的空间,不进行初始化。动态大小的矩阵,则未分配空间。

  • []操作符可以用于向量元素的获取,但不能用于matrix

  • matrix的大小可以通过rows(), cols(), size()获取,resize()可以重新调整矩阵大小。

  • Matrix 定义的矩阵为静态矩阵,在编译时确定尺寸、分配内存,随机初始化:

  • MatrixX 开头的为动态矩阵,两个维度都可以变化,本质为 Matrix<Type, Dynamic, Dynamic> 定义的类型

    例如: MatrixXd 为 double 类型的动态矩阵

  • 静态矩阵运算很快,但是有 128k 的堆栈尺寸限制,常用的还是动态矩阵类型

  • 仅变化一个维度的动态矩阵为动态向量 typedef Matrix<float, Dynamic, 1> VectorXf,使用方法类似

Array类 {#Array类}

  • Array是类模板,前三个参数必须指定,后三个参数可选。
  • 类似于 Matrix 类,Array 默认仍会产生静态数组

  • 一维动态数组为 ArrayX 开头,二维动态数组为 ArrayXX 开头

Array 和 Martix 的区别 {#Array-和-Martix-的区别}

  • Martix 表示的是矩阵,运算为矩阵运算,运算时尺寸需要遵循矩阵运算规则
  • Array 和 Matrix 数据组成相同,但运算规则为逐元素运算,需要相同尺寸数据进行运算

Array 和 Martix 的转换 {#Array-和-Martix-的转换}

  • Matrix对象------>Array对象:.array()函数
  • Array对象------>Matrix对象:.matrix()函数

初始化 {#初始化}

建议矩阵数据都要初始化,不然是十分危险的。

默认初始化 {#默认初始化}

  • 默认初始化为随机数:

赋值初始化 {#赋值初始化}

  • 赋值初始化

对象初始化 {#对象初始化}

  • 可以用其他对象初始化新的相同内容对象

逗号初始化 {#逗号初始化}

  • 为矩阵元素赋值,顺序是从左到右,从上到下,数目必须匹配。

特殊矩阵初始化 {#特殊矩阵初始化}

静态矩阵 {#静态矩阵}

一下几个函数均为静态矩阵调用的初始化函数,动态矩阵调用会报错:

  • 零阵:类静态成员函数Zero()
  • 常量矩阵:Constant(rows, cols, value)
  • 随机矩阵:Random()
  • 单位矩阵:Identity()
动态矩阵 {#动态矩阵}

| 函数 | 含义 | |-----------------|--------| | setZero() | 矩阵归零 | | setConstant() | 矩阵归常数 | | setIdentity() | 矩阵归单位阵 | | setOnes() | 矩阵归一 | | setRandom() | 矩阵随机数 |

动态向量函数 {#动态向量函数}
  • 仅能在向量类型数据中使用

| 函数 | 含义 | |------------------|-----------------| | setLinSpaced() | 填充线性间隔的数据 | | setUnit() | 指定向量位置数据置1,其余为0 |

索引数据 {#索引数据}

单个数据 {#单个数据}

  • 主要数据的存取和修改都是通过重载的括号运算符完成的

  • 对于二维矩阵,下标顺序为 row col

  • 对于向量,则只有向量下标需要传入

块操作 {#块操作}

  • 语法:

| 块 | 动态矩阵 | 静态矩阵 | |------------------------|--------------------------|---------------------------| | 尺寸 (p, q) 左上角坐标 (i, j) | matrix.block(i,j,p,q); | matrix.block<p,q>(i,j); |

  • 示例:

行列操作 {#行列操作}

  • 语法:

| 操作 | 方法 | |-------|------------------| | 第 i 行 | matrix.row(i); | | 第 i 列 | matrix.col(j); |

  • 示例:

角操作 {#角操作}

  • 语法:

| 块操作 | 动态矩阵语法 | 静态矩阵语法 | |--------------|----------------------------------|------------------------------------| | 左上角 p 行 q 列 | matrix.topLeftCorner(p,q); | matrix.topLeftCorner<p,q>(); | | 左下角 p 行 q 列 | matrix.bottomLeftCorner(p,q); | matrix.bottomLeftCorner<p,q>(); | | 右上角 p 行 q 列 | matrix.topRightCorner(p,q); | matrix.topRightCorner<p,q>(); | | 右下角 p 行 q 列 | matrix.bottomRightCorner(p,q); | matrix.bottomRightCorner<p,q>(); | | 前 q 行 | matrix.topRows(q); | matrix.topRows<q>(); | | 后 q 行 | matrix.bottomRows(q); | matrix.bottomRows<q>(); | | 前 p 列 | matrix.leftCols(p); | matrix.leftCols<p>(); | | 后 p 列 | matrix.rightCols(q); | matrix.rightCols<q>(); | | 第 i 列开始取 q 列 | matrix.middleCols(i,q); | matrix.middleCols<q>(i); | | 第 i 行开始取 q 行 | matrix.middleRows(i,q); | matrix.middleRows<q>(i); |

  • 示例:

向量块操作 {#向量块操作}

| 块操作 | 动态矩阵语法 | 静态矩阵语法 | |-------------------|------------------------|-------------------------| | 向量前 n 个数据 | vector.head(n); | vector.head<n>(); | | 向量后 n 个数据 | vector.tail(n); | vector.tail<n>(); | | 向量从下标 i 开始的 n 个数据 | vector.segment(i,n); | vector.segment<n>(i); |

常用操作 {#常用操作}

  • 大多数情况下,Eigen 要求操作的数据类型一致

布尔归约 {#布尔归约}

| 操作 | 语法 | 示例 | |--------------------------|----------------|-----------------| | 转置 | .transpose() | v.transpose() | | 所有元素为 true(非0),返回 bool 值 | all() | m.all() | | 存在元素为 true(非0),返回 bool 值 | any() | m.any() | | 统计 true(非0) 的个数 | count() | m.count() |

数据类型转换 {#数据类型转换}

| 操作 | 语法 | 示例 | |----------------|-------------------|------------------------| | 数据类型转换为 double | .cast<double>() | A.cast<double>() | | 数据类型转换为 float | .cast<float>() | A.cast<float>() | | 数据类型转换为 int | .cast<int>() | A.cast<int>() | | 数据类型转换为实部 | .real() | A.real() | | 数据类型转换为虚部 | .imag() | A.imag() | | 内存数据转 Eigen | Map<>() | Map<Matrix3i>(array) |

  • 内存数据转 Eigen:

Vector 向量操作 {#Vector-向量操作}

| 操作 | 语法 | 示例 | |-------|------------------|-------------------| | 点乘 | dot() | v.dot(w) | | 叉乘 | .cross() | v.cross(w) | | 元素个数 | .size() | v.size() | | 生成对角阵 | .asDiagonal() | v.asDiagonal() | | 向量平方和 | .squaredNorm() | v.squaredNorm() | | 向量模长 | .norm() | v.norm() |

Matrix 矩阵操作 {#Matrix-矩阵操作}

| 操作 | 语法 | 示例 | |-------|----------------|--------------------------------------------------| | 转置 | .transpose() | v.transpose() | | 共轭 | .conjugate() | a.conjugate() | | 共轭转置 | .adjoint() | a.adjoint() | | 元素个数 | .size() | a.size() | | 行数 | .rows() | a.rows() | | 列数 | .rols() | a.rols() | | 填充 | .fill() | a.fill(6) | | 交换行/列 | .swap() | m.block(0, 0, 2, 2).swap(n.block(2, 2, 2, 2)); | | 获取对角线 | .diagonal() | a.diagonal() | | 列向操作 | .colwise() | m.colwise().sum() | | 元素颠倒 | .reverse() | m.reverse() |

  • 赋值经过优化:

    行可以给列赋值,这个其实还是挺可怕的,需要格外小心。

  • 列向操作相当于 numpy 中的 axis=1,只对列方向做某种操作:

Matrix 矩阵运算 {#Matrix-矩阵运算}

| 操作 | 语法 | 示例 | |-----------|---------------------|-----------------------------------| | 矩阵相加 | + | a + b | | 矩阵相减 | - | a - b | | 负号 | - | - a | | 复合算子加 | += | a += b | | 复合算子减 | -= | a -= b | | 标量乘法 | * | matrix*scalar / scalar*matrix | | 标量除法 | / | matrix/scalar | | 复合算子乘 | *= | matrix*=scalar | | 复合算子除 | /= | matrix/=scalar | | 矩阵\向量乘法 | * | matrix*matrix, matrix*vector | | 矩阵求和 | .sum() | mat.sum() | | 所有元素乘积 | .prod() | mat.prod() | | 矩阵均值 | .mean() | mat.mean() | | 矩阵最小值 | .minCoeff() | mat.minCoeff() | | 矩阵最大值 | .maxCoeff() | mat.maxCoeff() | | 矩阵最小值,带位置 | .minCoeff(&r, &c) | mat.minCoeff(&r, &c) | | 矩阵最大值,带位置 | .maxCoeff(&r, &c) | mat.maxCoeff(&r, &c) | | 迹 | .trace() | mat.trace() | | 逐元素绝对值 | .cwiseAbs() | mat.cwiseAbs() | | 逐元素相乘 | .cwiseProduct() | mat = mat.cwiseProduct(mat) | | 逐元素相除 | .cwiseQuotient() | mat = mat.cwiseQuotient(mat) | | 逐元素取倒数 | .cwiseInverse() | mat.cwiseInverse() | | 逐元素开根号 | .cwiseSqrt() | mat.cwiseSqrt() | | 逐元素最大值 | .cwiseMax(m) | mat.cwiseMax(mat2) | | 逐元素最小值 | .cwiseMin(m) | mat.cwiseMin(mat2) | | 元素平方和 | .squaredNorm() | mat.squaredNorm() | | 矩阵二阶范数 | .norm() | mat.norm() | | p 阶范数 | .lpNorm<p>() | mat.lpNorm<3>() |

  • 最大值、最小值

    返回最大、最小值,同时定位位置

Array 数组运算 {#Array-数组运算}

| 操作 | 语法 | 示例 | |-----------|----------------------|-------------------------------| | 逐元素相乘 | * | a * b | | 逐元素相除 | / | a * b | | 矩阵相加 | + | a + b | | 矩阵相减 | - | a - b | | 负号 | - | - a | | 复合算子加 | += | a += b | | 复合算子减 | -= | a -= b | | 逐元素比较 | <, >, >=, <=, == | a < b | | 逐元素标量计算 | +, -, *, / | a + 3 | | 逐元素标量复合计算 | +=, -=, *=, /= | a /= 3 | | 逐元素取倒数 | .inverse() | a.inverse() | | 逐元素 sin | .sin() | a.sin() | | 逐元素 cos | .cos() | a.cos() | | 逐元素乘方 | .pow(s) | a.pow(3) | | 逐元素平方 | .square() | a.square() | | 逐元素立方 | .cube() | a.cube() | | 逐元素开根号 | .sqrt() | a.sqrt() | | 逐元素计算自然指数 | .exp() | a.exp() | | 逐元素计算自然对数 | .log() | a.log() | | 逐元素最小值 | .min() | a.min(b) | | 逐元素最大值 | .max() | a.max(b) | | 逐元素绝对值 | .abs() | a.abs() | | 逐元素选择 | .select() | (R.array() < s).select(P,Q) |

  • 逐元素比较:

  • 逐元素选择:

矩阵分解与解线性方程组 {#矩阵分解与解线性方程组}

矩阵分解 {#矩阵分解}

| 分解方法 | 语法 | 输出 | |------------|-----------|-------------------------------------------------| | 柯列斯基分解 | .ldlt() | .matrixL() and .matrixD() | | LLT 分解 | .llt() | .matrixL() | | 部分旋转的 LU分解 | .lu() | .matrixL() and .matrixU() | | QR 分解 | .qr() | .matrixQ() and .matrixR() | | SVD 分解 | .svd() | .matrixU(), .singularValues(), and .matrixV() |

求解线性方程组 {#求解线性方程组}

  • 对应不同矩阵分解方式,有不同的解方程组的方法

| 语法 | 描述 | |---------------------------|-------------------------------------------| | x = A.ldlt().solve(b)); | A sym. p.s.d. #include <Eigen/Cholesky> | | x = A.llt() .solve(b)); | A sym. p.d. #include <Eigen/Cholesky> | | x = A.lu() .solve(b)); | Stable and fast. #include <Eigen/LU> | | x = A.qr() .solve(b)); | No pivoting. #include <Eigen/QR> | | x = A.svd() .solve(b)); | Stable, slowest. #include <Eigen/SVD> |

特征值特征向量 {#特征值特征向量}

  • 特征值:

  • 特征向量:

混淆问题 {#混淆问题}

  • 使用eval()函数解决把右值赋值为一个临时矩阵,再赋给左值时可能有造成的混淆。如:
  • 原地操作的一类函数:

| 普通函数 | inplace函数 | |-------------------------|--------------------------------| | MatrixBase::adjoint() | MatrixBase::adjointInPlace() | | DenseBase::reverse() | DenseBase::reverseInPlace() | | LDLT::solve() | LDLT::solveInPlace() | | LLT::solve() | LLT::solveInPlace() | | TriangularView::solve() | TriangularView::solveInPlace() | | DenseBase::transpose() | DenseBase::transposeInPlace() |

  1. 当相同的矩阵或array出现在等式左右时,容易出现混淆
  2. 当确定不会出现混淆时,可以使用noalias()
  3. 混淆出现时,可以使用eval()xxxInPlace()函数解决

参考资料 {#参考资料}



文章链接:
https://www.zywvvd.com/notes/coding/cpp/eigen/eigen-usage/

赞(9)
未经允许不得转载:工具盒子 » Eigen 使用教程