solve_ivp#
- scipy.integrate.solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False, events=None, vectorized=False, args=None, **options)[Quelle]#
Löst ein Anfangswertproblem für ein System von ODEs.
Diese Funktion integriert numerisch ein System von gewöhnlichen Differentialgleichungen, gegeben einen Anfangswert.
dy / dt = f(t, y) y(t0) = y0
Hier ist t eine 1-dimensionale unabhängige Variable (Zeit), y(t) ist eine N-dimensionale vektorwertige Funktion (Zustand) und eine N-dimensionale vektorwertige Funktion f(t, y) bestimmt die Differentialgleichungen. Das Ziel ist, y(t) approximativ zu finden, das die Differentialgleichungen erfüllt, gegeben einen Anfangswert y(t0)=y0.
Einige der Löser unterstützen die Integration im komplexen Bereich, aber beachten Sie, dass für steife ODE-Löser die rechte Seite komplex differenzierbar sein muss (Cauchy-Riemann-Gleichungen erfüllen [11]). Um ein Problem im komplexen Bereich zu lösen, übergeben Sie y0 mit einem komplexen Datentyp. Eine andere immer verfügbare Option ist, Ihr Problem separat für Real- und Imaginärteil umzuschreiben.
- Parameter:
- funcallable
Rechte Seite des Systems: die Zeitableitung des Zustands
yzum Zeitpunktt. Die Aufrufsignatur istfun(t, y), wobeitein Skalar undyein ndarray mitlen(y) = len(y0)ist. Zusätzliche Argumente müssen übergeben werden, wennargsverwendet wird (siehe Dokumentation des Argumentsargs).funmuss ein Array der gleichen Form wieyzurückgeben. Siehe vectorized für weitere Informationen.- t_span2-element Sequenz
Integrationsintervall (t0, tf). Der Löser startet mit t=t0 und integriert, bis er t=tf erreicht. Sowohl t0 als auch tf müssen Floats oder Werte sein, die von der Float-Konvertierungsfunktion interpretierbar sind.
- y0array_like, shape (n,)
Anfangszustand. Für Probleme im komplexen Bereich übergeben Sie y0 mit einem komplexen Datentyp (auch wenn der Anfangswert rein reell ist).
- methodString oder
OdeSolver, optional Verwendete Integrationsmethode
‘RK45’ (Standard): Explizite Runge-Kutta-Methode der Ordnung 5(4) [1]. Der Fehler wird unter der Annahme der Genauigkeit der Methode vierter Ordnung kontrolliert, aber Schritte werden unter Verwendung der Formel fünfter Ordnung (lokale Extrapolation wird durchgeführt) unternommen. Ein quartisches Interpolationspolynom wird für die dichte Ausgabe verwendet [2]. Kann im komplexen Bereich angewendet werden.
‘RK23’: Explizite Runge-Kutta-Methode der Ordnung 3(2) [3]. Der Fehler wird unter der Annahme der Genauigkeit der Methode zweiter Ordnung kontrolliert, aber Schritte werden unter Verwendung der Formel dritter Ordnung (lokale Extrapolation wird durchgeführt) unternommen. Ein kubisches Hermite-Polynom wird für die dichte Ausgabe verwendet. Kann im komplexen Bereich angewendet werden.
‘DOP853’: Explizite Runge-Kutta-Methode der Ordnung 8 [13]. Python-Implementierung des Algorithmus „DOP853“, ursprünglich in Fortran geschrieben [14]. Ein Interpolationspolynom 7. Ordnung mit Genauigkeit 7. Ordnung wird für die dichte Ausgabe verwendet. Kann im komplexen Bereich angewendet werden.
‘Radau’: Implizite Runge-Kutta-Methode der Radau IIA-Familie der Ordnung 5 [4]. Der Fehler wird mit einer Formel dritter Ordnung gesteuert. Ein kubisches Polynom, das die Kollokationsbedingungen erfüllt, wird für die dichte Ausgabe verwendet.
‘BDF’: Implizite Mehrschrittmethode variabler Ordnung (1 bis 5) basierend auf einer Backward-Differentiation-Formel zur Approximation der Ableitung [5]. Die Implementierung folgt der in [6] beschriebenen. Ein quasi-konstantes Schrittweitenverfahren wird verwendet und die Genauigkeit wird durch die NDF-Modifikation erhöht. Kann im komplexen Bereich angewendet werden.
‘LSODA’: Adams/BDF-Methode mit automatischer Steifigkeitserkennung und -umschaltung [7], [8]. Dies ist ein Wrapper für den Fortran-Löser von ODEPACK.
Explizite Runge-Kutta-Methoden (‘RK23’, ‘RK45’, ‘DOP853’) sollten für nicht-steife Probleme verwendet werden und implizite Methoden (‘Radau’, ‘BDF’) für steife Probleme [9]. Unter den Runge-Kutta-Methoden wird ‘DOP853’ für die Lösung mit hoher Präzision (niedrige Werte von rtol und atol) empfohlen.
Wenn Sie unsicher sind, versuchen Sie zuerst, ‘RK45’ auszuführen. Wenn es ungewöhnlich viele Iterationen benötigt, divergiert oder fehlschlägt, ist Ihr Problem wahrscheinlich steif und Sie sollten ‘Radau’ oder ‘BDF’ verwenden. ‘LSODA’ kann auch eine gute universelle Wahl sein, aber es könnte etwas umständlicher zu handhaben sein, da es alte Fortran-Codes umschließt.
Sie können auch eine beliebige Klasse übergeben, die von
OdeSolverabgeleitet ist und den Löser implementiert.- t_evalArray-ähnlich oder None, optional
Zeitpunkte, an denen die berechnete Lösung gespeichert werden soll; müssen sortiert sein und innerhalb von t_span liegen. Wenn None (Standard), werden Punkte verwendet, die vom Löser ausgewählt wurden.
- dense_outputBool, optional
Ob eine kontinuierliche Lösung berechnet werden soll. Standard ist False.
- eventsAufrufbar oder Liste von Aufrufbaren, optional
Zu verfolgende Ereignisse. Wenn None (Standard), werden keine Ereignisse verfolgt. Jedes Ereignis tritt an den Nullstellen einer kontinuierlichen Funktion von Zeit und Zustand auf. Jede Funktion muss die Signatur
event(t, y)haben, wobei zusätzliche Argumente übergeben werden müssen, wennargsverwendet wird (siehe Dokumentation des Argumentsargs). Jede Funktion muss einen Float zurückgeben. Der Löser findet einen genauen Wert von t, an demevent(t, y(t)) = 0gilt, unter Verwendung eines Wurzel-Findungsalgorithmus. Standardmäßig werden alle Nullstellen gefunden. Der Löser sucht nach einem Vorzeichenwechsel über jeden Schritt, sodass, wenn mehrere Nullstellenübergänge innerhalb eines Schritts auftreten, Ereignisse möglicherweise übersehen werden. Zusätzlich kann jede event-Funktion die folgenden Attribute haben- terminal: Bool oder Int, optional
Wenn boolesch, ob die Integration abgebrochen werden soll, wenn dieses Ereignis eintritt. Wenn integral, tritt die Beendigung nach der angegebenen Anzahl von Ereignissen dieses Typs ein. Standardmäßig False, wenn nicht zugewiesen.
- direction: Float, optional
Richtung eines Nullstellenübergangs. Wenn direction positiv ist, wird event nur ausgelöst, wenn von negativ nach positiv gegangen wird, und umgekehrt, wenn direction negativ ist. Wenn 0, dann löst jede Richtung das Ereignis aus. Standardmäßig 0, wenn nicht zugewiesen.
Sie können Attribute wie
event.terminal = Truejeder Funktion in Python zuweisen.- vectorizedbool, optional
Ob fun vektorisiert aufgerufen werden kann. Standard ist False.
Wenn
vectorizedFalse ist, wird fun immer mityder Form(n,)aufgerufen, wobein = len(y0)ist.Wenn
vectorizedTrue ist, kann fun mityder Form(n, k)aufgerufen werden, wobeikeine Ganzzahl ist. In diesem Fall muss fun so funktionieren, dassfun(t, y)[:, i] == fun(t, y[:, i])(d. h. jede Spalte des zurückgegebenen Arrays ist die Zeitableitung des Zustands, der einer Spalte vonyentspricht).Das Setzen von
vectorized=Trueermöglicht eine schnellere finite Differenzen-Approximation des Jacobi-Matrix für die Methoden ‘Radau’ und ‘BDF’, führt aber zu einer langsameren Ausführung für andere Methoden und für ‘Radau’ und ‘BDF’ unter bestimmten Umständen (z. B. kleinelen(y0)).- argstuple, optional
Zusätzliche Argumente, die an die benutzerdefinierten Funktionen übergeben werden sollen. Wenn angegeben, werden die zusätzlichen Argumente an alle benutzerdefinierten Funktionen übergeben. Wenn also beispielsweise fun die Signatur
fun(t, y, a, b, c)hat, dann müssen jac (falls angegeben) und alle Ereignisfunktionen die gleiche Signatur haben und args muss ein Tupel der Länge 3 sein.- **options
An den gewählten Löser übergebene Optionen. Alle verfügbaren Optionen für bereits implementierte Löser sind unten aufgelistet.
- first_stepfloat or None, optional
Anfängliche Schrittweite. Standard ist None, was bedeutet, dass der Algorithmus dies wählen sollte.
- max_stepfloat, optional
Maximale zulässige Schrittgröße. Standard ist np.inf, d.h. die Schrittgröße ist nicht begrenzt und wird ausschließlich vom Solver bestimmt.
- rtol, atolFloat oder Array-ähnlich, optional
Relative und absolute Toleranzen. Der Löser hält die lokalen Fehlerabschätzungen kleiner als
atol + rtol * abs(y). Hier steuert rtol die relative Genauigkeit (Anzahl korrekter Ziffern), während atol die absolute Genauigkeit (Anzahl korrekter Dezimalstellen) steuert. Um die gewünschte rtol zu erreichen, setzen Sie atol kleiner als den kleinsten Wert, der vonrtol * abs(y)erwartet werden kann, sodass rtol den erlaubten Fehler dominiert. Wenn atol größer alsrtol * abs(y)ist, ist die Anzahl der korrekten Ziffern nicht garantiert. Umgekehrt, um die gewünschte atol zu erreichen, setzen Sie rtol so, dassrtol * abs(y)immer kleiner als atol ist. Wenn Komponenten von y unterschiedliche Skalen haben, kann es vorteilhaft sein, unterschiedliche atol-Werte für verschiedene Komponenten festzulegen, indem Array-ähnliches mit der Form (n,) für atol übergeben wird. Standardwerte sind 1e-3 für rtol und 1e-6 für atol.- jacArray-ähnlich, dünn besetztes Matrix, aufrufbar oder None, optional
Jacobi-Matrix der rechten Seite des Systems in Bezug auf y, erforderlich für die Methoden ‘Radau’, ‘BDF’ und ‘LSODA’. Die Jacobi-Matrix hat die Form (n, n) und ihr Element (i, j) ist gleich
d f_i / d y_j. Es gibt drei Möglichkeiten, die Jacobi-Matrix zu definieren:Wenn Array-ähnlich oder dünn besetztes Matrix, wird angenommen, dass die Jacobi-Matrix konstant ist. Nicht unterstützt von ‘LSODA’.
Wenn aufrufbar, wird angenommen, dass die Jacobi-Matrix sowohl von t als auch von y abhängt; sie wird bei Bedarf als
jac(t, y)aufgerufen. Zusätzliche Argumente müssen übergeben werden, wennargsverwendet wird (siehe Dokumentation des Argumentsargs). Für die Methoden ‘Radau’ und ‘BDF’ kann der Rückgabewert eine dünn besetzte Matrix sein.Wenn None (Standard), wird die Jacobi-Matrix durch endliche Differenzen angenähert.
Es wird generell empfohlen, die Jacobi-Matrix bereitzustellen, anstatt sich auf eine Approximation durch endliche Differenzen zu verlassen.
- jac_sparsityArray-ähnlich, dünn besetztes Matrix oder None, optional
Definiert die Sparsity-Struktur der Jacobi-Matrix für eine finite Differenzen-Approximation. Ihre Form muss (n, n) sein. Dieses Argument wird ignoriert, wenn jac nicht None ist. Wenn die Jacobi-Matrix in jeder Zeile nur wenige Nicht-Null-Elemente hat, wird die Angabe der Sparsity-Struktur die Berechnungen erheblich beschleunigen [10]. Ein Nulleintrag bedeutet, dass ein entsprechendes Element in der Jacobi-Matrix immer Null ist. Wenn None (Standard), wird angenommen, dass die Jacobi-Matrix dicht besetzt ist. Nicht unterstützt von ‘LSODA’, siehe stattdessen lband und uband.
- lband, ubandInt oder None, optional
Parameter, die die Bandbreite der Jacobi-Matrix für die Methode ‘LSODA’ definieren, d. h.,
jac[i, j] != 0 nur für i - lband <= j <= i + uband. Standard ist None. Das Setzen dieser Werte erfordert, dass Ihre jac-Routine die Jacobi-Matrix im gepackten Format zurückgibt: das zurückgegebene Array mussnSpalten unduband + lband + 1Zeilen haben, in denen die Diagonalen der Jacobi-Matrix geschrieben sind. Genauer gesagtjac_packed[uband + i - j , j] = jac[i, j]. Das gleiche Format wird inscipy.linalg.solve_bandedverwendet (siehe Abbildung). Diese Parameter können auch mitjac=Noneverwendet werden, um die Anzahl der durch endliche Differenzen geschätzten Jacobi-Elemente zu reduzieren.- min_stepFloat, optional
Die minimal zulässige Schrittweite für die Methode ‘LSODA’. Standardmäßig ist min_step Null.
- Rückgabe:
- Bunch-Objekt mit den folgenden definierten Feldern
- tndarray, Form (n_points,)
Zeitpunkte.
- yndarray, Form (n, n_points)
Werte der Lösung bei t.
- sol
OdeSolutionoder None Gefundene Lösung als
OdeSolution-Instanz; None, wenn dense_output auf False gesetzt war.- t_eventsListe von ndarrays oder None
Enthält für jeden Ereignistyp eine Liste von Arrays, an denen ein Ereignis dieses Typs erkannt wurde. None, wenn events None war.
- y_eventsListe von ndarrays oder None
Für jeden Wert von t_events der entsprechende Wert der Lösung. None, wenn events None war.
- nfevint
Anzahl der Auswertungen der rechten Seite.
- njevint
Anzahl der Auswertungen der Jacobi-Matrix.
- nluint
Anzahl der LU-Zerlegungen.
- statusint
Grund für die Beendigung des Algorithmus
-1: Integrationsschritt fehlgeschlagen.
0: Der Löser hat das Ende von tspan erfolgreich erreicht.
1: Ein terminierendes Ereignis ist aufgetreten.
- messageString
Menschenlesbare Beschreibung des Abbruchgrundes.
- successbool
True, wenn der Löser das Intervallende erreicht hat oder ein terminierendes Ereignis aufgetreten ist (
status >= 0).
Referenzen
[1]J. R. Dormand, P. J. Prince, „A family of embedded Runge-Kutta formulae“, Journal of Computational and Applied Mathematics, Vol. 6, No. 1, S. 19-26, 1980.
[2]L. W. Shampine, „Some Practical Runge-Kutta Formulas“, Mathematics of Computation, Vol. 46, No. 173, S. 135-150, 1986.
[3]P. Bogacki, L.F. Shampine, „A 3(2) Pair of Runge-Kutta Formulas“, Appl. Math. Lett. Vol. 2, No. 4. S. 321-325, 1989.
[4]E. Hairer, G. Wanner, „Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems“, Kap. IV.8.
[5]Backward Differentiation Formula auf Wikipedia.
[6]L. F. Shampine, M. W. Reichelt, „THE MATLAB ODE SUITE“, SIAM J. SCI. COMPUTE., Vol. 18, No. 1, S. 1-22, Januar 1997.
[7]A. C. Hindmarsh, „ODEPACK, A Systematized Collection of ODE Solvers“, IMACS Transactions on Scientific Computation, Vol 1., S. 55-64, 1983.
[8]L. Petzold, „Automatic selection of methods for solving stiff and nonstiff systems of ordinary differential equations“, SIAM Journal on Scientific and Statistical Computing, Vol. 4, No. 1, S. 136-148, 1983.
[9]Steife Gleichung auf Wikipedia.
[10]A. Curtis, M. J. D. Powell und J. Reid, „On the estimation of sparse Jacobian matrices“, Journal of the Institute of Mathematics and its Applications, 13, S. 117-120, 1974.
[11]Cauchy-Riemann-Gleichungen auf Wikipedia.
[12]Lotka-Volterra-Gleichungen auf Wikipedia.
[13]E. Hairer, S. P. Norsett G. Wanner, „Solving Ordinary Differential Equations I: Nonstiff Problems“, Kap. II.
Beispiele
Einfaches exponentielles Abklingen, das automatisch gewählte Zeitpunkte zeigt.
>>> import numpy as np >>> from scipy.integrate import solve_ivp >>> def exponential_decay(t, y): return -0.5 * y >>> sol = solve_ivp(exponential_decay, [0, 10], [2, 4, 8]) >>> print(sol.t) [ 0. 0.11487653 1.26364188 3.06061781 4.81611105 6.57445806 8.33328988 10. ] >>> print(sol.y) [[2. 1.88836035 1.06327177 0.43319312 0.18017253 0.07483045 0.03107158 0.01350781] [4. 3.7767207 2.12654355 0.86638624 0.36034507 0.14966091 0.06214316 0.02701561] [8. 7.5534414 4.25308709 1.73277247 0.72069014 0.29932181 0.12428631 0.05403123]]
Spezifikation von Zeitpunkten, an denen die Lösung gewünscht wird.
>>> sol = solve_ivp(exponential_decay, [0, 10], [2, 4, 8], ... t_eval=[0, 1, 2, 4, 10]) >>> print(sol.t) [ 0 1 2 4 10] >>> print(sol.y) [[2. 1.21305369 0.73534021 0.27066736 0.01350938] [4. 2.42610739 1.47068043 0.54133472 0.02701876] [8. 4.85221478 2.94136085 1.08266944 0.05403753]]
Eine Kanonenkugel wird nach oben geschossen und trifft mit einem terminierenden Ereignis auf den Boden. Die Felder
terminalunddirectioneines Ereignisses werden durch Monkey-Patching einer Funktion angewendet. Hier isty[0]die Position undy[1]die Geschwindigkeit. Das Projektil startet bei Position 0 mit Geschwindigkeit +10. Beachten Sie, dass die Integration niemals t=100 erreicht, da das Ereignis terminierend ist.>>> def upward_cannon(t, y): return [y[1], -0.5] >>> def hit_ground(t, y): return y[0] >>> hit_ground.terminal = True >>> hit_ground.direction = -1 >>> sol = solve_ivp(upward_cannon, [0, 100], [0, 10], events=hit_ground) >>> print(sol.t_events) [array([40.])] >>> print(sol.t) [0.00000000e+00 9.99900010e-05 1.09989001e-03 1.10988901e-02 1.11088891e-01 1.11098890e+00 1.11099890e+01 4.00000000e+01]
Verwendung von dense_output und events, um die Position 100 am Scheitelpunkt der Flugbahn der Kanonenkugel zu finden. Der Scheitelpunkt ist nicht als terminierend definiert, daher werden sowohl der Scheitelpunkt als auch der Bodenkontakt gefunden. Bei t=20 liegen keine Informationen vor, daher wird das Attribut sol zur Auswertung der Lösung verwendet. Das Attribut sol wird zurückgegeben, indem
dense_output=Truegesetzt wird. Alternativ kann das Attribut y_events verwendet werden, um auf die Lösung zum Zeitpunkt des Ereignisses zuzugreifen.>>> def apex(t, y): return y[1] >>> sol = solve_ivp(upward_cannon, [0, 100], [0, 10], ... events=(hit_ground, apex), dense_output=True) >>> print(sol.t_events) [array([40.]), array([20.])] >>> print(sol.t) [0.00000000e+00 9.99900010e-05 1.09989001e-03 1.10988901e-02 1.11088891e-01 1.11098890e+00 1.11099890e+01 4.00000000e+01] >>> print(sol.sol(sol.t_events[1][0])) [100. 0.] >>> print(sol.y_events) [array([[-5.68434189e-14, -1.00000000e+01]]), array([[1.00000000e+02, 1.77635684e-15]])]
Als Beispiel für ein System mit zusätzlichen Parametern implementieren wir die Lotka-Volterra-Gleichungen [12].
>>> def lotkavolterra(t, z, a, b, c, d): ... x, y = z ... return [a*x - b*x*y, -c*y + d*x*y] ...
Wir übergeben die Parameterwerte a=1.5, b=1, c=3 und d=1 mit dem Argument args.
>>> sol = solve_ivp(lotkavolterra, [0, 15], [10, 5], args=(1.5, 1, 3, 1), ... dense_output=True)
Berechnung einer dichten Lösung und deren Plotten.
>>> t = np.linspace(0, 15, 300) >>> z = sol.sol(t) >>> import matplotlib.pyplot as plt >>> plt.plot(t, z.T) >>> plt.xlabel('t') >>> plt.legend(['x', 'y'], shadow=True) >>> plt.title('Lotka-Volterra System') >>> plt.show()
Ein paar Beispiele für die Verwendung von solve_ivp zur Lösung der Differentialgleichung
y' = Aymit einer komplexen MatrixA.>>> A = np.array([[-0.25 + 0.14j, 0, 0.33 + 0.44j], ... [0.25 + 0.58j, -0.2 + 0.14j, 0], ... [0, 0.2 + 0.4j, -0.1 + 0.97j]])
Lösen eines IVP mit
Aaus dem obigen Beispiel undyals 3x1-Vektor.>>> def deriv_vec(t, y): ... return A @ y >>> result = solve_ivp(deriv_vec, [0, 25], ... np.array([10 + 0j, 20 + 0j, 30 + 0j]), ... t_eval=np.linspace(0, 25, 101)) >>> print(result.y[:, 0]) [10.+0.j 20.+0.j 30.+0.j] >>> print(result.y[:, -1]) [18.46291039+45.25653651j 10.01569306+36.23293216j -4.98662741+80.07360388j]
Lösen eines IVP mit
Aaus dem obigen Beispiel mityals 3x3-Matrix.>>> def deriv_mat(t, y): ... return (A @ y.reshape(3, 3)).flatten() >>> y0 = np.array([[2 + 0j, 3 + 0j, 4 + 0j], ... [5 + 0j, 6 + 0j, 7 + 0j], ... [9 + 0j, 34 + 0j, 78 + 0j]])
>>> result = solve_ivp(deriv_mat, [0, 25], y0.flatten(), ... t_eval=np.linspace(0, 25, 101)) >>> print(result.y[:, 0].reshape(3, 3)) [[ 2.+0.j 3.+0.j 4.+0.j] [ 5.+0.j 6.+0.j 7.+0.j] [ 9.+0.j 34.+0.j 78.+0.j]] >>> print(result.y[:, -1].reshape(3, 3)) [[ 5.67451179 +12.07938445j 17.2888073 +31.03278837j 37.83405768 +63.25138759j] [ 3.39949503 +11.82123994j 21.32530996 +44.88668871j 53.17531184+103.80400411j] [ -2.26105874 +22.19277664j -15.1255713 +70.19616341j -38.34616845+153.29039931j]]