如何从文本文件中读取微分方程组来用scipy.odeint求解系统?

时间:2015-06-05 21:46:19

标签: python numpy scipy sympy differential-equations

我有一个大型(> 2000方程式)ODE系统,我想用python scipy的odeint来解决。

我有三个问题需要解决(也许我会问3个不同的问题?)。 为简单起见,我将在这里用玩具模型解释它们,但请记住我的系统很大。 假设我有以下ODE系统:

dS/dt = -beta*S
dI/dt = beta*S - gamma*I
dR/dt = gamma*I

有beta = c p I

其中c,p和gamma是我想传递给odeint的参数。

odeint期待这样的文件:

def myODEs(y, t, params):
    c,p, gamma = params
    beta = c*p
    S = y[0]
    I = y[1]
    R = y[2]
    dydt = [-beta*S*I,
           beta*S*I - gamma*I,
           - gamma*I]  
    return dydt

然后可以像这样传递给odeint:

myoutput = odeint(myODEs, [1000, 1, 0], np.linspace(0, 100, 50), args = ([c,p,gamma], ))

我在Mathematica中生成了一个文本文件,比如myOdes.txt,其中文件的每一行都对应于我的ODE系统的RHS,所以它看起来像这样

#myODEs.txt

-beta*S*I
beta*S*I - gamma*I
- gamma*I

我的文本文件看起来与odeint期待的相似,但我还没有完全相同。 我有三个主要问题:

  1. 如何传递我的文本文件,以便odeint知道这是我系统的RHS?
  2. 如何以智能方式定义变量,即系统化方式?由于它们有> 2000,我无法手动定义它们。理想情况下,我会将它们定义在一个单独的文件中并读取它。
  3. 如何将参数(有很多参数)作为文本文件传递?
  4. 我读了this question接近我的问题1和2并试图复制它(我直接为参数设置值,这样我就不必担心上面的第3点):

        systemOfEquations = []
        with open("myODEs.txt", "r") as fp :
            for line in fp :
                systemOfEquations.append(line)
    
        def dX_dt(X, t):
            vals = dict(S=X[0], I=X[1], R=X[2], t=t)
            return [eq for eq in systemOfEquations]
    
        out = odeint(dX_dt, [1000,1,0], np.linspace(0, 1, 5))
    

    但我得到了错误:

        odepack.error: Result from function call is not a proper array of          floats.
        ValueError: could not convert string to float: -((12*0.01/1000)*I*S),
    

    编辑:我将我的代码修改为:

        systemOfEquations = []
        with open("SIREquationsMathematica2.txt", "r") as fp :
            for line in fp :
                   pattern = regex.compile(r'.+?\s+=\s+(.+?)$')
                   expressionString = regex.search(pattern, line) 
                   systemOfEquations.append( sympy.sympify( expressionString) )
    
    
        def dX_dt(X, t):
            vals = dict(S=X[0], I=X[1], R=X[2], t=t)
            return [eq for eq in systemOfEquations]
    
        out = odeint(dX_dt, [1000,1,0], np.linspace(0, 100, 50), )
    

    这是有效的(我不太了解for循环的前两行正在做什么)。但是,我想更自动地定义变量的过程,我仍然不知道如何使用此解决方案并在文本文件中传递参数。同样,如何在dX_dt函数中定义参数(取决于变量)?

    提前致谢!

1 个答案:

答案 0 :(得分:0)

这不是一个完整的答案,而是一些观察/问题,但它们太长而无法发表评论。

dX_dt使用1d数组odeint和元组y多次调用{p> t。您通过t参数提供argsyodeint生成,并随每一步而变化。 dX_dt应该简化,以便快速运行。

通常,像[eq for eq in systemOfEquations]这样的表达式可以简化为systemOfEquations[eq for eq...]没有做任何有意义的事情。但是systemOfEquations可能需要它。

我建议你打印systemOfEquations(针对这个小3行的情况),既为了您的利益,也为了我们的利益。您正在使用sympy将文件中的字符串转换为方程式。我们需要看看它产生了什么。

请注意myODEs是一个函数,而不是文件。它可以从模块导入,当然这是一个文件。

指向vals = dict(S=X[0], I=X[1], R=X[2], t=t)的目的是生成sympy表达式可以使用的字典。更直接(我认为更快)dX_dt函数看起来像:

def myODEs(y, t, params):
    c,p, gamma = params
    beta = c*p
    dydt = [-beta*y[0]*y[1],
           beta*y[0]*y[1] - gamma*y[1],
           - gamma*y[1]]  
    return dydt

我怀疑运行sympy生成的表达式的dX_dt比这样的“硬编码”慢得多。

我要添加sympy标记,因为正如所写,这是将文本文件转换为odeint可以使用的函数的关键。

我倾向于将等式可变性放在t参数中,而不是列出的同情表达式。

那就是取代:

    dydt = [-beta*y[0]*y[1],
           beta*y[0]*y[1] - gamma*y[1],
           - gamma*y[1]]  

类似

    arg12=np.array([-beta, beta, 0])
    arg1 = np.array([0, -gamma, -gamma])
    arg0 = np.array([0,0,0])
    dydt = arg12*y[0]*y[1] + arg1*y[1] + arg0*y[0]

一旦这是正确的,那么argxx定义可以移到dX_dt之外,并通过args传递。现在dX_dt只是一个简单而快速的计算。

这整个sympy方法可能会正常工作,但我担心在实践中它会很慢。但是,具有sympy经验的人可能会有其他见解。