主要内容

本页采用了机器翻译。点击此处可查看最新英文版本。

使用 parfeval 进行参数扫描时的绘图

此示例显示如何使用 parfeval 执行并行参数扫描,并在使用 DataQueue 对象进行计算时发回结果。

parfeval 不会阻止 MATLAB,因此您可以在进行计算时继续工作。

该示例对 Lorenz 常微分方程系统执行参数扫描,参数为 σρ,并展示了该系统的混沌性质。

ddtx=σ(y-z)ddty=x(ρ-z)-yddtz=xy-βx

设置并行环境

使用 parpool 函数创建一个并行线程工作单元池。

parpool("Threads");
Starting parallel pool (parpool) using the 'Threads' profile ...
Connected to parallel pool with 6 workers.

创建参数网格

定义您想要在参数扫描中探索的参数范围。

gridSize = 40;
sigma = linspace(5,45,gridSize);
rho = linspace(50,100,gridSize);
beta = 8/3;

使用 meshgrid 函数创建二维参数网格。

[rho,sigma] = meshgrid(rho,sigma);

执行并行参数扫描

定义参数后,即可执行并行参数扫描。

为了直观地显示参数扫描的中期结果,请创建一个表面图。请注意,使用 Z 初始化表面的 NaN 组件会创建一个空图。

figure;
surface = surf(rho,sigma,NaN(size(sigma)));
xlabel('\rho','Interpreter','Tex')
ylabel('\sigma','Interpreter','Tex')

要从工作单元发送临时数据,请创建一个 DataQueue 对象。设置一个函数,每次工作单元使用 afterEach 函数发送数据时更新表面图。updatePlot 函数是示例末尾定义的支持函数。

Q = parallel.pool.DataQueue;
afterEach(Q,@(data) updatePlot(surface,data));

当您分配工作量时,parfeval 的工作效率会更高。为了分配工作负载,将要探索的参数分组到各个分区中。对于此示例,使用冒号运算符 (step) 分成大小为 : 的均匀分区。结果数组 partitions 包含分区的边界。请注意,必须添加最后一个分区的终点。

step = 100;
partitions = [1:step:numel(sigma),numel(sigma)+1]
partitions = 1×17

           1         101         201         301         401         501         601         701         801         901        1001        1101        1201        1301        1401        1501        1601

为了获得最佳性能,请尝试分成以下分区:

  • 足够大,以至于计算时间与调度分区的开销相比很大。

  • 足够小,以便有足够的分区来让所有工作单元忙碌。

为了表示并行工作单元上的函数执行并保存其结果,请使用 future 对象。

f(1:numel(partitions)-1) = parallel.FevalFuture;

使用 parfeval 函数将计算卸载到并行工作单元。parameterSweep 是在此脚本末尾定义的辅助函数,用于在要探索的参数分区上解决 Lorenz 系统。它有一个输出参量,因此必须将 1 指定为 parfeval 中的输出数量。

for ii = 1:numel(partitions)-1
    f(ii) = parfeval(@parameterSweep,1,partitions(ii),partitions(ii+1),sigma,rho,beta,Q);
end

parfeval 不会阻止 MATLAB,因此您可以在进行计算时继续工作。工作单元并行计算,并在中间结果可用时立即通过 DataQueue 发送。

如果您想要阻止 MATLAB 直到 parfeval 完成,请在 future 对象上使用 wait 函数。当后续代码依赖于 wait 的完成时,使用 parfeval 函数很有用。

wait(f);

parfeval 完成计算后,wait 也完成,您就可以执行更多代码。例如,绘制 Lorenz 系统解决方案的选择。使用 fetchOutputs 函数检索存储在 future 对象中的结果。

results = fetchOutputs(f);
idxs = randperm(numel(results),4);
figure
for n = 1:numel(idxs)
    nexttile
    a = results{idxs(n)};
    plot3(a(:,1),a(:,2),a(:,3))
    grid on
    xlabel("x")
    ylabel("y")
    zlabel("z")
    title("Lorenz System Solution", ...
        "\rho = "+ num2str(rho(idxs(n)),'%5.2f') + " \sigma = "+ num2str(sigma(idxs(n)),'%5.2f'),Interpreter="tex")
end

如果您的参数扫描需要更多的计算资源并且您可以访问集群,那么您可以扩大您的 parfeval 计算。有关详细信息,请参阅从桌面扩展到集群

定义辅助函数

定义一个辅助函数,用于在要探索的参数分区上求解洛仑兹方程组。使用 send 对象上的 DataQueue 函数将中间结果发送到 MATLAB 客户端。

function results = parameterSweep(first,last,sigma,rho,beta,Q)
    results = cell(last-first,1);
    for ii = first:last-1
        lorenzSystem = @(t,a) [sigma(ii)*(a(2) - a(1)); a(1)*(rho(ii) - a(3)) - a(2); a(1)*a(2) - beta*a(3)];
        [t,a] = ode45(lorenzSystem,[0 100],[1 1 1]);
        send(Q,[ii,a(end,3)]);
        results{ii-first+1} = a;
    end
end

定义另一个辅助函数,当新数据到达时更新曲面图。

function updatePlot(surface,data)
    surface.ZData(data(1)) = data(2);
    drawnow('limitrate');
end

另请参阅

| | |

主题