web-dev-qa-db-fra.com

Passez des arguments pour resol_ivp (nouvelle API SciPy ODE)

Pour résoudre des ODE simples à l'aide de SciPy, j'avais l'habitude d'utiliser la fonction odeint, avec la forme:

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]

où une fonction simple à intégrer pourrait inclure des arguments supplémentaires de la forme:

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

Dans SciPy 1.0, il semble que les fonctions ode et odeint ont été remplacées par une méthode plus récente resolver_ivp .

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

Cependant, cela ne semble pas offrir un paramètre args , ni aucune indication dans la documentation quant à l'implémentation du passage d'args.

Par conséquent, je me demande si le passage d'arguments est possible avec la nouvelle API, ou est-ce une fonctionnalité qui n'a pas encore été ajoutée? (Cela me semblerait un oubli si ces fonctionnalités ont été intentionnellement supprimées?)

Référence: https://docs.scipy.org/doc/scipy/reference/integrate.html

18
sharkmas

Il ne semble pas que la nouvelle fonction ait un paramètre args. Pour contourner ce problème, vous pouvez créer un wrapper comme

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

et passez ça.

9
rlee827

Relativement récemment, une question similaire est apparue sur github de scipy . Leur solution consiste à utiliser lambda:

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

Et ils soutiennent qu'il y a déjà suffisamment de frais généraux pour que cela n'ait pas d'importance.

12
Lev K.

Récemment, l'option 'args' a été ajoutée pour resolver_ivp, voir ici: https://github.com/scipy/scipy/issues/8352#issuecomment-535689344

4
Javier-Acuna

Selon réponse ultra-brève et ultra-utile de Javier-Acuna , la fonctionnalité que vous (ainsi que moi) souhaitons a récemment été ajoutée. C'était annoncé sur Github par nul autre que le grand Warren Weckesser (Voir son Github , StackOverflow ) lui-même. Quoi qu'il en soit, blague à part le docstring de resol_ivp a un exemple l'utilisant pour les ` équations de Lotka-Volterra

resolver_ivp (fun, t_span, y0, method = 'RK45', t_eval = None, dense_output = False, events = None, vectorized = False, args = None, ** options,)

Donc, incluez simplement args en tant que Tuple. Dans ton cas

args = (arg1, arg2)

Veuillez ne pas utiliser ma réponse à moins que votre version scipy> = 1.4. Il n'y a pas de paramètre args dans resol_ivp pour les versions inférieures. J'ai personnellement constaté que ma réponse échouait pour la version 1.2.1.

L'implémentation par zahabaz fonctionnerait probablement toujours bien si votre version scipy <1.4

2
Tejas Shetty

Pour compléter la réponse de Cleb, voici un exemple d'utilisation de la méthode lambda t,y: fun(t,y,args). Nous avons mis en place le descripteur de fonction qui renvoie les rhs d'un ODE homogène de second ordre avec deux paramètres. Ensuite, nous l'introduisons dans notre solveur, avec quelques options.

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')
1
zahbaz

Pour être complet, je pense que vous pouvez également le faire, mais je ne sais pas pourquoi vous vous embêteriez avec les deux autres options affichées ici qui semblent parfaitement bien.

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)
1
Bill