给定 MATLAB 中的大型稀疏(方形)矩阵,如何有效地提取它的带状或块对角线部分(固定大小)?
在原型设计和测试预处理器时,或者如果您想要一个快速而肮脏(但仍然相对有效)的预处理器,这些操作非常有用。有时,这些近似值可以与更复杂的预处理器(如ILU )竞争。
但是,几乎没有任何可用的实现,我能找到的答案需要首先将稀疏矩阵转换为完整矩阵(这对于大型矩阵不可行)或使用for循环(它很慢 - 这是 JIT 编译器无法实现的情况之一优化呢)。
给定 MATLAB 中的大型稀疏(方形)矩阵,如何有效地提取它的带状或块对角线部分(固定大小)?
在原型设计和测试预处理器时,或者如果您想要一个快速而肮脏(但仍然相对有效)的预处理器,这些操作非常有用。有时,这些近似值可以与更复杂的预处理器(如ILU )竞争。
但是,几乎没有任何可用的实现,我能找到的答案需要首先将稀疏矩阵转换为完整矩阵(这对于大型矩阵不可行)或使用for循环(它很慢 - 这是 JIT 编译器无法实现的情况之一优化呢)。
(这个答案对 MATLAB 和 Octave 都有效,尽管我主要指的是 MATLAB)
有两只野兽要杀死;但让我们首先了解底层数据结构。MATLAB 和 Octave 以坐标 (COO) 格式存储稀疏矩阵,即稀疏矩阵S是长度等于 的非零条目数的三个数组的集合S:row包含非零条目的行坐标,col包含 的列坐标非零条目,最后val包含非零条目的值。例如,如果row(i)=1,col(i)=10则val(i)=3.14我们知道稀疏矩阵的第一行 - 第十列条目S是 3.14(这也是第 i 个非零值)。
我们可以使用以下find命令从 MATLAB 获取此信息:
[i,j,v] = find(S);
使用这些数组,很容易找到S. 我们只是寻找距离n主对角线小于或等于距离的条目:
idx = find(abs(i-j)<n);
然后可以构造的带状部分B:S
B = sparse(i(idx),j(idx),v(idx));
所以它只是三个步骤,不需要转换为完整的矩阵。这三个步骤可以替换为两个调用spdiags:首先,用于spdiags提取对角线和子/超对角线,然后spdiags与提取的数据一起使用来重建带状矩阵。在大多数情况下,该spdiags方法的工作速度明显更快,但前一种方法将使我们更好地理解第二部分的答案。
请注意,矩阵的 -blockn对角线部分包含在矩阵的n-banded 部分中。因此,类似的策略可以有效地提取n矩阵的 -block 对角线部分。主要观察结果是,根据行,有一些我们不想要的列条目。如果它是块的第一行,我们不希望对角线条目左侧的任何条目,如果它是块的最后一行,我们不希望对角线条目右侧的任何条目对角线入口。这些变化相对于块的行线性变化。这应该已经提醒我们模算术,如果mod(i(k),n)==0我们在一个块的最后一行,所以我们不应该接受任何非零条目kstj(k)>i(k). 我们可以为每一行得出相似的条件,这是我想出的一个简短(有点神秘)的解决方案来总结这些规则:
d = j - i;
idx=find( (d< n-(mod(i-1,n))) .* (d>=-(mod(i-1,n))));
现在,该数组idx包含落入任何对角块的所有非零条目的索引。S我们可以用与上面完全相同的方式构造块对角线部分:
B = sparse(i(idx),j(idx),v(idx));
我应该提一下,MATLAB 还提供了 command blkdiag。此命令在某种意义上更灵活,因为它允许创建具有可变块大小的块对角矩阵(上述步骤假定块大小固定n)。但是,没有内置方法可以提取稀疏矩阵的对角块。所以必须自己提取块,然后使用blkdiag. 即使这样,得到一个稀疏矩阵也不是一件容易的事。如果输入是密集矩阵的单元结构,则 MATLAB 返回一个密集矩阵。您应该确保其中至少一个是稀疏的,以保证 MATLAB 会返回一个稀疏矩阵。因此,据我所知,我上面描述的步骤是执行这些操作的最佳方法。有人可能在 MEX 中实现了它,它可以运行得更快或更节省内存,但我在我查看的一小部分互联网上找不到它。
我很想听听是否有人有改进此解决方案的想法。