Rパッケージ deSolveを使う

ここではRパッケージ deSolveの簡単な使い方を紹介する。 詳しくはPackage deSolve: Solving Initial Value Differential Equations in R を参照。

対象とする常微分方程式系

$(x,y,z)\in \mathbb{R}^3$内の次の微分方程式(Lorenz系)を例にしてDeSolveを使ってみよう。 \begin{align} \frac{dx}{dt} &=\sigma (y-x)\\ \frac{dy}{dt} &=r x -y - xz\\ \frac{dz}{dt} &=-b z + xy \end{align} [Deterministic Nonperiodic Flow (Edward N. Lorenz, J. Atmos. Sci., 20(11963), 130–141)]。 系の状態変数(state variables)は $(x, y, z)\in \mathbb{R}^3$、微分方程式系を定めるパラメータは$\sigma, b, r$ であるが、Lorenzに倣って $\sigma = 10$, $b=8/3$ を固定し、$r$ を様々に変化させるパラメータとしよう。 微分方程式の初期状態(初期値 initial values)を $x(0)=0, y(0)= 0.1, z(0)= 0$ とする。

deSolveの微分方程式の取り扱い

パッケージ deSolveを使うためには、まず、パッケージ deSolve を次のようにして読み込む。 以下で、Rのプロンプトを表す記号(>)は省略している。

> library(deSolve)

次いで、数値計算する対象の微分方程式を、パラメータと初期状態の組および解くべき時間幅とその差分刻みを定め、そして方程式を定義する。

パラメータと初期状態の組

Lorentz系でのパラメータを $\sigma(s) = 10$, $b=8/3 , r=28$ とするとき、これらをベクトル的に並べて任意のパラメータ変数名(ここでは parameters とした)を付ける。

parameters <- c(s = 10, b = 8/3, r = 28)

Lorenz系の初期条件値をベクトル的に並べて任意の変数名(ここでは initial とした)を付ける。

initial <- c(x=0, y=0.1, z=0)

解くべき時間幅とその差分刻み

微分方程式を解くべき時間(通常は時刻 $t=0$ から適当な時刻まで)をさだめ変数名(ここでは t とした)を付けた。 ここでは、時刻 0 から 100 までとし、数値計算のための時間刻み $\Delta t=0.01$ とした。

times <- seq(0, 100, 0.01)

微分方程式の定義

deSolveでは微分方程式を次のようにして関数定義し、任意の名称(こkでは Lorenz)を付けた。

Lorenz <- function(t, state, parameters) {
	with(as.list(c(state, parameters)), {
		dx <- s * (y - x)
		dy <- r * x - y - x * z
		dz <- -b * z + x * y
		list(c(dx, dy, dz))
	})
}

ここでの引数 t, state, parameters は、それぞれ「時間幅とその差分刻み」、「初期状態の組」および「パラメータの組」であるが、先に名付けた名称と同じである必要はないことに注意。 先に名付けた変数名は、deSolveの数値積分ルーチンの呼び出し時に引数として与える(後述)。

deSolveで数値積分を実行する

以上の準備をして、次のようにして数値積分を実行する。 その出力を out に代入している。

out <- ode(y = initial, times = times, func = Lorenz, parms = parameters)

ここで、引数値として initial, times, Lorenz, parameters は先に定めた変数値(任意の名称でよかった)を代入するのであるが、それぞれ、その代入先である y, times, func, parms は『このまま』の名称として使わねばならない

以上まとめると、次のようなソースとなる。

library(deSolve)# 微分方程式の数値解法パッケージ

parameters <- c(s = 10, b = 8/3, r = 28)# パラメータのセット
initial <- c(x=0, y=0.1, z=0)# 初期条件
times <- seq(0, 100, 0.01)# 差分刻み

Lorenz <- function(t, state, parameters) {
	with(as.list(c(state, parameters)), {
		dx <- s * (y - x)
		dy <- r * x - y - x * z
		dz <- -b * z + x * y
		list(c(dx, dy, dz))
	})
}

# deSolveを使った数値積分を out に出力
out <- ode(y = initial, times = times, func = Lorenz, parms = parameters)

計算結果

出力された数値積分結果 out の構造を見てみよう

> str(out)
 deSolve [1:10001, 1:4] 0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:4] "time" "x" "y" "z"
 - attr(*, "istate")= int [1:21] 2 11134 22324 NA 7 7 0 68 23 NA ...
 - attr(*, "rstate")= num [1:5] 0.01 0.01 100.01 0 72.92
 - attr(*, "lengthvar")= int 3
 - attr(*, "class")= chr [1:2] "deSolve" "matrix"
 - attr(*, "type")= chr "lsoda"

計算結果はデータフレームとして書き出されており、先頭の6業は次のようである。

> head(out)
     time           x         y            z
[1,] 0.00 0.000000000 0.1000000 0.000000e+00
[2,] 0.01 0.009512481 0.1003535 4.791048e-06
[3,] 0.02 0.018277226 0.1032424 1.869053e-05
[4,] 0.03 0.026597741 0.1084756 4.168197e-05
[5,] 0.04 0.034733515 0.1159428 7.456224e-05
[6,] 0.05 0.042911665 0.1256036 1.188774e-04

一列目が経過時間、2列目以降が対応する時刻の状態変数値であることがわかる。