简短的回答
不,lsoda
不会创造“虚构”价值。
详细解答
正如@Ben Bolker 所说,“浮点算术本质上是不精确的”。我想补充一点,包括lsoda
计算近似在内的所有求解器,以及非线性模型会放大误差。
您可以通过单独调用模型函数(不使用求解器)检查您的模型,如果所有导数都完全为零,请参见以下 Lorenz 系统示例,其中所有状态都设置为零:
library(deSolve)
Lorenz <- function(t, state, parameters) {
with(as.list(c(state, parameters)), {
dX <- a * X + Y * Z
dY <- b * (Y - Z)
dZ <- -X * Y + c * Y - Z
list(c(dX, dY, dZ))
})
}
parameters <- c(a = -8/3, b = -10, c = 28)
state <- c(X = 0, Y = 0, Z = 0)
times <- seq(0, 500, by = 1)
range(Lorenz(0, state, parameters))
这给了
[1] 0 0
反例
如果我们修改模型,使得一个导数由于舍入误差而与零略有不同,例如使用 Ben 的示例:
Fancy <- function(t, state, parameters) {
with(as.list(c(state, parameters)), {
dX <- a * X + Y * Z + 27 * (sqrt(X + Y + Z + 2)^2 - 2)
dY <- b * (Y - Z)
dZ <- -X * Y + c * Y - Z
list(c(dX, dY, dZ))
})
}
range(Fancy(0, state, parameters))
然后它给出:
[1] 0.000000e+00 1.199041e-14
根据公差,这样的模型可能会波动甚至崩溃。令人惊讶的是,较小的公差有时可能会更糟:
out1 <- ode(y = state, times = times, func = Lorenz, parms = parameters, atol=1e-6)
out2 <- ode(y = state, times = times, func = Fancy, parms = parameters, atol=1e-6)
out3 <- ode(y = state, times = times, func = Fancy, parms = parameters, atol=1e-8)
par(mfrow=c(3,1))
plot(out1[,1:2], type="l", main="Lorenz, derivatives = 0")
plot(out2[,1:2], type="l", main="Fancy, small error in derivatives, atol=1e-6")
plot(out3[,1:2], type="l", main="Fancy, small error in derivatives, atol=1e-8")
结论
lsoda
在浮点运算的限制范围内运行良好。
- 模型应该经过仔细设计和测试。