Python scipy.integrate.ode.integrate()是如何工作的?
很明显,我已经通读了这本书,但我还没有找到更详细的关于书中所发生事情的描述。具体来说,有一些行为我非常困惑: 一般设置 问题1:Python scipy.integrate.ode.integrate()是如何工作的?,python,scipy,ode,Python,Scipy,Ode,很明显,我已经通读了这本书,但我还没有找到更详细的关于书中所发生事情的描述。具体来说,有一些行为我非常困惑: 一般设置 问题1:solver.integrate(t0)失败 设置积分器,并在t0处首次请求值,将返回成功的积分。重复此操作将返回正确的数字,但solver.successful()方法将返回false: solver.integrate(t0) >>> array([ 0. , 0.20943951, 0.41887902, ..., 5.654
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真的有那么快吗,还是这个输出完全是胡说八道?问题0
不要忽略错误消息。是的,ode
的错误消息有时可能很神秘,但您仍然希望避免它们
问题1
由于第一次调用solver.integrate(t0)
时,您已经将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
在一次调用中,解算器在不引发错误的情况下将执行的最大(内部)步骤数。这可以通过set\u integrator
的nsteps
参数进行设置。如果一次集成大量时间,即使没有任何错误,也会超出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
,则集成运行不会出现问题。但速度仍然相当快(约为1 在我的机器上)。原因如下:
对于像您这样的多维系统,集成时有两个主要的运行时接收器:
- 积分器中的向量和矩阵运算。在
scipy.ode
中,这些都是通过NumPy操作或移植的Fortran或C代码实现的。无论如何,它们是通过编译代码实现的,没有Python开销,因此非常高效
- 求导数(
lambda t,y:w+K/N*np.sum(np.sin(y-y.restrape(N,1)),轴=1)
(在您的例子中)。您通过NumPy操作实现了这一点,而NumPy操作又是通过编译代码实现的,非常高效。您可以使用一个纯编译的函数对此进行一点改进,但这最多只会给您一个小因素。如果改用Python列表和循环,速度会非常慢
因此,对于您的问题,所有相关的内容都是在后台用编译代码处理的,集成的处理效率与纯C程序相当。我不知道上述两个方面在Matlab中是如何处理的,但是如果上面的任何一个挑战是用解释循环而不是编译循环来处理的,这就可以解释您观察到的运行时差异。对于第二个问题,是的,输出可能是无意义的。局部误差,无论是离散化还是浮点运算,都会累积一个复合因子,该因子与ODE函数的Lipschitz常数有关。在第一次估计中,此处的Lipschitz常数为K=0.5
。因此,早期错误的放大率,即作为全局错误一部分的系数,可以大到exp(0.5*10000)
,这是一个巨大的数字
另一方面,集成速度之快也就不足为奇了。所提供的大多数方法都使用步长自适应,在标准误差公差下,这可能只会导致几十个内部步长。减少误差容差将增加内部步骤的数量,并可能大幅改变数值结果。至于第1部分,当我运行代码时,我收到一条警告和一条消息“DVODE--ISTATE(=I1).gt.1但DVODE未初始化”,这显然来自Fortran内部的某个地方。这意味着可能会有更多的事情发生,而不仅仅是一个不成功的状态。顺便说一句,尝试用一个简单的ODE重现你的问题可能是有用的,它的结果可以通过分析或更简单的数值方法来验证。是我刚刚为遇到类似问题的其他人找到的一篇相关文章。奇怪的是,我实际上没有收到任何错误消息(当然我不会简单地忽略它们),这就是为什么我担心integrate()方法会悄悄失败的原因。我发布的是我在shell中看到的确切输出。我使用的是Spyder内置的IPython外壳,这不是我第一次有奇怪的行为。也许错误输出被重定向到某个地方而没有显示。关于第一段:这与集成许多小步骤有什么不同?此外,所讨论的动态看起来不像是您关心全局错误的动态。作为后续,是否有方法强制方法以固定步长运行?还有,是否有关于误差公差如何影响自适应步长的文档?@Wrzlprmft(如果
/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))