SciPy 的Integrate.odeint() 的方程数和精度

计算科学 精确 scipy
2021-12-19 10:47:14

当方程数量增加时,SciPy 的Integrate.odeint() 是否有任何理由变得不那么精确?我正在尝试解决这两组微分方程:

dy1dx=x2 — (1)

dy2dx=x3

和,

dy1dx=x2 — (2)

由于 (1) 中的两个方程是解耦的,因此应该是相同的。我在以下代码段中实现了这两个:y1

import numpy as np
from scipy.integrate import odeint

def f(y, x):
    return [x**2, x**3]

def g(y, x):
    return x**2

a = odeint(f, [0.0, 0.0], np.arange(0, 5, 0.0001))
b = odeint(g, 0.0, np.arange(0, 5, 0.0001))

print a[-1][0], b[-1][0], abs(a[-1][0] - b[-1][0])

运行上面的代码给了我:

41.6641667161 41.6641667354 1.93298319573e-08

这里的差异似乎非常微不足道。但是在方程数量变大的情况下(例如在李雅普诺夫指数计算中),它似乎会导致显着差异。

这里会发生什么?


更新:就像@horchler 解释的那样,使用numpy.linspace而不是numpy.arange确实增加了最终值的准确性,但是两个答案之间的差异是相同的顺序:

41.6666666618 41.6666666811 1.93298248519e-08
2个回答

首先,linspace如果您关心最终值或只是让积分器选择中间步骤,则在这种情况下使用可能是一个更好的主意。这将为您提供准确的最终值,但两种情况之间的精度差异仍然存在。

发生这种情况有几个相互关联的原因。仅仅因为两个方程解耦并不意味着odeint分别求解它们。求解器必须在每次迭代中选择一个时间步,这将取决于所有方程的行为。在您更简单的标量二次方程的情况下,求解器可能能够采用更大的时间步长,而在另一种情况下,三次方程增长得更快,并且可能导致求解器减小步长甚至在模式之间切换。选择的时间步长取决于(除其他外)您可以调整的绝对和相对容差。

scipy.integrate.odeint它实际上是 1980 年代编写的 ODEPACK 的一部分 LSODA 的包装器。它使用一些启发式方法在 Adams(非刚性)和 BDF(刚性)方法之间切换,具体取决于用于确定 ODE 系统刚性的诊断。由于 BDF 方法是隐式的,因此使用的 Adams 方法可能是 Adams-Moulton 方法(这也是隐式的,与显式的 Adams-Bashforth 方法相反)。

如果没有比较两个大型系统的更具体示例,您看到的错误可能是由以下原因引起的:

  • 右手边的刚度/病态雅可比矩阵
  • 进行更多浮点运算的舍入(例如,在较大的系统变得无限大于较小的系统时)
  • 容错,取决于您如何为任何“新”变量设置它们