【发布时间】:2019-01-11 18:38:08
【问题描述】:
分子模拟中的能量计算本质上充满了“for”循环。传统上,每个原子/分子的坐标存储在数组中。数组很容易矢量化,但结构很适合编码。将分子视为单独的对象,每个对象都有自己的坐标和其他属性,就簿记而言非常方便和清晰。
我使用的是 Python 3.6 版
我的问题是,当我使用对象数组时,我无法弄清楚如何对计算进行矢量化......似乎无法避免 for 循环。我是否有必要使用数组来利用 numpy 并矢量化我的代码?
这是一个使用数组的 python 示例(代码的第 121 行),并显示了快速(numpy)和慢速('normal')python 能量计算。
https://github.com/Allen-Tildesley/examples/blob/master/python_examples/mc_lj_module.py
使用 numpy 加速方法计算速度要快得多,因为它是矢量化的。
如果我不使用数组,而是使用一组对象,每个对象都有自己的坐标,我将如何向量化能量计算?这似乎需要使用较慢的 for 循环。
这是一个简单的示例代码,其中包含一个运行缓慢的 for 循环版本,以及一个无效的向量化尝试:
import numpy as np
import time
class Mol:
num = 0
def __init__(self, r):
Mol.num += 1
self.r = np.empty((3),dtype=np.float_)
self.r[0] = r[0]
self.r[1] = r[1]
self.r[2] = r[2]
""" Alot more useful things go in here in practice"""
################################################
# #
# Main Program #
# #
################################################
L = 5.0 # Length of simulation box (arbitrary)
r_cut_box_sq = L/2 # arbitrary cutoff - required
mol_list=[]
nmol = 1000 # number of molecules
part = 1 # arbitrary molecule to interact with rest of molecules
""" make 1000 molecules (1 atom per molecule), give random coordinates """
for i in range(nmol):
r = np.random.rand(3) * L
mol_list.append( Mol( r ) )
energy = 0.0
start = time.time()
################################################
# #
# Slow but functioning loop #
# #
################################################
for i in range(nmol):
if i == part:
continue
rij = mol_list[part].r - mol_list[i].r
rij = rij - np.rint(rij/L)*L # apply periodic boundary conditions
rij_sq = np.sum(rij**2) # Squared separations
in_range = rij_sq < r_cut_box_sq
sr2 = np.where ( in_range, 1.0 / rij_sq, 0.0 )
sr6 = sr2 ** 3
sr12 = sr6 ** 2
energy += sr12 - sr6
end = time.time()
print('slow: ', end-start)
print('energy: ', energy)
start = time.time()
################################################
# #
# Failed vectorization attempt #
# #
################################################
""" The next line is my problem, how do I vectorize this so I can avoid the for loop all together?
Leads to error AttributeError: 'list' object has no attribute 'r' """
""" I also must add in that part cannot interact with itself in mol_list"""
rij = mol_list[part].r - mol_list[:].r
rij = rij - np.rint(rij/L)*L # apply periodic boundary conditions
rij_sq = np.sum(rij**2)
in_range = rij_sq < r_cut_box_sq
sr2 = np.where ( in_range, 1.0 / rij_sq, 0.0 )
sr6 = sr2 ** 3
sr12 = sr6 ** 2
energy = sr12 - sr6
energy = sum(energy)
end = time.time()
print('faster??: ', end-start)
print('energy: ', energy)
最后
如果在能量计算中,任何可能的解决方案都会受到影响,有必要遍历每个分子中的每个原子,现在每个分子的原子数超过 1 个,并且并非所有分子都具有相同数量的原子,因此有用于分子-分子相互作用的双 for 循环,而不是当前使用的简单的对-对相互作用。
【问题讨论】:
-
您希望矢量化哪些具体计算
-
最后一个for循环中的能量计算。能量是所有对相互作用的总和......我想同时计算所有对相互作用,然后在最后总结它们,而不是循环遍历所有对相互作用
-
您能否让您的示例仅包含您寻求矢量化的部分...这对未来的用户根本没有用。您的问题很可能是伪装成新问题的重复问题。
-
第一个 for 循环工作并为任何工作的矢量化提供了一个基线进行比较。没有一段代码可以测试是没有意义的。一切都标明了。
-
numpy中最快的计算是那些使用编译代码的计算,使用数字dtypes- 元素乘法和矩阵乘法,各种ufunc,以及避免复制的索引。numba和cython可用于编译更多的迭代计算。但作为一般规则,这些工具不能很好地处理面向对象的代码。对象数组需要与对象列表相同的迭代(并且速度稍慢)。而cython无法将计算简化为纯 C。
标签: python arrays numpy vectorization