在 MATLAB 中循环添加多个稀疏矩阵的最快方法

Fastest way to add multiple sparse matrices in a loop in MATLAB

我有一个代码可以在循环中重复计算稀疏矩阵(准确地说,它执行了 13472 次计算)。这些稀疏矩阵中的每一个都是唯一的。

每次执行后,它将新计算的稀疏矩阵添加到原来的稀疏零矩阵中。

添加完所有 13742 个矩阵后,代码退出循环,程序终止。

添加稀疏矩阵时出现代码瓶颈。我制作了一个虚拟版本的代码,它表现出与我的真实代码相同的行为。它由一个 MATLAB 函数和下面给出的脚本组成。

(1) 生成稀疏矩阵的函数:

function out = test_evaluate_stiffness(n)
ind = randi([1 n*n],300,1);
val = rand(300,1);
[I,J] = ind2sub([n,n],ind);
out = sparse(I,J,val,n,n);
end

(2) 主脚本(程序)

% Calculate the stiffness matrix
n=1000;
K=sparse([],[],[],n,n,n^2);
tic
for i=1:13472
    temp=rand(1)*test_evaluate_stiffness(n);
    K=K+temp;
end
fprintf('Stiffness Calculation Complete\nTime taken = %f s\n',toc)

我对稀疏矩阵运算不是很熟悉,所以我可能在这里遗漏了一个关键点,这可能会大大加快我的代码速度。

我在代码中是否以合理的方式处理刚度矩阵的更新?有没有我应该使用稀疏的另一种方法可以得到更快的解决方案?

下面还提供了分析器报告:

如果您只需要这些矩阵的总和,而不是单独构建所有矩阵然后对它们求和,只需连接向量 IJvals 并调用sparse只有一次。如果[I,J]中有重复行[i,j],对应的值S(i,j)会自动求和,所以代码是绝对等价的。由于调用 sparse 涉及对排序算法的内部调用,因此您可以节省 13742-1 次中间排序,并且只需要一次即可。


这涉及将 test_evaluate_stiffness 的签名更改为输出 [I,J,val]:

function [I,J,val] = test_evaluate_stiffness(n)

并删除行 out = sparse(I,J,val,n,n);.

然后您将其他功能更改为:

n = 1000;
[I,J,V] = deal([]);
tic;
for i = 1:13472
    [I_i, J_i, V_i] = test_evaluate_stiffness(n);
    nE = numel(I_i);
    I(end+(1:nE)) = I_i;
    J(end+(1:nE)) = J_i;
    V(end+(1:nE)) = rand(1)*V_i;
end
K = sparse(I,J,V,n,n);
fprintf('Stiffness Calculation Complete\nTime taken = %f s\n',toc);

如果您提前知道 test_evaluate_stiffness 的输出长度,您可以通过预分配数组 IJV 来节省一些时间使用适当大小的 zeros 矩阵并使用类似以下内容设置它们:

I((i-1)*nE + (1:nE)) = ...
J((i-1)*nE + (1:nE)) = ...
V((i-1)*nE + (1:nE)) = ...

The biggest remaining computation, taking 11s, is the sparse operation on the final I,J,V vectors so I think we've taken it down to the bare bones.

几乎...但最后一个技巧:如果您可以创建向量以使 J 升序排序,那么您将大大提高 sparse 调用的速度,大约提高 4 倍根据我的经验。

(如果 I 排序更容易,则创建转置矩阵 sparse(J,I,V) 并在之后取消转置。)