Python 如何提高此代码的速度?(使用scipy.integrate.odeint求解ODE)
我试图用Python 如何提高此代码的速度?(使用scipy.integrate.odeint求解ODE),python,scipy,ode,odeint,Python,Scipy,Ode,Odeint,我试图用scipy.integrate.odeint模块解决一个相对较大的ODE系统。我已经实现了代码,我可以正确地解方程。但这个过程非常缓慢。我分析了代码,意识到几乎大部分计算时间都花在计算或生成ODE系统本身上,sigmoid函数也很昂贵,但我想我不得不接受它。下面是我正在使用的一段代码: def __sigmoid(self, u): # return .5 * ( u / np.sqrt(u**2 + 1) + 1) return 0.5 + np.arctan(u) /
scipy.integrate.odeint
模块解决一个相对较大的ODE系统。我已经实现了代码,我可以正确地解方程。但这个过程非常缓慢。我分析了代码,意识到几乎大部分计算时间都花在计算或生成ODE系统本身上,sigmoid函数也很昂贵,但我想我不得不接受它。下面是我正在使用的一段代码:
def __sigmoid(self, u):
# return .5 * ( u / np.sqrt(u**2 + 1) + 1)
return 0.5 + np.arctan(u) / np.pi
def __connectionistModel(self, g, t):
"""
Returning the ODE system
"""
g_ia_s = np.zeros(self.nGenes * self.nCells)
for i in xrange(0, self.nCells):
for a in xrange(0, self.nGenes):
g_ia = self.Params["R"][a] *\
self.__sigmoid( sum([ self.Params["W"][b + a*self.nGenes]*g[self.nGenes*i + b] for b in xrange(0, self.nGenes) ]) +\
self.Params["Wm"][a]*self.mData[i] +\
self.Params["h"][a] ) -\
self.Params["l"][a] * g[self.nGenes*i + a]
# Uncomment this part for including the diffusion
if i == 0:
g_ia += self.Params["D"][a] * ( - 2*g[self.nGenes*i + a] + g[self.nGenes*(i+1) + a] )
elif i == self.nCells-1:
g_ia += self.Params["D"][a] * ( g[self.nGenes*(i-1) + a] - 2*g[self.nGenes*i + a] )
else:
g_ia += self.Params["D"][a] * ( g[self.nGenes*(i-1) + a] - 2*g[self.nGenes*i + a] + g[self.nGenes*(i+1) + a] )
g_ia_s[self.nGenes*i + a] = g_ia
return g_ia_s
def solve(self, inp):
g0 = np.zeros(self.nGenes * self.nCells)
t = np.arange(self.t0, self.t1, self.dt)
self.integratedExpression = odeint(self.__connectionistModel, g0, t, full_output=0)
return self.integratedExpression
正如您在每次迭代中看到的,我应该生成nCells*nGenes(100*3=300)方程,并将其传递给odeint
。虽然我不确定,但我想生成这些方程比求解它们要昂贵得多。在我的实验中,解决整个系统需要7秒,其中包括1秒的odeint
和6秒的\uu连接模型
我想知道是否有一个方法,我可以改善这一点或不?我试图使用Symphy来定义符号ODE系统,并将符号方程传递给odeint
,但它无法正常工作,因为您无法真正定义符号数组,以后可以像数组一样访问这些符号数组
在最坏的情况下,我必须处理它或使用Cython来加快解决过程,但我想确保我做得正确,没有办法改进它
提前谢谢你的帮助
[更新]:分析结果
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 7.915 7.915 grnTest.py:1(<module>)
1 0.000 0.000 7.554 7.554 grn.py:83(solve)
1 0.000 0.000 7.554 7.554 odepack.py:18(odeint)
1 0.027 0.027 7.554 7.554 {scipy.integrate._odepack.odeint}
1597 5.506 0.003 7.527 0.005 grn.py:55(__connectionistModel)
479100 1.434 0.000 1.434 0.000 grn.py:48(__sigmoid)
479102 0.585 0.000 0.585 0.000 {sum}
1 0.001 0.001 0.358 0.358 grn.py:4(<module>)
2 0.001 0.001 0.207 0.104 __init__.py:10(<module>)
27 0.014 0.001 0.185 0.007 __init__.py:1(<module>)
7 0.006 0.001 0.106 0.015 __init__.py:2(<module>)
ncalls tottime percall cumtime percall文件名:lineno(函数)
1 0.000 0.000 7.915 7.915 grnTest.py:1()
1 0.000 0.000 7.554 7.554 grn.py:83(求解)
1 0.000 0.000 7.554 7.554 odepack.py:18(odeint)
1 0.027 0.027 7.554 7.554{scipy.integrate.\u odepack.odeint}
1597 5.506 0.003 7.527 0.005总产量:55
479100 1.434 0.000 1.434 0.000伽马比:48(u_sigmoid)
479102 0.585 0.000 0.585 0.000{sum}
1 0.001 0.001 0.358 0.358 grn.py:4()
2 0.001 0.001 0.207 0.104 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.py:10()
27 0.014 0.001 0.185 0.007 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu初始值:1()
7 0.006 0.001 0.106 0.015 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.py:2()
[更新2]:我公开了代码:矢量化,矢量化,然后再矢量化一些。并使用便于矢量化的数据结构 函数
\uu connectionistor model
使用了大量的访问模式a[i*m+j]
,这相当于在总共有m
列的二维数组中访问行i
和列j
。这表明2D数组是存储数据的正确方式。我们可以通过使用NumPy切片表示法和矢量化从函数中消除循环i
,如下所示:
def __connectionistModel_vec(self, g, t):
"""
Returning the ODE system
"""
g_ia_s = np.zeros(self.nGenes * self.nCells)
g_2d = g.reshape((self.nCells, self.nGenes))
W = np.array(self.Params["W"])
mData = np.array(self.mData)
g_ia_s = np.zeros((self.nCells, self.nGenes))
for a in xrange(0, self.nGenes):
g_ia = self.Params["R"][a] *\
self.__sigmoid( (W[a*self.nGenes:(a+1)*self.nGenes]*g_2d).sum(axis=1) +\
self.Params["Wm"][a]*mData +\
self.Params["h"][a] ) -\
self.Params["l"][a] * g_2d[:,a]
g_ia[0] += self.Params["D"][a] * ( - 2*g_2d[0,a] + g_2d[1,a] )
g_ia[-1] += self.Params["D"][a] * ( g_2d[-2,a] - 2*g_2d[-1,a] )
g_ia[1:-1] += self.Params["D"][a] * ( g_2d[:-2,a] - 2*g_2d[1:-1,a] + g_2d[2:,a] )
g_ia_s[:,a] = g_ia
return g_ia_s.ravel()
就我所见,它返回的值与原始的\uu连接模型
相同。作为奖励,该功能现在更加紧凑。我只优化了此函数,使其具有与原始函数相同的输入和输出,但为了提高性能,您可能希望以NumPy数组而不是列表来组织数据,以避免每次调用时从列表到数组的转换。我相信还有其他一些小的性能调整
无论如何,原始代码给了我这些评测结果(在这里插入强制性的“我的电脑比你的快”自夸):
使用\uuuu connectionistist model\u vec
,我得到:
ncalls tottime percall cumtime percall filename:lineno(function)
1597 0.175 0.000 0.247 0.000 grn.py:79(__connectionistModel_vec)
4791 0.031 0.000 0.031 0.000 grn.py:48(__sigmoid)
4800 0.021 0.000 0.021 0.000 {method 'reduce' of 'numpy.ufunc' objects}
1 0.018 0.018 0.265 0.265 {scipy.integrate._odepack.odeint}
3197 0.013 0.000 0.013 0.000 {numpy.core.multiarray.array}
矢量化,矢量化,然后再矢量化一些。并使用便于矢量化的数据结构 函数
\uu connectionistor model
使用了大量的访问模式a[i*m+j]
,这相当于在总共有m
列的二维数组中访问行i
和列j
。这表明2D数组是存储数据的正确方式。我们可以通过使用NumPy切片表示法和矢量化从函数中消除循环i
,如下所示:
def __connectionistModel_vec(self, g, t):
"""
Returning the ODE system
"""
g_ia_s = np.zeros(self.nGenes * self.nCells)
g_2d = g.reshape((self.nCells, self.nGenes))
W = np.array(self.Params["W"])
mData = np.array(self.mData)
g_ia_s = np.zeros((self.nCells, self.nGenes))
for a in xrange(0, self.nGenes):
g_ia = self.Params["R"][a] *\
self.__sigmoid( (W[a*self.nGenes:(a+1)*self.nGenes]*g_2d).sum(axis=1) +\
self.Params["Wm"][a]*mData +\
self.Params["h"][a] ) -\
self.Params["l"][a] * g_2d[:,a]
g_ia[0] += self.Params["D"][a] * ( - 2*g_2d[0,a] + g_2d[1,a] )
g_ia[-1] += self.Params["D"][a] * ( g_2d[-2,a] - 2*g_2d[-1,a] )
g_ia[1:-1] += self.Params["D"][a] * ( g_2d[:-2,a] - 2*g_2d[1:-1,a] + g_2d[2:,a] )
g_ia_s[:,a] = g_ia
return g_ia_s.ravel()
就我所见,它返回的值与原始的\uu连接模型
相同。作为奖励,该功能现在更加紧凑。我只优化了此函数,使其具有与原始函数相同的输入和输出,但为了提高性能,您可能希望以NumPy数组而不是列表来组织数据,以避免每次调用时从列表到数组的转换。我相信还有其他一些小的性能调整
无论如何,原始代码给了我这些评测结果(在这里插入强制性的“我的电脑比你的快”自夸):
使用\uuuu connectionistist model\u vec
,我得到:
ncalls tottime percall cumtime percall filename:lineno(function)
1597 0.175 0.000 0.247 0.000 grn.py:79(__connectionistModel_vec)
4791 0.031 0.000 0.031 0.000 grn.py:48(__sigmoid)
4800 0.021 0.000 0.021 0.000 {method 'reduce' of 'numpy.ufunc' objects}
1 0.018 0.018 0.265 0.265 {scipy.integrate._odepack.odeint}
3197 0.013 0.000 0.013 0.000 {numpy.core.multiarray.array}
更好地分析代码,要具体。确切地说,您在
\uuuu connectionistor model
中花的时间最多的地方是哪里?您能否提供inp
输入,让它运行7秒钟?@flebool我不知道如何在函数中分析哪一部分更贵。但是我包括了评测结果。我在GitHub上上传了代码-@willProfile你的代码更好,具体一点。确切地说,您在\uuuu connectionistor model
中花的时间最多的地方是哪里?您能否提供inp
输入,让它运行7秒钟?@flebool我不知道如何在函数中分析哪一部分更贵。但是我包括了分析结果。我将代码上传到GitHub-@willThat太棒了!我知道我应该做那样的事,但我不知道怎么做。我甚至尝试使用np.vectorize对函数进行矢量化,但它并没有像我预期的那样工作@jsl从vectorize
doc:提供矢量化功能主要是为了方便,而不是为了性能。该实现本质上是一个for循环。
太棒了!我知道我应该做那样的事,但我不知道怎么做。我甚至尝试使用np.vectorize对函数进行矢量化,但我