Python 奇数SciPy ODE积分错误
我正在为一个空闲端项目实现一个非常简单的、人口稳定的模型——通常这是一个非常简单的任务。但我在使用PysCeS或SciPy时遇到了解算器错误,这两种方法都使用lsoda作为其底层解算器。这只会发生在参数的特定值上,我不明白为什么。我使用的代码如下所示:Python 奇数SciPy ODE积分错误,python,scipy,ode,Python,Scipy,Ode,我正在为一个空闲端项目实现一个非常简单的、人口稳定的模型——通常这是一个非常简单的任务。但我在使用PysCeS或SciPy时遇到了解算器错误,这两种方法都使用lsoda作为其底层解算器。这只会发生在参数的特定值上,我不明白为什么。我使用的代码如下所示: import numpy as np from pylab import * import scipy.integrate as spi #Parameter Values S0 = 99. I0 = 1. R0 = 0. PopIn= (S0
import numpy as np
from pylab import *
import scipy.integrate as spi
#Parameter Values
S0 = 99.
I0 = 1.
R0 = 0.
PopIn= (S0, I0, R0)
beta= 0.50
gamma=1/10.
mu = 1/25550.
t_end = 15000.
t_start = 1.
t_step = 1.
t_interval = np.arange(t_start, t_end, t_step)
#Solving the differential equation. Solves over t for initial conditions PopIn
def eq_system(PopIn,t):
'''Defining SIR System of Equations'''
#Creating an array of equations
Eqs= np.zeros((3))
Eqs[0]= -beta * (PopIn[0]*PopIn[1]/(PopIn[0]+PopIn[1]+PopIn[2])) - mu*PopIn[0] + mu*(PopIn[0]+PopIn[1]+PopIn[2])
Eqs[1]= (beta * (PopIn[0]*PopIn[1]/(PopIn[0]+PopIn[1]+PopIn[2])) - gamma*PopIn[1] - mu*PopIn[1])
Eqs[2]= gamma*PopIn[1] - mu*PopIn[2]
return Eqs
SIR = spi.odeint(eq_system, PopIn, t_interval)
require(deSolve)
sir.model <- function (t, x, params) {
S <- x[1]
I <- x[2]
R <- x[3]
with (
as.list(params),
{
dS <- -beta*S*I/(S+I+R) - mu*S + mu*(S+I+R)
dI <- beta*S*I/(S+I+R) - gamma*I - mu*I
dR <- gamma*I - mu*R
res <- c(dS,dI,dR)
list(res)
}
)
}
times <- seq(0,15000,by=1)
params <- c(
beta <- 0.50,
gamma <- 1/10,
mu <- 1/25550
)
xstart <- c(S = 99, I = 1, R= 0)
out <- as.data.frame(lsoda(xstart,times,sir.model,params))
这会产生以下错误:
lsoda-- at current t (=r1), mxstep (=i1) steps
taken on this call before reaching tout
In above message, I1 = 500
In above message, R1 = 0.7818108252072E+04
Excess work done on this call (perhaps wrong Dfun type).
Run with full_output = 1 to get quantitative information.
通常,当我遇到这样的问题时,我建立的方程组有点极端错误,但我俩都看不出有什么错误。奇怪的是,如果您将mu更改为1/15550
,它也可以工作。如果系统出现问题,我也在R中实现了如下模型:
import numpy as np
from pylab import *
import scipy.integrate as spi
#Parameter Values
S0 = 99.
I0 = 1.
R0 = 0.
PopIn= (S0, I0, R0)
beta= 0.50
gamma=1/10.
mu = 1/25550.
t_end = 15000.
t_start = 1.
t_step = 1.
t_interval = np.arange(t_start, t_end, t_step)
#Solving the differential equation. Solves over t for initial conditions PopIn
def eq_system(PopIn,t):
'''Defining SIR System of Equations'''
#Creating an array of equations
Eqs= np.zeros((3))
Eqs[0]= -beta * (PopIn[0]*PopIn[1]/(PopIn[0]+PopIn[1]+PopIn[2])) - mu*PopIn[0] + mu*(PopIn[0]+PopIn[1]+PopIn[2])
Eqs[1]= (beta * (PopIn[0]*PopIn[1]/(PopIn[0]+PopIn[1]+PopIn[2])) - gamma*PopIn[1] - mu*PopIn[1])
Eqs[2]= gamma*PopIn[1] - mu*PopIn[2]
return Eqs
SIR = spi.odeint(eq_system, PopIn, t_interval)
require(deSolve)
sir.model <- function (t, x, params) {
S <- x[1]
I <- x[2]
R <- x[3]
with (
as.list(params),
{
dS <- -beta*S*I/(S+I+R) - mu*S + mu*(S+I+R)
dI <- beta*S*I/(S+I+R) - gamma*I - mu*I
dR <- gamma*I - mu*R
res <- c(dS,dI,dR)
list(res)
}
)
}
times <- seq(0,15000,by=1)
params <- c(
beta <- 0.50,
gamma <- 1/10,
mu <- 1/25550
)
xstart <- c(S = 99, I = 1, R= 0)
out <- as.data.frame(lsoda(xstart,times,sir.model,params))
require(deSolve)
sir.model我认为,对于您选择的参数,您遇到了问题-由于数值不稳定,在解曲线斜率实际上相当浅的区域,解算器的步长变得非常小。Fortran解算器lsoda
,由scipy.integrate.odeint
包装,试图在适合“刚性”和“非刚性”系统的方法之间自适应切换,但在这种情况下,它似乎无法切换到刚性方法
非常粗略地说,您只需大幅增加允许的最大步长,最终解算器就会达到:
SIR = spi.odeint(eq_system, PopIn, t_interval,mxstep=5000000)
更好的选择是使用面向对象的ODE解算器scipy.integrate.ODE
,它允许您显式选择是使用刚性方法还是非刚性方法:
import numpy as np
from pylab import *
import scipy.integrate as spi
def run():
#Parameter Values
S0 = 99.
I0 = 1.
R0 = 0.
PopIn= (S0, I0, R0)
beta= 0.50
gamma=1/10.
mu = 1/25550.
t_end = 15000.
t_start = 1.
t_step = 1.
t_interval = np.arange(t_start, t_end, t_step)
#Solving the differential equation. Solves over t for initial conditions PopIn
def eq_system(t,PopIn):
'''Defining SIR System of Equations'''
#Creating an array of equations
Eqs= np.zeros((3))
Eqs[0]= -beta * (PopIn[0]*PopIn[1]/(PopIn[0]+PopIn[1]+PopIn[2])) - mu*PopIn[0] + mu*(PopIn[0]+PopIn[1]+PopIn[2])
Eqs[1]= (beta * (PopIn[0]*PopIn[1]/(PopIn[0]+PopIn[1]+PopIn[2])) - gamma*PopIn[1] - mu*PopIn[1])
Eqs[2]= gamma*PopIn[1] - mu*PopIn[2]
return Eqs
ode = spi.ode(eq_system)
# BDF method suited to stiff systems of ODEs
ode.set_integrator('vode',nsteps=500,method='bdf')
ode.set_initial_value(PopIn,t_start)
ts = []
ys = []
while ode.successful() and ode.t < t_end:
ode.integrate(ode.t + t_step)
ts.append(ode.t)
ys.append(ode.y)
t = np.vstack(ts)
s,i,r = np.vstack(ys).T
fig,ax = subplots(1,1)
ax.hold(True)
ax.plot(t,s,label='Susceptible')
ax.plot(t,i,label='Infected')
ax.plot(t,r,label='Recovered')
ax.set_xlim(t_start,t_end)
ax.set_ylim(0,100)
ax.set_xlabel('Time')
ax.set_ylabel('Percent')
ax.legend(loc=0,fancybox=True)
return t,s,i,r,fig,ax
将numpy导入为np
从派拉布进口*
导入scipy.integrate作为spi
def run():
#参数值
S0=99。
I0=1。
R0=0。
PopIn=(S0,I0,R0)
β=0.50
伽马=1/10。
mu=1/25550。
t_end=15000。
t_start=1。
t_步骤=1。
t_间隔=np.arange(t_开始、t_结束、t_步)
#解微分方程。求解t上的PopIn初始条件
def均衡器系统(t,PopIn):
“定义SIR方程组”
#创建一组方程式
等式=np.零((3))
等式[0]=-beta*(PopIn[0]*PopIn[1]/(PopIn[0]+PopIn[1]+PopIn[2])-mu*PopIn[0]+mu*(PopIn[0]+PopIn[1]+PopIn[2])
等式[1]=(β*(PopIn[0]*PopIn[1]/(PopIn[0]+PopIn[1]+PopIn[2])-γ*PopIn[1]-mu*PopIn[1])
等式[2]=γ*PopIn[1]-mu*PopIn[2]
返回等式
ode=spi.ode(均衡器系统)
#适用于刚性常微分方程组的BDF方法
ode.set_积分器('vode',nsteps=500,方法='bdf')
ode.set_初始值(PopIn,t_start)
ts=[]
ys=[]
而ode.successful()和ode.t
输出:
受感染的人群PopIn[1]
衰减为零。显然,(正常)数值不精确导致PopIn[1]
在t=322.9附近变为负值(约-3.549e-12)。最后,解在t=7818.093附近爆炸,而PopIn[0]
朝向+无穷大,而PopIn[1]
朝向-无穷大
编辑:我删除了我先前关于“快速修复”的建议。这是一个值得怀疑的黑客行为。这里的所有内容都打包为run(),以便于调用,还是出于机械原因?@Fomite将python文件作为模块而不是脚本编写通常是一个好主意,因为它允许代码重用,并鼓励更好的代码结构。