我正在建立一个具有多个(软)约束的稀疏线性系统。我正在将一些用于通过boost::ublas构建矩阵的代码转换为Eigen。 boost:ublas提供了一种方便的方法来创建具有已知(或估计的)非零数量的稀疏矩阵,并且具有相当快的运算符(int row,int col)来更新其元素。
问题如下:
我的系统有很多限制。作为一个天真的,``略微''的夸张示例,假设我有一个100x100的稀疏矩阵,具有500 nnz但有10亿个冗余约束(即,非零系数被修改了十亿次)。 setFromTriplets需要我存储10亿个系数,其中大部分将加起来形成我的500个非零系数集。那不是很有效,也不是内存友好的。
当然,我可以用std::map替换我的std::vector,并手动执行约束的累加,但这在某种程度上错过了拥有稀疏矩阵类的要点,这也不是很有效。
如果该元素已经存在,则不起作用。我的问题是要能够累积已经存在的系数。
那确实有效,并且将是我正在寻找的功能。但是,它比boost::ublas慢几个数量级。令我惊讶的是,我没有看到更好的功能。
我认为这是由于预先未知的非零数导致的,并迫使进行多次重新分配(实际上是这样)。但是,使用SparseMatrix::reserve()无效,因为它是仅适用于压缩矩阵的函数(在断言之前,源中的注释说“在非压缩模式下此函数没有意义”)。 ..并且,如文档所述:“将新元素插入SparseMatrix会将其稍后转换为未压缩模式”。
在Eigen中构建稀疏矩阵同时仍然能够多次更新其系数的最有效方法是什么?
谢谢
[编辑:示例用例:具有10个非零的10x10矩阵。为简单起见,矩阵是对角线]
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());
=>对内存不友好...
最佳答案
为了使insert
的coeffRef
高效,您需要使用mat.reserve(nnz)
保留足够的空间,其中nnz
是一个Eigen::VectorXi
,其中包含每列非零的估计数量。最好稍微高估这些数字,以避免大量的重新分配/拷贝。另一个补充技巧是确保您首次访问(i,j)
元素时,此元素是j
列的最后一个元素。
如果您可以轻松地计算稀疏模式,那么另一种方法是用值的0填充唯一三元组的 vector ,然后coeffRef将很快。