Eigen中的稀疏矩阵构建

8
我正在构建一个具有多个(软)约束的稀疏线性系统。我正在将一些使用boost::ublas构建矩阵的代码转换为Eigen。boost:ublas有一种方便的方式可以创建具有已知(或估计)非零数的稀疏矩阵,并且具有相当快的operator(int row, int col)操作符来更新其元素。
问题如下:
- 使用SparseMatrix::setFromTriplets: 我的系统有许多约束条件。作为一个天真的、略微夸张的例子,假设我有一个100x100的稀疏矩阵,其中有500个非零元素,但有10亿个冗余约束(即,非零系数被修改了10亿次)。setFromTriplets要求我存储10亿个系数,其中大部分将被加起来形成我的500个非零系数集。这不是很高效也不友好。 当然,我可以用std::map替换我的std::vector,并手动执行约束的累积,但这在某种程度上忽略了拥有稀疏矩阵类的重点,而且这也不是很高效。
- 使用SparseMatrix::insert(i,j,val): 如果该元素已经存在,则无法使用。我的问题是能够累加已经存在的系数。 - 使用SparseMatrix::coeffRef(i, j): 这样做是可行的,也是我正在寻找的函数。然而,它比boost::ublas慢几个数量级。我很惊讶没有看到更好的函数。 我认为这是因为非零元素的数量事先不知道,强制进行多次重新分配(实际上就是这样发生的)。然而,使用SparseMatrix::reserve()没有效果,因为它是一个仅适用于压缩矩阵的函数(源代码中的注释说"此函数在非压缩模式下没有意义",在断言之前)。...而且,正如文档所说的"向SparseMatrix插入新元素会将其转换为未压缩模式"。
在Eigen中构建稀疏矩阵的最有效方法是什么,同时还能够多次更新其系数?
谢谢
SparseMatrix<double> mat(10, 10);
for (int i=0; i<10; i++) {
  for (int j=0; j<1000000; j++) {
    mat.coeffRef(i, i) += rand()%10;
  }
}

=>的功能是可以实现的,但当处理更大的矩阵和更现实的情况时,速度要慢得多,与ublas operator()相比。

std::vector<Eigen::Triplet<double> > triplets(10000000);
int k=0;
for (int i=0; i<10; i++) {
  for (int j=0; j<1000000; j++) {
    triplets[k++] = Eigen::Triplet<double>(i,i,rand()%10);
  }
}
SparseMatrix<double> mat(10, 10);
mat.setFromTriplets(triplets.begin(), triplets.end());

=> 不友好的内存...


我刚刚添加了一个简单的例子。 - nbonneel
你的例子并没有传达任何意义。我的意思是,你有一个稀疏矩阵,然后你对它进行了一些操作,接下来呢?你改变了矩阵吗?你洗牌系数吗?你添加了一组新的系数吗? - Escualo
我的实际用例涉及内在图像分解中的非局部约束,我不确定它是否与问题非常相关。一旦我有了矩阵,我就会解一个带有给定右手边的线性系统,但同样,我不确定这有什么帮助。我的问题实际上是能够执行与上述非常相似的操作(除了矩阵不是对角线矩阵且更大,系数也不是随机的)。 - nbonneel
1
你尝试过使用 mat.coeffRef(i,j) += v_ij; 吗?(参见 http://eigen.tuxfamily.org/dox-devel/group__TutorialSparse.html#TutorialSparseFilling) - Escualo
1
谢谢你的建议。但是,高效地做到这一点最终等同于创建一个新的SparseMatrix类,以便能够初始化Eigen::SparseMatrix。实际上,这就是我当前的代码所做的:它使用ublas作为创建矩阵的便捷方式,然后将其转换为Eigen::SparseMatrix。我想摆脱这个奇怪的步骤...感谢你的帮助! - nbonneel
显示剩余3条评论
1个回答

7
为了使coeffRefinsert更加高效,您需要使用mat.reserve(nnz)来预留足够的空间,其中nnz是一个包含每列非零元素数量的估计值的Eigen::VectorXi。最好稍微高估这些数字,以避免大量的重新分配/复制。另一个补充技巧是确保第一次访问元素(i,j)时,该元素是列j中的最后一个元素。
如果您可以轻松地计算稀疏模式,则另一种选择是填充一个带有0值的唯一三元组向量,然后coeffRef将变得更快。

谢谢!文档并不是很清楚:我认为coeffRef会使矩阵变成未压缩状态,“向SparseMatrix插入新元素将其转换为未压缩模式”,而reserve函数则说明它对于未压缩的矩阵没有意义(虽然我不确定为什么)。谢谢! - nbonneel
有两个保留签名,一个接受压缩模式的 size_t,另一个接受未压缩模式的向量表达式。 - ggael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接