Rs deSolve 和 Pythons odeint 的区别

Differences between Rs deSolve and Pythons odeint

我目前正在使用 PythonR 探索 Lorenz 系统,并注意到 ode 包中的细微差别。 odeint from Python and ode 都说他们使用 lsoda 来计算他们的导数。但是,对两者都使用 lsoda 命令似乎会产生截然不同的结果。我已经尝试 ode45R 中的 ode 函数得到更类似于 Python 的东西,但我想知道为什么我不能得到完全相同的结果:

from scipy.integrate import odeint
def lorenz(x, t):
    return [
        10 * (x[1] - x[0]),
        x[0] * (28 - x[2]) - x[1],
        x[0] * x[1] - 8 / 3 * x[2],
    ]


dt = 0.001
t_train = np.arange(0, 0.1, dt)
x0_train = [-8, 7, 27]
x_train = odeint(lorenz, x0_train, t_train)


x_train[0:5, :]
array([[-8.        ,  7.        , 27.        ],
       [-7.85082366,  6.98457874, 26.87275343],
       [-7.70328919,  6.96834721, 26.74700467],
       [-7.55738803,  6.95135316, 26.62273959],
       [-7.41311133,  6.93364263, 26.49994363]])
library(deSolve)
n <- round(100, 0)
# Lorenz Parameters: sigma, rho, beta
parameters <- c(s = 10, r = 28, b = 8 / 3)
state <- c(X = -8, Y = 7, Z = 27) # Initial State
# Lorenz Function used to generate Lorenz Derivatives
lorenz <- function(t, state, parameters) {
  with(as.list(c(state, parameters)), {
    dx <- parameters[1] * (state[2] - state[1])
    dy <- state[1] * (parameters[2] - state[3]) - state[2]
    dz <- state[1] * state[2] - parameters[3] * state[3]
    list(c(dx, dy, dz))
  })
}
times <- seq(0, ((n) - 1) * 0.001, by = 0.001)
# ODE45 used to determine Lorenz Matrix
out <- ode(y = state, times = times,
           func = lorenz, parms = parameters, method = "ode45")[, -1]
out[1:nrow(out), , drop = FALSE]
             X        Y        Z
 [1,] -8.00000000 7.000000 27.00000
 [2,] -7.85082366 6.984579 26.87275
 [3,] -7.70328918 6.968347 26.74700
 [4,] -7.55738803 6.951353 26.62274
 [5,] -7.41311133 6.933643 26.49994

我必须调用 out[1:nrow(out), , drop = FALSE] 才能获得完整提供的小数位,head 似乎四舍五入到最接近的五位。我知道它非常微妙,但希望得到完全相同的结果。有谁知道这是否不仅仅是 RPython 之间的舍入问题?

提前致谢。

所有求解 ODE 的数值方法都是在给定精度范围内工作的近似值。默认情况下,deSolve 求解器的精度设置为 atol=1e-6, rtol=1e-6,其中 atol 是绝对精度,rtol 是相对公差。此外,ode45还有一些额外的参数来微调自动步长算法,它可以利用插值。

增加公差,设置例如:

out <- ode(y = state, times = times, func = lorenz, 
  parms = parameters, method = "ode45", atol = 1e-10, rtol = 1e-10)

最后,我建议使用像 lsodavode 这样的 odepack 求解器,而不是经典的 ode45。更多详细信息可以在 odelsoda 帮助页面以及 ode45 的 ?rkMethod 帮助页面中找到。

odeint 也可能存在类似的参数。

最后一点:由于洛伦兹是一个混沌系统,局部误差会因误差放大而导致发散行为。这是混沌系统的一个基本特征,从理论上讲,混沌系统在长期 运行 上是不可预测的。所以无论你做什么,设置多少精度,模拟的轨迹都不是“真实的”,它们只是表现出相似的模式。