为solve_ivp 传递参数(新的SciPy ODE API)

Posted

技术标签:

【中文标题】为solve_ivp 传递参数(新的SciPy ODE API)【英文标题】:Pass args for solve_ivp (new SciPy ODE API) 【发布时间】:2018-06-23 01:36:49 【问题描述】:

为了使用 SciPy 解决简单的 ODE,我曾经使用 odeint 函数,形式为:

scipy.integrate.odeint(func, y0, t, args=(), Dfun=None, col_deriv=0, full_output=0, ml=None, mu=None, rtol=None, atol=None, tcrit=None, h0=0.0, hmax=0.0, hmin=0.0, ixpr=0, mxstep=0, mxhnil=0, mxordn=12, mxords=5, printmessg=0)[source]

要集成的简单函数可以包含以下形式的附加参数:

def dy_dt(t, y, arg1, arg2):
    # processing code here

在 SciPy 1.0 中,似乎 odeodeint 函数已被更新的 solve_ivp 方法取代。

scipy.integrate.solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False, events=None, vectorized=False, **options)

但是,这似乎没有提供 args 参数,文档中也没有任何关于实现 args 传递的指示。

因此,我想知道新 API 是否可以传递参数,或者这是一个尚未添加的功能? (如果有意删除此功能,我会觉得这是一种疏忽?)

参考: https://docs.scipy.org/doc/scipy/reference/integrate.html

【问题讨论】:

【参考方案1】:

最近scipy's github上出现了一个类似的问题。他们的解决方案是使用lambda

solve_ivp(fun=lambda t, y: fun(t, y, *args), ...)

他们认为已经有足够的开销,这无关紧要。

【讨论】:

我阅读了 cmets,与简单的 arg=() 相比,似乎很多人对 lambda 的使用并不满意。他们仍然有例子,因为我不使用 lambda 我仍然不确定该怎么做。您能否发布一个完整的示例,我认为很多人都会受益。【参考方案2】:

新函数似乎没有args 参数。作为一种解决方法,您可以创建一个包装器,如

def wrapper(t, y):
    orig_func(t,y,hardcoded_args)

然后把它传进去。

【讨论】:

谢谢,虽然这似乎是 solve_ivp 的一个严重问题(包装器希望增加开销并在我的应用程序中变慢)。因此,我将回到旧 API。我想知道为什么开发人员会做出如此倒退的举动? 同意,这对于新手和普通用户来说太复杂了。不知道开发人员在想什么。更糟糕的是,他们甚至没有在文档中为这种非常常见的情况提供示例。【参考方案3】:

最近将“args”选项添加到solve_ivp,请参见此处:https://github.com/scipy/scipy/issues/8352#issuecomment-535689344

【讨论】:

欢迎提供解决方案链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted.. 您或许应该直接添加它预计将从 scipy 版本 1.4 开始提供。【参考方案4】:

根据Javier-Acuna's ultra-brief, ultra-useful answer,最近添加了您(以及我)想要的功能。这就是伟大的 Warren Weckesser(见他的Github,***)本人的announced on Github。 无论如何,除了docstring of solve_ivp 的笑话之外,还有一个将它用于`Lotka-Volterra equations 的示例

解决_ivp( 乐趣, t_span, y0, 方法='RK45', t_eval=无, 密集输出=假, 事件=无, 矢量化=假, args=无, **选项, )

因此,只需将 args 包含为元组即可。你的情况

args = (arg1, arg2)

请不要使用我的答案,除非您的 scipy 版本 >= 1.4 。 对于低于它的版本,solve_ivp 中没有 args 参数。我亲身经历了我对 1.2.1 版的回答失败。

如果您的 scipy 版本 ,zahabaz 的实现可能仍然可以正常工作

【讨论】:

【参考方案5】:

为了完整起见,我认为您也可以这样做,但我不确定您为什么要打扰,因为此处发布的其他两个选项都很好。

from functools import partial
fun = partial(dy_dt, arg1=arg1, arg2=arg2)
scipy.integrate.solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False, events=None, vectorized=False, **options)

【讨论】:

【参考方案6】:

添加到 Cleb 的答案中,这是使用 lambda t,y: fun(t,y,args) 方法的示例。我们设置了函数句柄,它返回带有两个参数的二阶齐次 ODE 的 rhs。然后我们将它与几个选项一起提供给我们的求解器。

import numpy as np
from scipy import integrate
import matplotlib.pyplot as plt


def rhs_2nd_order_ode(t, y, a, b):
    """
    2nd order ODE function handle for use with scipy.integrate.solve_ivp
    Solves u'' + au'+ bu = 0 after reducing order with y[0]=u and y[1]=u'.

    :param t: dependent variable
    :param y: independent variables
    :param a: a
    :param b: b
    :return: Returns the rhs of y[0]' = y[1] and y[1]' = -a*y[1] - b*y[0]
    """
    return [y[1], -a*y[1] - b*y[0]]


if __name__ == "__main__":
    t_span = (0, 10)
    t_eval = np.linspace(t_span[0], t_span[1], 100)
    y0 = [0, 1]
    a = 1
    b = 2
    sol = integrate.solve_ivp(lambda t,y: rhs_2nd_order_ode(t,y,a,b), t_span, y0, 
                              method='RK45', t_eval=t_eval)

    fig, ax = plt.subplots(1, 1)
    ax.plot(sol.t, sol.y[0])
    ax.set(xlabel='t',ylabel='y')

【讨论】:

以上是关于为solve_ivp 传递参数(新的SciPy ODE API)的主要内容,如果未能解决你的问题,请参考以下文章

scipy.integrate.solve_ivp 中的初始值

参数空间中solve_ivp的问题

scipy.integrate.solve_ivp 矢量化

SciPy solve_ivp 的解决方案包含一阶 ODE 系统的振荡

scipy solve_ivp events:导数积分中的事件

时间依赖的1D Schroedinger方程使用Numpy和SciPy solve_ivp