晶胞内考虑对称性的距离矩阵计算及NumPy/SciPy加速方案咨询
周期性晶胞距离矩阵优化方案
现有代码可优化点
- 两层显式循环存在大量冗余计算:距离矩阵为对称矩阵,
dist[i][j] = dist[j][i],当前代码重复计算了所有对称对,计算量直接翻倍;对角元固定为0无需计算。 - 偏移计算逻辑冗余:逐维度计算三个相邻拷贝平方差取最小值的操作,等价于直接对分数坐标差取整偏移,无需生成三个数组再求
argmin,可大幅减少单步运算量。 - 未用到numpy/scipy的预编译向量化运算能力,纯循环效率偏低。
最优实现方案
方案1:numpy向量化实现(需完整稠密距离矩阵场景)
你需要的功能完全可以通过numpy广播机制全量向量化实现,所有运算都调用底层C实现,速度比numba循环提升10倍以上:
import numpy as np def getDistInCell_vectorized(fract, xyz, a, b, c): n = fract.shape[0] # 广播计算所有点对的分数坐标差,shape为(n, n, 3) delta_frac = fract[:, np.newaxis, :] - fract[np.newaxis, :, :] # 直接计算各维度最近邻偏移,和原代码的三段diff+argmin逻辑完全等效 shift = np.round(delta_frac) # 计算笛卡尔坐标差 delta_cart = xyz[:, np.newaxis, :] - (xyz[np.newaxis, :, :] + shift * np.array([a, b, c])) # 批量计算欧氏距离 return np.linalg.norm(delta_cart, axis=-1)
如果内存不足无法加载完整的(n,n,3)数组,可以采用分块计算:每次取1000个点作为一批,计算这批点和所有点的距离,逐批写入结果矩阵,内存占用可降低到原来的1/13,速度损失极小。
方案2:scipy KDTree实现(仅需近邻距离场景)
如果不需要完整的13160×13160稠密矩阵,只需要查询每个点的近邻,直接用scipy自带的支持周期性边界的KDTree,时间复杂度从O(N²)降到O(N log N),速度可提升上百倍:
from scipy.spatial import KDTree # 构建分数坐标下的周期性KDTree,boxsize设为[1,1,1]对应晶胞范围 kdt = KDTree(fract, boxsize=[1.0, 1.0, 1.0]) # 示例:查询每个点的10个最近邻,返回的距离为分数坐标下的最小距离 dist_frac, neighbor_idx = kdt.query(fract, k=10) # 转换为笛卡尔距离(正交晶胞场景) dist_cart = np.linalg.norm(dist_frac * np.array([a, b, c]), axis=-1)
原有numba代码的优化方案
如果你坚持用numba实现,做两处修改即可提速2倍以上:
- 删掉所有
diff_x/diff_y/diff_z的计算逻辑,直接用shx = np.round(fract[i][0] - fract[j][0])替代原来的三段计算+argmin逻辑 - 只计算上三角矩阵,结果同时赋值给
dist[i][j]和dist[j][i],对角元直接设为0,计算量直接减半
你当前的40秒耗时远未到该数据集的性能上限,经过上述优化后,稠密矩阵计算可以降到5秒以内,近邻查询可以降到1秒以内。
内容的提问来源于stack exchange,提问作者Glxblt76
相关产品推荐
相关产品推荐

