如何使Python代码快速最小化?

问题描述 投票:0回答:1

我是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 秒或更长时间! 谢谢。

python-3.x numpy performance scipy minimization
1个回答
0
投票

我建议的首要事情是避免

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 次额外调用。
  • 用 Numba 写这个可能会加快速度。
© www.soinside.com 2019 - 2024. All rights reserved.