Webページでは数式を表すためにLaTeX表記が使えるMathJaxを利用しています。
WebブラウザにはSafari/Chrome/Firefoxを使って下さい(IEでは表示できないようです。)
SciPyで微分方程式を解く
モジュール SciPyの数値積分には微分方程式の数値解法 scipy.integrate.odeint が含まれている。 FORTRANライブラリのodepackにあるlsodaを使っているので高速な計算が可能である。 『硬い方程式』(stiff equation)にも対応しており汎用性が高い。 詳しい使い方はSciPyのドキュメントscipy.integrate.odeintを参照。
問題とする微分方程式系は$n$変数の1階微分方程式と$n$個の初期値の組 とする。 scipy.integrate.odeintの基本的な使い方は次のようである。
import scipy.integrate.odeint orbit = scipy.integrate.odeint(vectorfield, y0, t, args)
引数 vectorfield は微分方程式の右辺を定めるベクトル場リスト(以下の例)、u0 は初期値のリスト(array)、tは解くべき時間点のarrayリスト(最初の時間点での変数値が初期値 u0)、args はvectorfield に渡す引数のタプルである。 返り値 orbit はarrayリストで、解かれる時間点に応じた軌道変数値のarrayリスト [u0, u1, u2,...] である(各要素もまた変数の数の長さからなるarrayである)。
具体例
ベクトル場関数の与え方
Lorenz系(Deterministic Nonperiodic Flow J. Atmos. Sci., 20(11963), 130–141)では、odeintで呼ばれるベクトル場は次のように書くことができる。 p, b, r はベクトル場のパラメータ(ここでは定数)である。
def lorenz(u, t, p, b, r):
x = u[0]
y = u[1]
z = u[2]
dxdt = -p * x + p * y
dydt = -x * z + r * x - y
dzdt = x * y - b * z
return([dxdt, dydt, dzdt])
初期値の設定
odeintに渡す初期値は、$x_0 = 0.1, y_0=0.1, z_0=0.1$とすると次のように与える。
u0 = [0.1, 0.1, 0.1]
解くべき時間点
初期値 u0 の時間点を 0.0 としたとき、差分刻み 0.01 で 時刻 40 までの計算のために odeint に渡す時間点 times は次のように与える。dt = 0.01 T = 40.0 times = np.arange(0.0, T, dt)
プログラム例
以上をまとめて、Lorenz系をSciPyを使って微分方程式の数値解を求めて、軌道要素を3次元プロットするプログラム scipy_diff.py を次のように書く。 odeintで返される軌道要素は多数行$\times 3$のarrayであり、Numpyを使って効率化を図るで取り上げたように、 各列全ての要素リストはそれぞれ orbit[:, 0], orbit[:, 1], orbit[:, 2] でスライスできることに注意しよう(コメントアウトされた xorbit, yorbit, zorbitを改めて抜き出す計算は必要はない)。
# -*- coding: utf-8 -*-
# numerila solution using scipy.integrate
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
def lorenz(u, t, p, b, r):
x = u[0]
y = u[1]
z = u[2]
dxdt = -p * x + p * y
dydt = -x * z + r * x - y
dzdt = x * y - b * z
return([dxdt, dydt, dzdt])
# parameters
p =10
b = 8.0 / 3.0
r = 28
# initial values
u0 = [0.1, 0.1, 0.1]
# setting
dt = 0.01
T = 40.0
times = np.arange(0.0, T, dt)
# numerical solution using scipy.integrate
args = (p, b, r)
orbit = odeint(lorenz, x0, times, args)
#xorbit = []
#yorbit = []
#zorbit = []
#for x, y, z in orbit:
# xorbit.append(x)
# yorbit.append(y)
# zorbit.append(z)
#plot3D using matplotlib
fig = plt.figure()
ax = fig.gca(projection='3d')
#ax.plot(xorbit, yorbit, zorbit)
ax.plot(orbit[:, 0], orbit[:, 1], orbit[:, 2])
ax.set_xlabel('x(t)')
ax.set_ylabel('y(t)')
ax.set_zlabel('z(t)')
plt.show()