scipy.integrate.ode.integrate()如何工作?

时间:2017-04-12 21:21:18

标签: python scipy ode

我显然已经阅读了documentation,但我无法找到更详细的描述正在发生的事情。具体来说,有一些我非常困惑的行为:

常规设置

import numpy as np
from scipy.integrate import ode

#Constants in ODE
N = 30                                 
K = 0.5                               
w = np.random.normal(np.pi, 0.1, N)

#Integration parameters
y0 = np.linspace(0, 2*np.pi, N, endpoint=False)   
t0 = 0                                                                         

#Set up the solver
solver = ode(lambda t,y: w + K/N*np.sum( np.sin( y - y.reshape(N,1) ), axis=1))
solver.set_integrator('vode', method='bdf')
solver.set_initial_value(y0, t0)

问题1:solver.integrate(t0)失败

设置积分器,并在第一次询问t0时的值会返回成功的积分。重复此操作将返回正确的数字,但solver.successful()方法返回false:

solver.integrate(t0)
>>> array([ 0.        ,  0.20943951,  0.41887902, ...,  5.65486678,
            5.86430629,  6.0737458 ])

solver.successful()
>>> True

solver.integrate(t0)
>>> array([ 0.        ,  0.20943951,  0.41887902, ...,  5.65486678,
            5.86430629,  6.0737458 ])

solver.successful()
>>> False

我的问题是,solver.integrate(t)方法中发生了什么导致它第一次成功,然后失败,以及“不成功”整合意味着什么?此外,为什么集成商会无声地失败,并继续产生有用的输出,直到我明确询问它是否成功?

相关,有没有办法重置失败的集成,还是需要从头开始重新实例化解算器?

问题2:solver.integrate(t)立即返回几乎任何t

值的答案

即使y0的初始值为t0=0,我也可以t=10000请求值并立即得到答案。我希望在如此大的时间跨度上进行数值积分至少需要几秒钟(例如在Matlab中,要求整合超过10000个时间步骤需要几分钟)。

例如,从上面重新运行设置并执行:

solver.integrate(10000)
>>> array([ 2153.90803383,  2153.63023706,  2153.60964064, ...,  2160.00982959,
            2159.90446056,  2159.82900895])

Python真的那么快,还是这个输出完全是废话?

2 个答案:

答案 0 :(得分:4)

问题0

不要忽略错误消息。是的,ode的错误消息有时会很神秘,但您仍然希望避免它们。

问题1

由于您在t0的第一次通话时已经整合到solver.integrate(t0),因此您正在与第二次通话合并0的时间步长。这引发了一个神秘的错误:

 DVODE--  ISTATE (=I1) .gt. 1 but DVODE not initialized      
      In above message,  I1 =         2
/usr/lib/python3/dist-packages/scipy/integrate/_ode.py:869: UserWarning: vode: Illegal input detected. (See printed message.)
  'Unexpected istate=%s' % istate))

问题2.1

在一次调用中,解算器将采用最大数量的(内部)步骤而不会抛出错误。可以使用nsteps set_integrator参数进行设置。如果您一次集成大量时间,即使没有任何错误,也会超出nsteps,并抛出以下错误消息:

/usr/lib/python3/dist-packages/scipy/integrate/_ode.py:869: UserWarning: vode: Excess work done on this call. (Perhaps wrong MF.)
  'Unexpected istate=%s' % istate))

然后,只要发生这种情况,积分器就会停止。

问题2.2

如果设置nsteps=10**10,则集成运行没有问题。它仍然很快(在我的机器上大约1s)。原因如下:

对于像你这样的多维系统,集成时有两个主要的运行时接收器:

  • 积分器内的矢量和矩阵运算。在scipy.ode中,这些都是通过NumPy操作或移植Fortran或C代码实现的。无论如何,它们是通过编译代码实现的,没有Python开销,因此非常有效。

  • 评估衍生产品(在您的案例中为lambda t,y: w + K/N*np.sum( np.sin( y - y.reshape(N,1) ), axis=1))。您通过NumPy操作实现了这一点,这些操作再次通过编译代码实现并且非常高效。您可以使用纯编译函数稍微改进一下,但这最多只会给您一个小因素。如果您使用Python列表和循环,那将非常慢。

因此,对于您的问题,所有相关内容都由编译后的代码处理,并且集成的处理效率与例如纯C程序的效率相当。我不知道在Matlab中如何处理上述两个方面,但如果使用解释而不是编译循环处理上述任何一个挑战,这将解释您观察到的运行时差异。

答案 1 :(得分:1)

对于第二个问题,是的,输出可能是无意义的。局部误差,无论是来自离散化还是浮点运算,都会以复合因子累积,该复合因子约为ODE函数的Lipschitz常数。在第一次估计中,这里的Lipschitz常数是K=0.5。因此,早期误差的放大率,即它们作为全局误差的一部分的系数,可以与exp(0.5*10000)一样大,这是一个巨大的数字。

另一方面,整合速度快也就不足为奇了。大多数提供的方法使用步长调整,并且对于标准误差容差,这可能仅导致几十个内部步骤。减小误差容差会增加内部步骤的数量,并可能会大幅改变数值结果。