我是Python新手。以下用于最小化机械系统的代码可以正常运行。 然而,它很慢,对我来说,有不必要的步骤或循环,使它变得很重!
您能否发表评论,最好是代码的修改版本。
代码:
from os.path import join
import numpy as np
from scipy.optimize import minimize
import matplotlib.pyplot as plt
nu = 30
nv = 5
k = float(10)
def Us(i, j, uu):
uu = uu.reshape((nu + 1, nv + 1))
return i + np.sign(i) * uu[np.abs(i), j + nv]
def Vs(i, j, vv):
vv = vv.reshape((nu + 1, nv + 1))
return 2 * j + vv[np.abs(i), j + nv]
def Enrgy(varlbs):
uu = varlbs[:(nu + 1) * (nv + 1)].reshape((nu + 1, nv + 1))
vv = varlbs[(nu + 1) * (nv + 1) : 2 * (nu + 1) * (nv + 1)].reshape((nu + 1, nv + 1))
sumsp = np.sum(np.fromiter(
((((Us(i + 1, j, uu) - Us(i, j, uu)) ** 2 + (Vs(i + 1, j, vv) - Vs(i, j, vv)) ** 2) ** (1/2) - 1) ** 2
for i in range(-nu, nu)
for j in range(-nv, 1)), dtype=float))
return 0.5 * k * sumsp - .5 * Vs(0, -2, vv)
def con1(varlbs):
uu = varlbs[:(nu + 1) * (nv + 1)].reshape((nu + 1, nv + 1))
return np.array([uu[nu, j + nv] for j in range(-nv, 1)])
def con2(varlbs):
vv = varlbs[(nu + 1) * (nv + 1) : 2 * (nu + 1) * (nv + 1)].reshape((nu + 1, nv + 1))
return np.array([vv[i, 0] for i in range(0, nu)])
con12 = [{'type': 'eq', 'fun': lambda varlbs: con1(varlbs)}, {'type': 'eq', 'fun': lambda varlbs: con2(varlbs)}]
initialvals = np.zeros((2 * (nu + 1) * (nv + 1))).flatten()
minoutput = minimize(lambda varlbs: Enrgy(varlbs), initialvals, constraints=con12)
print(minoutput)
我尝试使用 sum (没有 np.fromiter)而不是 np.sum。 我尝试在函数定义中删除 reshape 。 我什至尝试了“sympy”的 Sum(尽管不确定我是否正确使用了它)。 我预计代码会在 10 秒内运行,而不是 110 秒或更长时间! 谢谢。
我建议的首要事情是避免
Enrgy()
中的循环。
def Enrgy(varlbs):
uu = varlbs[:(nu + 1) * (nv + 1)].reshape((nu + 1, nv + 1))
vv = varlbs[(nu + 1) * (nv + 1) : 2 * (nu + 1) * (nv + 1)].reshape((nu + 1, nv + 1))
i_array, j_array = np.meshgrid(range(-nu, nu), range(-nv, 1), sparse=True)
term1 = (Us(i_array + 1, j_array, uu) - Us(i_array, j_array, uu)) ** 2
term2 = (Vs(i_array + 1, j_array, vv) - Vs(i_array, j_array, vv)) ** 2
inner = ((term1 + term2) ** (1/2) - 1).flatten()
sumsp = np.dot(inner, inner)
return 0.5 * k * sumsp - .5 * Vs(0, -2, vv)
这使用
meshgrid()
获取索引数组,可用于索引操作以立即获取 Us 和 Vs 的所有结果。
我还注意到多余的重塑操作:
def Us(i, j, uu):
uu = uu.reshape((nu + 1, nv + 1))
return i + np.sign(i) * uu[np.abs(i), j + nv]
def Vs(i, j, vv):
vv = vv.reshape((nu + 1, nv + 1))
return 2 * j + vv[np.abs(i), j + nv]
这里,vv 和 uu 已经有了正确的形状。 (重塑发生在
Enrgy()
。)这些重塑操作可以删除。
在 con2 中,这是低效的:
return np.array([vv[i, 0] for i in range(0, nu)])
使用
vv[:, 0]
选择第一列,然后使用 vv[:, 0][:-1]
删除该列中的最后一个值会更快。 (假设您打算删除最后一个值。我无法判断这是否是一个错误。)
对两个实现进行基准测试,我发现这个版本快了大约 50 倍。
Original
5.53 ms ± 71.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Optimized
109 µs ± 418 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
我认为进一步加速是可能的。您可能想要研究的一些领域:
minimize()
函数将通过数值微分来近似函数的雅可比矩阵。这真的很慢:对于 1 维,它需要对目标函数进行 1 次额外调用才能执行此操作。对于 372 维,需要 372 次额外调用。