numpy 我如何用np.sum改进这些嵌套循环?

xtfmy6hx  于 2023-02-23  发布在  其他
关注(0)|答案(1)|浏览(165)

如何在Python中使用np.sum创建这个函数?

我创建了这段代码,它运行良好,但是使用np.sum比使用两个for循环要快,所以我正在尝试弄清楚如何使用np.sum
我的代码:

def potential(x):
    A, XI, P, Q, R0 = parameters()
    atoms, _ = read_file_xyz()
    n_atoms = len(atoms)
    x = x.reshape(n_atoms, 3)
    U = 0
    for i in range(n_atoms):
        Ub = 0
        Ur = 0
        for j in range(n_atoms):
            if j != i:
                Ub += (XI[i, j]**2) * np.exp(-2 * Q[i, j] * ((np.sqrt((x[i, 0]-x[j, 0])**2 + (x[i, 1]-x[j, 1])**2 +(x[i, 2]-x[j, 2])**2) / R0[i, j]) - 1))
                Ur += A[i, j] * np.exp(-P[i, j] * ((np.sqrt((x[i, 0]-x[j, 0])**2 + (x[i, 1]-x[j, 1])**2 +(x[i, 2]-x[j, 2])**2) / R0[i, j]) - 1))
            else:
                pass
        U += (Ur - np.sqrt(Ub))
    return U

我在努力缩短行刑时间。

t3psigkw

t3psigkw1#

如果我只是看看你发布的代码,我建议你试着删除内部循环,并使用Numpy的自动广播来做同样的事情。我注意到你正在以一种非常手动的方式计算欧氏距离,但distance module from SciPy似乎不支持autograd
这就给我留下了下面的代码:

def potential(x):
    A, XI, P, Q, R0 = parameters()

    n_atoms = len(A)
    di = np.diag_indices(n_atoms)
    x = x.reshape(n_atoms, 3)

    # preprocess params to minimize work and remove need to special case "i == j"
    A[di] = 0.
    XI[di] = 0.
    P *= -1.
    Q *= -1.
    R0[di]= 1.

    U = 0.
    for i, xi in enumerate(x):
        dist = np.linalg.norm(xi - x, axis=1) / R0[i] - 1.
        Ub = XI[i] * np.exp(Q[i] * dist)
        Ur = A[i] * np.exp(P[i] * dist)
        U += sum(Ur) - sum(Ub)

    return U

当我查看你的上游代码库时,我看到你从optimize调用这个方法,这表明你在做数值优化的时候从文件中加载数据,这感觉非常错误。
因此,我建议进行重构以消除这种重复加载数据的情况,并清理参数。将内容存储在对象中似乎是有意义的,只需让它暴露potential和渐变的方法。

from autograd import elementwise_grad as egrad
import autograd.numpy as np

class GuptaPotential:
    def __init__(self):
        self.A, self.XI, self.P, self.Q, self.R0 = parameters()
        
        self.n_atoms = len(self.A)
        di = np.diag_indices(self.n_atoms)
        
        # preprocess params to minimize work and remove need to special case "i == j"
        self.A[di] = 0.
        self.XI[di] = 0.
        self.P *= -1
        self.Q *= -1
        self.R0[di] = 1.

    def potential(self, x):
        x = x.reshape(self.n_atoms, 3)
        U = 0.
        for i, xi in enumerate(x):
            dist = np.linalg.norm(xi - x, axis=1) / self.R0[i] - 1.
            U -= sum(self.XI[i] * np.exp(self.Q[i] * dist))
            U += sum(self.A[i] * np.exp(self.P[i] * dist))
        return U
    
    gradient = egrad(potential)

你可以通过最小化来使用它:

gp = GuptaPotential()
sol = minimize(gp.potential, x0, method='BFGS', jac=gp.gradient, options={'gtol':1e-8, 'disp':True})

相关问题