Eigen只读稀疏块子表达式报错?如何正确编写代码?
解决Eigen稀疏矩阵块赋值只读错误
错误根源
Eigen的SparseMatrix块表达式(比如topLeftCorner)属于只读子表达式,无法直接通过赋值运算符=修改。这是因为稀疏矩阵基于非零元素列表存储,块操作无法直接映射到底层存储的修改逻辑,触发了只读断言限制。
可行解决方案(无需转换为密集矩阵)
方法1:用+=结合pruned()实现块赋值
利用稀疏矩阵的加法操作,搭配pruned()过滤零元素(避免冗余存储),完成块区域的元素写入:
Eigen::SparseMatrix<double> top_left = K + Eigen::SparseMatrix<double>(K.transpose()); Eigen::SparseMatrix<double> block_laplacian_sq; block_laplacian_sq.resize(top_left.rows() + C.rows(), top_left.cols() + C.rows()); // 通过加法操作写入左上角块的非零元素 block_laplacian_sq.topLeftCorner(top_left.rows(), top_left.cols()) += top_left.pruned();
方法2:用setFromTriplets批量构建完整矩阵
如果需要合并多个块,建议统一收集所有非零元素的三元组(行、列、值),一次性构建大矩阵,效率更高:
Eigen::SparseMatrix<double> top_left = K + Eigen::SparseMatrix<double>(K.transpose()); int total_rows = top_left.rows() + C.rows(); int total_cols = top_left.cols() + C.rows(); std::vector<Eigen::Triplet<double>> triplets; // 预分配足够空间,避免频繁扩容 triplets.reserve(top_left.nonZeros() + /*其他三个块的非零元素总数*/); // 加入左上角块的元素 for (int k = 0; k < top_left.outerSize(); ++k) { for (Eigen::SparseMatrix<double>::InnerIterator it(top_left, k); it; ++it) { triplets.emplace_back(it.row(), it.col(), it.value()); } } // 依次加入其他三个块的元素(需根据块位置调整行/列索引) // 示例:右上角块(假设为top_right) // for (...) { triplets.emplace_back(it.row(), it.col() + top_left.cols(), it.value()); } // 左下角块(假设为bottom_left) // for (...) { triplets.emplace_back(it.row() + top_left.rows(), it.col(), it.value()); } // 右下角块(假设为bottom_right) // for (...) { triplets.emplace_back(it.row() + top_left.rows(), it.col() + top_left.cols(), it.value()); } // 构建最终稀疏矩阵 Eigen::SparseMatrix<double> block_laplacian_sq(total_rows, total_cols); block_laplacian_sq.setFromTriplets(triplets.begin(), triplets.end());
注意事项
- 方法1适合单个块的快速赋值,
pruned()可指定阈值(如pruned(1e-9))过滤数值噪声。 - 方法2适合多块合并场景,能减少多次稀疏矩阵操作的性能开销。
内容的提问来源于stack exchange,提问作者Cedric Martens
相关产品推荐
相关产品推荐

