使用 Python 最小化扩散模拟中使用的内存

计算科学 Python 麻木的 扩散 内存管理
2021-12-28 08:26:33

我最近正在处理一个扩散模拟项目,我想出了以下代码:

N = 10000000
num_steps = 100
dim = 3

particles2 = npr.uniform(-1, 1, (N, num_steps, dim))
particles = np.cumsum(particles2, axis=1)

简单说明一下:前3行是仿真参数;粒子数 (N)、模拟时间步数 ( num_step) 和模拟的维数 ( dim)。我通过将每个粒子视为随机游走器来模拟扩散,因此我使用该numpy.random模块为每个时间步生成一个随机平移向量,这样每个坐标就是一个介于之间的随机数。现在,在该行中,我生成一个填充了之间随机数的张量,然后在下一行中使用累积和来对不同的时间步进行求和。这与以下(更直观的)代码完全相同,但效率更高:11particles211

particles = np.zeros((N, num_steps, dim))

for i in range(N):  
    prev_vec = np.zeros((dim))

    for t in range(num_steps):
        trans_vec = npr.uniform(-1, 1, dim)
        particles[i, t, :] = prev_vec
        prev_vec = prev_vec + trans_vec

其中prev_vec代表“前一个向量”,trans_vec代表每个时间步的随机平移向量。

但是,我有一个问题 - 内存使用情况。由于我存储了每个粒子的整个轨迹,因此我(相对)快速达到了可用内存上限。对于分析,我只需要最后一个时间步,所以我并不需要整个轨迹。我在底部代码中看到了如何解决这个问题:

particles = np.zeros((N, dim))

for i in range(N):
    prev_vec = np.zeros((dim))

    for t in range(num_steps):
        trans_vec = npr.uniform(-1, 1, dim)
        prev_vec = prev_vec + trans_vec

    particles[i,:] = prev_vec

但我不知道如何在上面解决这个问题,更高效的代码。我想这样做,因为 2 个 for 循环确实使计算变慢。

1个回答

似乎您正在从一个极端走向另一个极端:您可能希望在没有 for 循环的情况下一次但是,您不想一次生成所有这些,因为您只需要最后两个。Nnum_steps

所以,我认为你正在寻找类似的东西:

import numpy as np
import numpy.random as npr

N = 10000000
dim = 3
num_steps = 100

# First, generating N initial particle positions (or you can initialize them to zeros)
particles = npr.uniform(-1, 1, (N, dim)) 

# start looping over time
for t in range(num_steps):
    # store the previous particles position
    prev_particles = particles
    # overwrite positions with the addition of random translation (no additional storage!)
    particles = np.add(particles,npr.uniform(-1,1,(N, dim)))  
    # notice, I could have saved the translation in the following way (commented out)
    # trans_vec = npr.uniform(-1,1,(N, dim)); particles = np.add(particles,trans_vec)
    # but that would have required additional storage.
    #   
    # do whatever you need with particles and prev_particles that store positions at the two preceding time_steps.

我可能有点误解了代码内部的内容,但是使用这种结构,您只在时间维度上执行 for 循环,您需要很多步骤,并且您希望通过获取来节省内存摆脱你不需要存储的东西。

上面的代码在不到一分钟的时间内在我的机器上运行,消耗不到 500 MB 的存储空间(这是有道理的,我有效地存储了 2 个大小为的实向量,这将导致大约 160 MB至少存储 + Python 增加的任何开销)。10106×1