在不同的 scipy ode 求解器之间进行交换

Posted

技术标签:

【中文标题】在不同的 scipy ode 求解器之间进行交换【英文标题】:Interchanging between different scipy ode solvers 【发布时间】:2016-04-07 02:31:56 【问题描述】:

我做了一个求解器,它可以在scipy.integrate.odescipy.integrate.odeint 之间互换。这是代码。

def f(y,s,C,u,v):
    y0 = y[0] # u
    y1 = y[1] # u'
    y2 = y[2] # v
    y3 = y[3] # v'
    dy = np.zeros_like(y)
    dy[0] = y1
    dy[2] = y3

    C = C.subs(u:y0,v:y2)
    dy[1] = -C[0,0][0]*dy[0]**2\
            -2*C[0,0][1]*dy[0]*dy[2]\
            -C[0,1][1]*dy[2]**2
    dy[3] = -C[1,0][0]*dy[0]**2\
            -2*C[1,0][1]*dy[0]*dy[2]\
            -C[1,1][1]*dy[2]**2
    return dy

def solve(C,u0,s0,s1,ds,solver=None):
    from sympy.abc import u,v
    if solver == None: # use lsoda from scipy.integrate.odeint
        s = np.arange(s0,s1+ds,ds)
        print 'Running solver ...'
        return sc.odeint(f,u0,s,args=(C,u,v))
    else: # use any other solver from scipy.integrate.ode
        r = sc.ode(f).set_integrator(solver) # vode,zvode,lsoda,dopri5,dop853
        r.set_f_params(C,u,v)
        r.set_initial_value(u0)
        #t = []
        y = []
        print 'Running solver ...'
        while r.successful() and r.t <= s1:
            r.integrate(r.t + ds)
            y.append(r.y)#; t.append(r.t)
        return np.array(y)

我遇到的问题如下。如果我决定使用来自scipy.integrate.odeint 的求解器,则必须按照代码中的顺序指定f 的参数。但是,如果我决定使用来自scipy.integrate.ode 的求解器,我必须将函数f(y,s,C,u,v) 的参数顺序更改为f(s,y,C,u,v),否则会出现错误

TypeError: 'float' object has no attribute '__getitem__'

如果我这样做,那么scipy.integrate.odeint 会为定义为f(s,y,C,u,v)f 生成相同的错误。无论参数顺序如何,如何统一f操作?

编辑:

总结问题:

scipy.integrate.ode 求解器在函数 f 定义为 f(s,y,C,u,v) 时工作,scipy.integrate.odeint 求解器在函数 f 定义为 f(y,s,C,u,v) 时工作。为什么会出现这种情况,我该如何解决?

编辑:

Scipy -- 版本 0.16.0

【问题讨论】:

如果我交换 s 和 y 参数,求解器会尝试访问浮点数,就好像它是一个列表一样。这导致了上面的错误消息。 但是为什么你通常需要odeintodeint 使用 lsoda 求解器,ode 也可用。 抱歉回复晚了。最初我使用的是 odeint,但后来我想添加使用其他求解器的可能性(除了 Lsoda)。此示例中显示的代码只是整个代码的一小部分。我想我是因为不想更改代码而变得懒惰。但我也认为这个问题似乎很相关,因为这两个求解器有不同的变量映射很奇怪。 【参考方案1】:

为什么会发生这种情况,我该如何解决?

这是由于多年前做出的一个不幸的 API 设计决定而发生的。 odeintode 类需要不同的签名才能解决系统。

您可以通过添加一个包装器来修复它,该包装器会在您使用 ode 类时更改前两个参数的顺序。例如,您可以更改:

    r = sc.ode(f).set_integrator(solver)

    r = sc.ode(lambda t, x, *args: f(x, t, *args)).set_integrator(solver)

更新:在 SciPy 1.1.0 中,tfirst 参数已添加到 scipy.integrate.odeint。默认值tfirst=False 保持旧的行为。对于tfirst=Trueodeint 需要 first 参数 funct(即自变量)。通过使用tfirst=True,相同的func 可以与odeodeint 和更新的solver_ivp 一起使用。

【讨论】:

谢谢!这就像一个魅力。这样,我仍然可以使用相同的函数 f,而无需物理更改参数的顺序或创建新函数。

以上是关于在不同的 scipy ode 求解器之间进行交换的主要内容,如果未能解决你的问题,请参考以下文章

Python - Scipy:ode 模块:启用求解器的 step 选项的问题

使用 SymPy 表达式和 SciPy 求解器求解一阶 ODE 系统

奇怪的 SciPy ODE 集成错误

离散值的 ODE 集成

用 SciPy 数值求解 ODE

python的ODE求解器尊重周期性