2011-03-11 55 views
1

我有一个模拟我的博士论文以下挑战:高效的数学运算

我需要优化下面的代码:

repelling_forces = repelling_force_prefactor * np.exp(-(height_r_t/potential_steepness)) 

在此代码段'height_r_t'是一个真正的Numpy数组,'potential_steepness'是一个标量。 'repelling_force_prefactor'也是一个Numpy数组,它大部分为零,但是在预先计算的位置为1,在运行时不会改变(即Mask)。 很明显,代码是低效的,因为只有在'repelling_force_prefactor'非零的位置计算指数函数才更有意义。

问题是我该如何以最有效的方式做到这一点?

我到现在为止的唯一想法是使用'repelling_force_prefactor'将切片定义为'height_r_t',并将'np.exp'应用于这些切片。但是,我已经做出了切片慢的经验(不知道这是否正确),而且解决方案看起来很尴尬。

正如一个侧面说明'repelling_force_prefactor'中1到0的比例大约是1/1000,我在循环中运行它,所以效率非常重要。 (评论:我不会遇到使用Cython的问题,因为无论如何我都需要/想要学习它......但是我是新手,所以我需要一个好的指针/解释。)

回答

3

蒙面阵列正是为您的目的而实施的。

性能是一样的斯文的回答是:

height_r_t = np.ma.masked_where(repelling_force_prefactor == 0, height_r_t) 
repelling_forces = np.ma.exp(-(height_r_t/potential_steepness)) 

蒙面阵列的好处是,你不必切片和扩展阵列,大小始终是相同的,但numpy的自动知道不计算数组被屏蔽的exp。

另外,您可以用不同的掩码对数组进行求和,得到的数组具有掩码的交集。

+0

这是一个非常整洁,因为优雅的解决方案。另一个问题 - 也许你在最后一句中提到了这个问题。我可以使用这种方法在矩阵的不同部分进行操作吗?即像这样: height_part1 =身高(掩码1) height_part2 =身高(掩码2) – packoman 2011-03-12 19:34:53

+0

然后,然后如果我在height_part1或height_part2,像height_part1 = operation1(height_part1),做数学运算,将这些被高度体现? – packoman 2011-03-12 19:42:18

+0

是的,该掩码是存储在masked_array的'.mask'属性中的简单布尔数组。当你创建一个被屏蔽的数组时,你可以指定copy = False,以便你的操作被反映到原始数组中。但最好的策略取决于你的应用程序的细节。 – 2011-03-14 02:59:32

2

切片可能比计算所有指数要快得多。除了使用面膜repelling_force_prefactor直接切片的,我建议预先计算的指数它是非零,并将其用于切片:

# before the loop 
indices = np.nonzero(repelling_force_prefactor) 

# inside the loop 
repelling_forces = np.exp(-(height_r_t[indices]/potential_steepness)) 

现在repelling_forces将只包含非零的结果。如果必须用此值更新height_r_t的原始形状的某个数组,则可以再次使用indices进行切片,或使用np.put()或类似的函数。

在这种情况下,使用索引列表切片将比使用布尔掩码切片效率更高,因为索引列表的长度缩短了千分之一。实际上衡量表现当然取决于你。

+0

+1击败我。我只会补充说,有几个选择可以做到这一点,最好的做法是如果你的代码的这一部分是瓶颈,就要对每个选项进行基准测试。一般来说,我发现切片速度非常快,特别是如果比例是1/1000。 – JoshAdel 2011-03-11 17:30:51