Skip to content

Eigen稀疏矩阵格式及使用方法

finish time:2025-05-20 15:45

参考链接:

  • https://zhuanlan.zhihu.com/p/188700729

  • https://eigen.tuxfamily.org/dox/group__SparseQuickRefPage.html

  • https://eigen.tuxfamily.org/dox/group__TutorialSparse.html

详细介绍Eigen中的稀疏矩阵格式、定义方法以及索引方法。

稀疏矩阵格式

压缩存储:只存储非零值

存储矩阵的一般方法是采用二维数组,其优点是可以随机地访问每一个元素,因而能够容易实现矩阵的各种运算。

对于稀疏矩阵,它通常具有很大的维度,有时甚大到整个矩阵(零元素)占用了绝大部分内存

采用二维数组的存储方法既浪费大量的存储单元来存放零元素,又要在运算中浪费大量的时间来进行零元素的无效运算。因此必须考虑==对稀疏矩阵进行压缩存储(只存储非零元素)==

一、Eigen中的稀疏矩阵格式

Eigen主要支持以下几种稀疏矩阵存储格式:

  1. Compressed Sparse Column (CSC) - 压缩稀疏列格式
  2. 默认格式,也是最高效的格式
  3. 适合列优先操作

  4. Compressed Sparse Row (CSR) - 压缩稀疏行格式

  5. 适合行优先操作
  6. 按行对矩阵进行压缩的
  7. 需要通过SparseMatrix<double, RowMajor>指定
  8. 适用:常用于读入数据后进行稀疏矩阵计算,运算高效
  9. 优点:

    • 高效的稀疏矩阵算术运算
    • 高效的行切片
    • 快速地矩阵矢量积运算
  10. Triplet格式 (COO) - 坐标列表格式

  11. 用于构建稀疏矩阵的中间格式
  12. 每个非0值用(row, column, value)三元组列表表示
  13. 应用场景:
    • 主要用来创建矩阵,因为coo_matrix无法对矩阵的元素进行增删改等操作
    • 一旦创建之后,除了将之转换成其它格式的矩阵,几乎无法对其做任何操作和矩阵运算
  14. 优点:
    • 转换成其它存储格式很快捷简便,能与CSR / CSC格式的快速转换
    • 允许重复的索引(例如在1行1列处存了值2.0,又在1行1列处存了值3.0,则转换成其它矩阵时就是2.0+3.0=5.0)
  15. 缺点:
    • 不支持切片和算术运算操作;

稀疏矩阵的顺序:COO用于读取和构造稀疏矩阵变量->Coo转换为csr/csc进行矩阵操作和运算;

二、稀疏矩阵的定义方法

1. 直接定义稀疏矩阵

#include <Eigen/Sparse>

// 定义一个10x10的双精度稀疏矩阵(默认CSC格式)
Eigen::SparseMatrix<double> mat(10, 10);

// 定义一个行优先的稀疏矩阵
Eigen::SparseMatrix<double, Eigen::RowMajor> row_major_mat(10, 10);

2. 使用Triplet列表构建稀疏矩阵

#include <Eigen/Sparse>

// 定义Triplet列表
std::vector<Eigen::Triplet<double>> triplets;

// 添加非零元素 (行索引, 列索引, 值),索引是0-based
triplets.push_back(Eigen::Triplet<double>(0, 0, 1.0));
triplets.push_back(Eigen::Triplet<double>(1, 1, 2.0));
triplets.push_back(Eigen::Triplet<double>(2, 2, 3.0));

// 创建稀疏矩阵并填充
Eigen::SparseMatrix<double> mat(3, 3);
mat.setFromTriplets(triplets.begin(), triplets.end());

3. 从密集矩阵转换

Eigen::MatrixXd dense_mat = Eigen::MatrixXd::Random(5, 5);
Eigen::SparseMatrix<double> sparse_mat = dense_mat.sparseView();

三、稀疏矩阵的索引方法

1. 使用coeffRef()访问元素

Eigen::SparseMatrix<double> mat(3, 3);
mat.coeffRef(0, 0) = 1.0;  // 设置(0,0)元素
double val = mat.coeff(1, 1);  // 获取(1,1)元素

注意:coeffRef()会自动插入不存在的元素,而coeff()只是读取。

2. 使用insert()和prune()方法

mat.insert(0, 0) = 1.0;  // 高效插入新元素
mat.insert(1, 1) = 2.0;
mat.prune(0.0);  // 移除所有数值为0的元素

3. 迭代非零元素

// 外部迭代器
for (int k = 0; k < mat.outerSize(); ++k) {
    for (Eigen::SparseMatrix<double>::InnerIterator it(mat, k); it; ++it) {
        std::cout << "(" << it.row() << "," << it.col() << ") = " 
                  << it.value() << std::endl;
    }
}

4. 块操作

// 获取左上角2x2的子矩阵
Eigen::SparseMatrix<double> block = mat.block(0, 0, 2, 2);

四、稀疏矩阵的特殊操作

2. 获取稀疏矩阵信息

int nonZeros = mat.nonZeros();  // 非零元素数量
int rows = mat.rows();          // 行数
int cols = mat.cols();          // 列数

3. 稀疏矩阵运算

Eigen::SparseMatrix<double> mat1, mat2, result;

// 矩阵加法
result = mat1 + mat2;

// 矩阵乘法
result = mat1 * mat2;

// 标量乘法
result = mat1 * 2.0;

五、性能考虑

  1. 对于一次性构建稀疏矩阵,使用Triplet格式最方便
  2. 对于逐步插入元素,使用insert()方法
  3. 频繁操作后调用prune()移除零元素