Juliaで学ぶ確率微分方程式
Julia言語の疑似乱数にはdSFMTが採用されており, 特別な準備をせずに良質な疑似乱数を用いて計算を行うことができるため, 確率微分方程式(Stochastic Differential Equation, SDE)との相性が極めて良い. この記事では, 確率過程の概念, Eular-Maruyama法による種々の確率微分方程式の数値解法について, Julia言語での実装例を交えて解説する. 得られた数値解はPlots.jlを用いて可視化し, Fokker-Planck方程式の解析解と比較する.
パッケージ
可視化にはPlots.jlを用いる. Plots.jlの入門的内容はこちらのノートを参照されたい. plot()
を使用するためには, Juliaのパッケージモードでadd Plots
を実行し, 事前にPlots.jlをインストールしておく必要がある. ノート上ではusing Plots
を宣言する. また, using Printf
も宣言するが, 標準ライブラリであるためインストールは不要である.
versioninfo()
# using Pkg
# Pkg.add("Plots")
using Plots
using Printf
Julia Version 1.6.2
Commit 1b93d53fc4 (2021-07-14 15:36 UTC)
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: Intel(R) Core(TM) i7-4650U CPU @ 1.70GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-11.0.1 (ORCJIT, haswell)
確率過程
確率過程(stochastic process)とは, 時間に依存して変化する確率変数(stochastic variable)のことである. 時間が
いい加減な定義ではあるものの, 確率変数が
このノートでは, 離散確率過程の例としてランダムウォークモデルを, 連続確率過程の例としてWiener過程(数学ではBrown運動と呼ぶことがある), ドラフト項のあるWiener過程, Ornstein–Uhlenbeck過程などのを取り上げる. これらの連続確率過程は確率微分方程式(Stochastic Differential Equation, SDE)の解でもある.
ランダムウォークモデル
よく勘違いされるが, ランダムウォークモデルとWiener過程(数学ではBrown運動と呼ぶことがある)は別のモデルであり, 前者は離散確率過程で後者は連続確率過程である. ここでは, 離散確率過程の例としてランダムウォークモデルを取り上げる.
先に補足しておくと, ランダムウォークモデルの連続極限として拡散方程式を導出できる(付録参照). Wiener過程もその確率密度関数の時間発展が拡散方程式(Fokker-Planck方程式の特殊化)で記述されることから, これらの対応関係を知っておくことは学習の役に立つと思われる.
コインを投げて表なら
以下に, 横軸を投げた回数
step = 30
R = Int.((floor.(rand(step)*2) .- 0.5)*2)
X = zeros(Int64, step)
X[1] = R[1]
for i in 2:step
X[i] = X[i-1] + R[i]
end
println("R = ", R)
println("X = ", X)
plot(
plot(R, xlabel="\$i\$", ylabel="\$R_i\$", label=""),
plot(X, xlabel="\$i\$", ylabel="\$X_i\$", label=""),
size=(600, 300),
fmt=:png
)
savefig("randomwalk.png")
plot!()
R = [-1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1]
X = [-1, 0, -1, 0, -1, 0, 1, 2, 1, 2, 3, 2, 3, 2, 1, 2, 1, 2, 1, 2, 3, 4, 5, 6, 7, 6, 7, 6, 7, 6]
また, 500人でランダムウォーク大会を行った場合のグラフと, それぞれの
step = 30
member = 500
R = (floor.(rand(step, member)*2) .- 0.5)*2
X = zeros(step, member)
X[1,:] = R[1,:]
for i in 2:step
X[i,:] = X[i-1,:] + R[i,:]
end
plt1 = plot(R, alpha=0.2, label="", xlabel="\$i\$", ylabel="\$R_i\$", legend=:topleft)
plot!(plt1, 1:step, i->1, color=:red, label="Max ")
plot!(plt1, 1:step, i->-1, color=:blue, label="Min")
plt2 = plot(X, alpha=0.2, label="", xlabel="\$i\$", ylabel="\$X_i\$", legend=:topleft)
plot!(plt2, 1:step, i->i, color=:red, label="Max ")
plot!(plt2, 1:step, i->-i, color=:blue, label="Min")
plot(plt1, plt2, size=(600, 300), fmt=:png)
savefig("randomwalks.png")
plot!()
plt = []
for i in 1:12
push!(plt, histogram(X[i,:], title="i=$i", bins=range(-1.1*i,1.1*i,step=0.1), label=""))
end
plot(plt...)
savefig("randomwalks_histogram.svg")
plot!()
白色雑音とWiener過程
正確ではないが, 白色雑音
step = 1000
Du = 1.5
dt = 0.01
T = [dt*i for i in 0:step-1]
W = zeros(step)
dW = sqrt(2*Du*dt)*randn(step)
for i in 2:step
W[i] = W[i-1] + dW[i]
end
plot(
plot(T, dW, xlabel="\$i\\Delta t\$", ylabel="\$\\Delta W_i\$", label=""),
plot(T, W, xlabel="\$i\\Delta t\$", ylabel="\$W_i\$", label=""),
size=(600, 300),
fmt=:png
)
savefig("wiener_process.png")
plot!()
step = 1000
member = 200
Du = 1.5
dt = 0.01
T = [dt*i for i in 0:step-1]
W = zeros(step, member)
dW = sqrt(2*Du*dt)*randn(step, member)
for i in 2:step
W[i,:] = W[i-1,:] + dW[i,:]
end
plot(
plot(T, dW, xlabel="\$i\\Delta t\$", ylabel="\$\\Delta W_i\$", label="", lw=1, la=0.1, lc=[1 2 3 4 5 6 7 8 9 10]),
plot(T, W, xlabel="\$i\\Delta t\$", ylabel="\$W_i\$", label="", lw=1, la=0.1, lc=[1 2 3 4 5 6 7 8 9 10]),
size=(600, 300),
fmt=:png
)
savefig("wiener_processes.png")
plot!()
Langevin方程式を扱う物理学の文脈では, 白色雑音は揺動力として導入され, その性質を出発点とした相関関数の計算によって議論が進められる. 一方, 数学では白色雑音の扱いを避けて, Wiener過程を議論の出発点に採用することが多いようである(理由は後述する).
このノートでも, 白色雑音の性質は不要なので使わない. また, Wiener過程についても
なお,
である. 兼清(2017) p.103ではこの性質をWiener過程
step = 10
dt = 0.01
# Du = 1.5
# T = [dt*i for i in 0:step-1]
# W = zeros(step)
# dW = sqrt(2*Du*dt)*randn(step)
# for i in 2:step
# W[i] = W[i-1] + dW[i]
# end
# println("T = $T")
# println("W = $W")
T = [0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09]
W = [0.0, 0.058763734934133255, -0.3064429609662668, -0.1150187791553188, 0.20713134172141554, 0.118633670957381, 0.39487790707242687, 0.1440882274695458, -0.007232462279190721, -0.16312644695165768]
plot(xlims=(-dt,maximum(T)), ylims=(minimum(W)-0.15, maximum(W)), fmt=:png)
for i in 1:step
plot!([T[i],T[i]], [W[i],minimum(W)-0.125], label="", ls=:dash, lc="#000000", alpha=0.5)
end
for i in 1:step-1
plot!([T[i],T[i]], [W[i],W[i+1]], label="", lc="#24292E", arrow=(:closed, 2.0))
plot!([T[i],T[i+1]], [minimum(W)-0.05,minimum(W)-0.05], label="", lc="#24292E", arrow=(:closed, 2.0))
plot!([T[i],T[i+1]], [W[i+1],W[i+1]], label="", ls=:dash, lc="#000000", alpha=0.5)
plot!(annotations=((T[i]+T[i+1])/2, minimum(W)-0.09, ("\$+\\Delta t\$", 7, 0.0, :center)))
if !(i in [3,7])
plot!(annotations=(T[i]-dt/2, (W[i]+W[i+1])/2, ("\$+\\Delta W_$i\$", 7, 0.0, :center)))
end
end
i = 3
plot!(annotations=(T[i], W[i+1]+0.02, ("\$+\\Delta W_$i\$", 7, 0.0, :bottom)))
i = 7
plot!(annotations=(T[i]-dt/2, W[i+1], ("\$+\\Delta W_$i\$", 7, 0.0, :center)))
plot!(T, W, xlabel="\$i\\Delta t\$", ylabel="\$W_i\$", label="", lc=1, lw=2)
savefig("wiener_process_description.png")
plot!()
白色雑音を取り巻く問題
厳密には, 白色雑音
白色雑音
結論から言うと, このような白色雑音は, 数学的には確率過程のカテゴリーに入れて議論することができない
― 兼清泰明『確率微分方程式とその応用』(森北出版, 2017) p.100
白色雑音を数学的に厳密に取り扱うには, 確率超過程と呼ばれる概念を導入しなければならない
― 兼清泰明『確率微分方程式とその応用』(森北出版, 2017) p.100
白色雑音を数学的な議論の出発点とするのは適切ではない.
― 堀淳一『ランジュバン方程式』(岩波書店, 2015) p.53
2乗平均微分可能性の判定定理によって,
は2乗平均微分可能でなく, W(t) の微分過程として白色雑音 W(t) を定義することはできない P(t)
― 堀淳一『ランジュバン方程式』(岩波書店, 2015) p.54
連続時間過程としての白色雑音は厳密には存在しないため, もちろんそのグラフを正確に図示することはできない.
― 兼清泰明『確率微分方程式とその応用』(森北出版, 2017) p.102
確率微分方程式
微分方程式の解は微分可能でなければならない. Brown運動の軌道は明らかに微分可能ではないから, Brown運動の運動方程式(微分方程式)を考えるのはおかしい. そのようなわけで, Langevin方程式は通常の微分方程式としては解釈できない. ではどのように解釈すればよいかというと, 確率積分を含む方程式の別表記と考えるのである. というわけで, SDEは微分方程式というよりはむしろ積分方程式である. 確率微分方程式には確率積分としてIto積分を採用する立場とStratonovich積分を採用する立場の2つの立場があるが, このノートでは全てIto積分を採用する.
(3.2.3)の第2の積分をIto積分と解釈したとき, これらの方程式をItoのLangevin方程式あるいはItoの方程式という.
― 堀淳一『ランジュバン方程式』(岩波書店, 2015) p.61
Ito方程式(3.2.1)~(3.2.3)をBrown運動の運動方程式(Langevin方程式)として解釈しようとすると,
Fokker-Planck方程式
確率論と解析学の間には実り多い相互関係がある. その研究は少なくともKolmogorov(1931)にさかのぼる. (中略) 楕円型, 放物型偏微分方程式の多くの問題の解は確率汎関数の期待値として表現することができる.
― L.カラザス, S.E.シュレーブ著, 渡邉壽夫訳『ブラウン運動と確率積分』(丸善出版, 2012) 第4章 4.1序論
このノートでは, 上記のEular-Maruyama法による数値解の妥当性をFokker-Planck方程式によって評価する. Ito方程式
の解
が知られている. 以下に具体例をまとめる. 今回は
条件 | Ito方程式 | Fokker-Planck方程式 |
---|---|---|
Wiener過程 |
拡散方程式 |
|
ドリフト項のあるWiener過程 |
移流拡散方程式 |
|
Ornstein–Uhlenbeck過程 |
名前なし |
初期条件
D_u と拡散係数D
揺動力の強さ後に説明するが, 拡散方程式(
で結ばれる. Fokker-Planck方程式の説明だけを見ると揺動力の強さ
数値計算
ここではEular-Maruyama法を用いてWiener過程, ドリフト項のあるWiener過程, Ornstein–Uhlenbeck過程を数値的に解く.
Eular-Maruyama法
Eular-Maruyama法は確率初期値問題
を次のように離散化する方法である.
このとき,
Wiener過程
Wiener過程がGauss分布
Wiener過程はIto方程式の
Eular-Maruyama法による離散化は次式である.
対応するFokker-Planck方程式は次式である. これは拡散方程式と呼ばれる.
対応する初期条件はそれぞれ
であり, 境界条件
function SDE()
# パラメータ:
N = 5000 # 粒子数
dt = 0.1 # 時間刻み幅
step = 50 # 時間ステップ
name = "SDE_1" # ファイル名
x0 = 0 # 初期位置
Du = 1.5 # 揺動力の強さ(一般には拡散係数Dと一致しない)
D = Du # 厳密解の計算で使う拡散係数D
f(x,t) = 0 # 外力のようなもの
g(x,t) = 1 # 今回は1
exact(x) = exp(-(x-x0)^2/(4*D*t)) / sqrt(4*D*pi*t)
# 初期値:
t = 0.0
X = fill(x0, N)
Y = [-0.01-0.1*i/N for i in 1:N] # 描写のときのサンプルのy座標. 計算には関係ない
anim = Animation()
for i in 1:step
# 描写:
plt = scatter(X, Y, xlims=(-5,5), ms=1, msw=0, mc="#3261AB", label="Sample", xlabel="\$x\$", ylabel="\$P(x,t)\$")
histogram!(plt, X, bins=range(-5,5,step=0.2), ylims=(-0.14,1.0), normed=true, label="Histogram", fa=0.3, fc=1, lc="#FFFFFF")
plot!(plt, exact, lw=2, lc="#393e46", label="Exact PDF", title= @sprintf("t = %.2f",t))
frame(anim,plt)
# 計算:
t += dt
X += f.(X,t)*dt + g.(X,t).*sqrt(2*Du*dt).*randn(N)
end
gif(anim, name*".gif", fps=15)
end
SDE()
ドリフト項のあるWiener過程
Ito方程式の
Eular-Maruyama法による離散化は次式である.
対応するFokker-Planck方程式は次式である. これは移流拡散方程式と呼ばれる.
対応する初期条件はそれぞれ
であり, 境界条件
上記の解は全領域
function SDE()
# パラメータ:
N = 5000 # 粒子数
dt = 0.1 # 時間刻み幅
step = 50 # 時間ステップ
name = "SDE_2" # ファイル名
x0 = -4 # 初期位置
Du = 1.5 # 揺動力の強さ(一般には拡散係数Dと一致しない)
D = Du # 厳密解の計算で使う拡散係数D
c = 2.0 # 移流係数
f(x,t) = c # 外力のようなもの
g(x,t) = 1 # 今回は1
exact(x) = exp(-(x-x0-c*t)^2/(4*Du*t))/sqrt(4*pi*Du*t)
# 初期値:
t = 0.0
X = fill(x0, N)
Y = [-0.01-0.1*i/N for i in 1:N] # 描写のときのサンプルのy座標. 計算には関係ない
anim = Animation()
for i in 1:step
# 描写:
plt = scatter(X, Y, xlims=(-5,5), ms=1, msw=0, mc="#3261AB", label="Sample", xlabel="\$x\$", ylabel="\$P(x,t)\$")
histogram!(plt, X, bins=range(-5,5,step=0.2), ylims=(-0.14,1.0), normed=true, label="Histogram", fa=0.3, fc=1, lc="#FFFFFF")
plot!(plt, exact, lw=2, lc="#393e46", label="Exact PDF", title= @sprintf("t = %.2f",t))
frame(anim,plt)
# 計算:
t += dt
X += f.(X,t)*dt + g.(X,t).*sqrt(2*Du*dt).*randn(N)
end
gif(anim, name*".gif", fps=15)
end
SDE()
Ornstein–Uhlenbeck過程
Eular-Maruyama法による離散化は次式である.
対応するFokker-Planck方程式は次式である.
対応する初期条件はそれぞれ
であり, 境界条件
f(x)による横方向の移動はEular法で計算しており, 本来ならば短い時間刻みの間にも徐々に移動速度が小さくなっていくべきところを, 大きい速度のまま計算してしまっている. これはdtを小さくするか, 高次のスキームを使うなどすれば改善されるはずである.
function SDE()
# パラメータ:
N = 5000 # 粒子数
dt = 0.1 # 時間刻み幅
step = 50 # 時間ステップ
name = "SDE_3" # ファイル名
x0 = -4 # 初期位置
Du = 1.5 # 揺動力の強さ(一般には拡散係数Dと一致しない)
D = Du # 厳密解の計算で使う拡散係数D
γ = 1.5 # 粘性抵抗のようなもの
f(x,t) = -γ*x # 外力(粘性抵抗)のようなもの
g(x,t) = 1 # 今回は1
exact(x) = exp(-γ*(x-x0*exp(-γ*t))^2/(2*D*(1-exp(-2*γ*t))))*sqrt(γ/(2*pi*D*(1-exp(-2*γ*t))))
# 初期値:
t = 0.0
X = fill(x0, N)
Y = [-0.01-0.1*i/N for i in 1:N] # 描写のときのサンプルのy座標. 計算には関係ない
anim = Animation()
for i in 1:step
# 描写:
plt = scatter(X, Y, xlims=(-5,5), ms=1, msw=0, mc="#3261AB", label="Sample", xlabel="\$x\$", ylabel="\$P(x,t)\$")
histogram!(plt, X, bins=range(-5,5,step=0.2), ylims=(-0.14,1.0), normed=true, label="Histogram", fa=0.3, fc=1, lc="#FFFFFF")
plot!(plt, exact, lw=2, lc="#393e46", label="Exact PDF", title= @sprintf("t = %.2f",t))
frame(anim,plt)
# 計算:
t += dt
X += f.(X,t)*dt + g.(X,t).*sqrt(2*Du*dt).*randn(N)
end
gif(anim, name*".gif", fps=15)
end
SDE()
付録
拡散方程式の導出
コインの試行と言えば二項分布だが, 試行回数が多い時は正規分布に近似できることが知られている. そのため, 連続極限
サイコロを振って, 表なら
である. 両辺から
のように変形できる. 最後に両辺を
となり,
数値的に再現するには, ヒストグラムの面積に気を遣わなければいけない. ランダムウォークは偶数か奇数の格子上にしか分布しない(例えば2回コインを投げると-2, 0, 2であり, -1や1には存在しない)ので. step=dx
とすると, ヒストグラムの総面積を1にする都合上, 高さが2倍になってしまう. そのため, 隙間ができないようにstep=2*dx
としなければならない. さらに, 浮動小数点の都合上, 格子点上からは若干ズレるので, ヒストグラムの端は格子点上よりも格子の間においた方がよい.
function RW2D(N, step)
X = zeros(N)
for i in 1:step
X += (floor.(rand(N)*2) .- 0.5)*2
end
exact(D,x,t) = exp(-x^2/(4*D*t)) / sqrt(4*D*pi*t)
D = 2.0
dx = 0.1
dt = dx^2/(2*D)
return plot!(
histogram(X*dx, bins=range(minimum(X*dx)-dx/2,maximum(X*dx)+dx/2,step=dx*2), normed=true, label="", title="N=$N,step=$step", titlefontsize=10, fa=0.3, fc=1, lc="#FFFFFF"),
x->exact(D,x,step*dt), label="", lw=2, lc="#393e46"
)
end
plt = []
for j in [5,20,100]
for i in [10,100,10000]
push!(plt, RW2D(i,j))
end
end
plot(plt...)
参考文献
-
太田隆夫『非平衡系の物理学』(裳華房, 2000) p.67
ランダムウォークモデルの連続極限として拡散方程式を導出している. 拡散方程式を経由して, Wiener過程とランダムウォークの対応関係を考えるときに参考になる. -
石村直之『確率微分方程式入門―数理ファイナンスへの応用― 』(共立出版, 2014) pp.6,74,75
確率過程や確率微分方程式の定義を参考にした. -
大平徹『確率論 講義ノート 場合の数から確率微分方程式まで』(森北出版, 2017) pp.118,160,165,166,167
確率過程の定義を参考にした. Ornstein–Uhlenbeck過程の解が載っている. -
兼清泰明『確率微分方程式とその応用』(森北出版, 2017) p.1,100,102
白色雑音を取り巻く数学的な困難さについて述べられている. Ito-Taylor展開を用いた近似スキームの導出はこちらを参考にされたい. - B.エクセンダール著, 谷口説男訳『確率微分方程式 入門から応用まで』(丸善出版, 2012)
- L.カラザス, S.E.シュレーブ著, 渡邉壽夫訳『ブラウン運動と確率積分』(丸善出版, 2012)
-
堀淳一『ランジュバン方程式』(岩波書店, 2015) pp.4, 53, 54, 61, 80
数学的にも厳密であり, かつ物理への応用例が非常に充実している. 白色雑音についての困難さについても述べられており, Ito積分やItoのLangevin方程式についての説明がある. Ito方程式の定義はこれを参考にした. -
三井斌友, 小藤俊幸, 斉藤善弘『微分方程式による計算科学入門』(共立出版, 2004) pp.139, 150
SDEの数値計算について解説している希少な和書である. Eular-Maruyama法の表記はこれを参考にした.
この記事は2021年10月23日(土)に行われた第8回 ぶつりがく徒のつどい の講演資料を元に改良を加えたものです. 記事の元になったノートはGistにアップロードしてあります.
Discussion