Python/Numpy——加速放射性衰变的蒙特卡罗方法
我试图优化放射性同位素蒙特卡罗衰变时间的生成。 给出了半衰期为t12的同位素原子的nsims,每个同位素何时衰变? 我试图通过使用单个Python/Numpy——加速放射性衰变的蒙特卡罗方法,python,performance,numpy,montecarlo,Python,Performance,Numpy,Montecarlo,我试图优化放射性同位素蒙特卡罗衰变时间的生成。 给出了半衰期为t12的同位素原子的nsims,每个同位素何时衰变? 我试图通过使用单个numpy.random.random调用(我称此方法为并行)一次为所有未衰变原子生成随机数来优化此方法,但我希望仍能获得更多性能。我还展示了一种方法,该方法对每个同位素分别(系列)进行计算 我对任何比纯python的parallel函数(即不是cython)性能更好的建议感兴趣。 该方法已经大大改进了计算大型NSIM的serial方法 从最初的“并行”(矢量化是
numpy.random.random
调用(我称此方法为并行)一次为所有未衰变原子生成随机数来优化此方法,但我希望仍能获得更多性能。我还展示了一种方法,该方法对每个同位素分别(系列)进行计算
我对任何比纯python的parallel
函数(即不是cython)性能更好的建议感兴趣。
该方法已经大大改进了计算大型NSIM的serial
方法
从最初的“并行”(矢量化是正确的词)执行中,仍有一些速度提升 注意,这是一种微观管理,但它仍然会带来少量的性能提升
import numpy as np
t12 = 3.1*60.
dt = 0.01
ln2 = np.log(2)
s = 98765
def parallel(nsims): # your code, unaltered, except removed inaccurate timing method
decay_time = np.zeros(nsims)
t = dt
np.random.seed(s) # also had to add a seed to get comparable results
while 0 in decay_time:
inot_decayed = np.where(decay_time == 0)[0]
idecay_check = np.random.random(len(inot_decayed)) > np.exp(-ln2*dt/t12)
decay_time[inot_decayed[np.where(idecay_check==True)[0]]] = t
t += dt
return decay_time
def parallel_micro(nsims): # micromanaged code
decay_time = np.zeros(nsims)
t = dt
half_time = np.exp(-ln2*dt/t12) # there was no need to calculate this again in every loop iteration
np.random.seed(s) # fixed seed to get comparable results
while 0 in decay_time:
inot_decayed = np.where(decay_time == 0)[0] # only here you need the call to np.where
# to my own surprise, len(some_array) is quicker than some_array.size (function lookup vs attribute lookup)
idecay_check = np.random.random(len(inot_decayed)) > half_time
decay_time[inot_decayed[idecay_check]] = t # no need for another np.where and certainly not for another boolean comparison
t += dt
return decay_time
您可以使用运行计时测量。分析将告诉您这里的瓶颈是调用np.where
知道瓶颈是np。其中
,您可以这样摆脱它:
def parallel_micro2(nsims):
decay_time = np.zeros(nsims)
t = dt
half_time = np.exp(-ln2*dt/t12)
np.random.seed(s)
indices = np.where(decay_time==0)[0]
u = len(indices)
while u:
decayed = np.random.random(u) > half_time
decay_time[indices[decayed]] = t
indices = indices[np.logical_not(decayed)]
u = len(indices)
t += dt
return decay_time
这确实会带来相当大的速度提升:
In [2]: %timeit -n1 -r1 parallel_micro2(1e4)
1 loops, best of 1: 7.81 s per loop
In [3]: %timeit -n1 -r1 parallel_micro(1e4)
1 loops, best of 1: 29 s per loop
In [4]: %timeit -n1 -r1 parallel(1e4)
1 loops, best of 1: 33.5 s per loop
完成优化后,不要忘记取消对np.random.seed
的调用
In [2]: %timeit -n1 -r1 parallel_micro2(1e4)
1 loops, best of 1: 7.81 s per loop
In [3]: %timeit -n1 -r1 parallel_micro(1e4)
1 loops, best of 1: 29 s per loop
In [4]: %timeit -n1 -r1 parallel(1e4)
1 loops, best of 1: 33.5 s per loop