Стохастические дифференциальные уравнения
В этом руководстве содержатся сведения о функциональных возможностях для решения стохастических дифференциальных уравнений (SDE). Другие руководства можно найти в SciMLTutorials.jl.
Здесь предполагается, что вы ознакомились с руководством об обыкновенных дифференциальных уравнениях. |
Пример 1. Скалярные стохастические дифференциальные уравнения
В этом примере мы будем решать уравнение
где и . Благодаря стохастическому исчислению мы знаем, что решение этого уравнения имеет следующий вид:
Для численного решения мы определяем тип задачи, задавая ей уравнение и начальное условие:
using DifferentialEquations
α = 1
β = 1
u₀ = 1 / 2
f(u, p, t) = α * u
g(u, p, t) = β * u
dt = 1 // 2^(4)
tspan = (0.0, 1.0)
prob = SDEProblem(f, g, u₀, (0.0, 1.0))
SDEProblem with uType Float64 and tType Float64. In-place: false
timespan: (0.0, 1.0)
u0: 0.5
Интерфейс solve
такой же, как и в случае с ODE. Здесь мы воспользуемся классическим алгоритмом Эйлера-Маруямы EM
и построим решение:
sol = solve(prob, EM(), dt = dt)
using Plots
plot(sol)
Использование методов более высокого порядка
Уникальной особенностью DifferentialEquations.jl является включение методов высших порядков для стохастических дифференциальных уравнений. Для справки приведем также аналитическое решение SDEProblem
. Для этого можно создать тестовую задачу. Она позволит оценить точность алгоритмов, или ее могут использовать разработчики методов для проверки сходимости алгоритмов. Таким образом, мы определяем объект задачи следующим образом:
f_analytic(u₀, p, t, W) = u₀ * exp((α - (β^2) / 2) * t + β * W)
ff = SDEFunction(f, g, analytic = f_analytic)
prob = SDEProblem(ff, g, u₀, (0.0, 1.0))
SDEProblem with uType Float64 and tType Float64. In-place: false
timespan: (0.0, 1.0)
u0: 0.5
и затем передаем эту информацию решателю и построим следующее:
#Мы можем построить классический алгоритм Эйлера-Маруямы следующим образом.
sol = solve(prob, EM(), dt = dt)
plot(sol, plot_analytic = true)
Для получения более точного результата можно выбрать решатель более высокого порядка:
sol = solve(prob, SRIW1(), dt = dt, adaptive = false)
plot(sol, plot_analytic = true)
По умолчанию методам высших порядков свойственна адаптивность. Поэтому можно использовать
sol = solve(prob, SRIW1())
plot(sol, plot_analytic = true)
Здесь мы позволили решателю автоматически определить начальный промежуток времени dt
. Это значение в начале является консервативным (небольшим), чтобы обеспечить точность. Но можно запустить метод с большим значением dt
, передав значение для начального промежутка dt
:
sol = solve(prob, SRIW1(), dt = dt)
plot(sol, plot_analytic = true)
Ансамблевое моделирование
Вместо того чтобы решать отдельные траектории, задачу можно преобразовать в задачу EnsembleProblem
для одновременного решения многих траекторий. Для этого используется конструктор EnsembleProblem
:
ensembleprob = EnsembleProblem(prob)
EnsembleProblem with problem SDEProblem
Команды решателя определены на странице о параллельном ансамблевом моделировании. Например, можно выбрать 1000 траекторий с помощью trajectories=1000
. Кроме того, при добавлении дополнительных процессов с помощью функции addprocs()
будет автоматически выполняться распараллеливание с использованием собственного параллелизма Julia, но этот вариант можно изменить на использование многопоточности с помощью EnsembleThreads()
. В совокупности это выглядит следующим образом.
sol = solve(ensembleprob, EnsembleThreads(), trajectories = 1000)
EnsembleSolution Solution of length 1000 with uType:
RODESolution{Float64, 1, Vector{Float64}, Vector{Float64}, Dict{Symbol, Float64}, Vector{Float64}, NoiseProcess{Float64, 1, Float64, Float64, Float64, Vector{Float64}, typeof(DiffEqNoiseProcess.WHITE_NOISE_DIST), typeof(DiffEqNoiseProcess.WHITE_NOISE_BRIDGE), false, ResettableStacks.ResettableStack{Tuple{Float64, Float64, Float64}, false}, ResettableStacks.ResettableStack{Tuple{Float64, Float64, Float64}, false}, RSWM{Float64}, Nothing, RandomNumbers.Xorshifts.Xoroshiro128Plus}, SDEProblem{Float64, Tuple{Float64, Float64}, false, SciMLBase.NullParameters, Nothing, SDEFunction{false, SciMLBase.FullSpecialize, typeof(Main.f), typeof(Main.g), UniformScaling{Bool}, typeof(Main.f_analytic), Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing}, typeof(Main.g), Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, Nothing}, SOSRI, StochasticDiffEq.LinearInterpolationData{Vector{Float64}, Vector{Float64}}, DiffEqBase.Stats, Nothing}
!!! warn При использовании пользовательского процесса шума, возможно, потребуется указать его в пользовательской функции prob_func в конструкторе EnsembleProblem, так как для каждой траектории необходим свой процесс шума.
Множество других элементов управления, включая инструменты анализа, определены на странице об ансамблевом моделировании. Очень простой анализ можно выполнить с помощью функции EnsembleSummary
, которая строит статистику средних/квадратичных значений и имеет соответствующий шаблон графика. Например, можно получить статистику на каждом временном шаге 0.01
и построить график среднее + погрешность, используя следующее.
using DifferentialEquations.EnsembleAnalysis
summ = EnsembleSummary(sol, 0:0.01:1)
plot(summ, labels = "Middle 95%")
summ = EnsembleSummary(sol, 0:0.01:1; quantiles = [0.25, 0.75])
plot!(summ, labels = "Middle 50%", legend = true)
Кроме того, можно легко вычислить корреляцию между значениями в t=0.2
и t=0.7
следующим образом.
timepoint_meancor(sol, 0.2, 0.7) # Дает оба средних значения, а затем коэффициент корреляции
(0.6138549457510034, 1.0029244305681482, 0.3919552711142436)
Пример 2. Системы стохастических дифференциальных уравнений с диагональным шумом
В общем случае SDE
обобщается до системы уравнений так же, как и ODE. Здесь g
теперь является матрицей значений. Одним из распространенных случаев, который используется по умолчанию в DifferentialEquations.jl, является диагональный шум, где g
— это диагональная матрица. Это означает, что каждая функция в системе получает свое случайное число. Вместо того чтобы работать с матрицами в этом случае, мы просто определяем f
и g
как функции на месте. Таким образом, f(du,u,p,t)
дает вектор du
, который является детерминированным изменением, а g(du2,u,p,t)
дает вектор du2
, для которого du2.*W
является стохастической частью уравнения.
Например, уравнение Лоренца с аддитивным шумом имеет ту же детерминированную часть, что и уравнения Лоренца, но к каждому шагу уравнения добавляется аддитивный шум, который просто равен 3*N(0,dt)
, где N
— нормальное распределение, а dt
— временной шаг. Это выполняется следующим образом.
using DifferentialEquations
using Plots
function lorenz(du, u, p, t)
du[1] = 10.0(u[2] - u[1])
du[2] = u[1] * (28.0 - u[3]) - u[2]
du[3] = u[1] * u[2] - (8 / 3) * u[3]
end
function σ_lorenz(du, u, p, t)
du[1] = 3.0
du[2] = 3.0
du[3] = 3.0
end
prob_sde_lorenz = SDEProblem(lorenz, σ_lorenz, [1.0, 0.0, 0.0], (0.0, 10.0))
sol = solve(prob_sde_lorenz)
plot(sol, idxs = (1, 2, 3))
Отметим, что для функции шума допустимо смешение членов уравнения. Например
function σ_lorenz(du, u, p, t)
du[1] = sin(u[3]) * 3.0
du[2] = u[2] * u[1] * 3.0
du[3] = 3.0
end
σ_lorenz (generic function with 1 method)
является допустимой функцией шума, которая снова выдаст диагональный шум с помощью du2.*W
.
Пример 3. Системы стохастических дифференциальных уравнений со скалярным шумом
В этом примере мы будем решать систему уравнений SDE со скалярным шумом. Это означает, что ко всем уравнениям SDE применяется один и тот же процесс шума. Нам необходимо определить процесс скалярного шума, используя интерфейс процесса шума. Поскольку нам необходим процесс WienerProcess
, который начинается в точке 0.0
в момент времени 0.0
, мы используем команду W = WienerProcess(0.0,0.0,0.0)
для определения нужного броуновского движения, а затем передаем его в параметр noise
в задаче SDEProblem
. Для полного примера решим линейное уравнение SDE со скалярным шумом с помощью алгоритма высокого порядка:
using DifferentialEquations
using Plots
f(du, u, p, t) = (du .= u)
g(du, u, p, t) = (du .= u)
u0 = rand(4, 2)
W = WienerProcess(0.0, 0.0, 0.0)
prob = SDEProblem(f, g, u0, (0.0, 1.0), noise = W)
sol = solve(prob, SRIW1())
plot(sol)
Пример 4. Системы стохастических дифференциальных уравнений (SDE) с недиагональным шумом
В предыдущих примерах у нас был диагональный шум, то есть вектор случайных чисел dW
, размер которого совпадает с выводом g
, где шум применяется поэлементно, и скалярный шум, когда ко всем зависимым переменным применяется одна случайная величина. Однако более общий вид шума позволяет линейно смешивать члены уравнения с помощью g
, являющегося матрицей.
Обратите внимание, что нелинейные смешивания не являются уравнениями SDE, а относятся к более общему классу случайных обыкновенных дифференциальных уравнений (RODE), которые имеют отдельный набор решателей.
Определим задачу с четырьмя процессами Винера и двумя зависимыми случайными переменными. В данном случае мы хотим, чтобы выводом g
была матрица 2x4 так, чтобы решением было g(u,p,t)*dW
, матричное умножение. Например, можно сделать следующее.
using DifferentialEquations
f(du, u, p, t) = du .= 1.01u
function g(du, u, p, t)
du[1, 1] = 0.3u[1]
du[1, 2] = 0.6u[1]
du[1, 3] = 0.9u[1]
du[1, 4] = 0.12u[1]
du[2, 1] = 1.2u[2]
du[2, 2] = 0.2u[2]
du[2, 3] = 0.3u[2]
du[2, 4] = 1.8u[2]
end
prob = SDEProblem(f, g, ones(2), (0.0, 1.0), noise_rate_prototype = zeros(2, 4))
SDEProblem with uType Vector{Float64} and tType Float64. In-place: true
timespan: (0.0, 1.0)
u0: 2-element Vector{Float64}:
1.0
1.0
В нашем g
мы определяем функции для вычисления значений матрицы. Теперь можно представить решаемое уравнение SDE как систему уравнений
означающую, что, например, du[1,1]
и du[2,1]
соответствуют стохастическим изменениям с одним и тем же случайным числом в первом и втором уравнениях SDE.
Эту задачу можно решить только моими методами SDE, которые совместимы с недиагональным шумом. Этот вопрос рассматривается на странице решателей SDE. |
Сама матрица определяется именованным аргументом noise_rate_prototype
в конструкторе SDEProblem
. Это прототип для типа, когда du
будет находиться в g
. Это может быть любой тип AbstractMatrix
. Итак, задача определяется следующим образом.
# Определить разреженную матрицу, создав плотную матрицу и задав некоторые значения не равными нулю
using SparseArrays
A = zeros(2, 4)
A[1, 1] = 1
A[1, 4] = 1
A[2, 4] = 1
A = sparse(A)
# Пусть `g` записывает значения разреженной матрицы
function g(du, u, p, t)
du[1, 1] = 0.3u[1]
du[1, 4] = 0.12u[2]
du[2, 4] = 1.8u[2]
end
# Пусть `g` использует разреженную матрицу
prob = SDEProblem(f, g, ones(2), (0.0, 1.0), noise_rate_prototype = A)
SDEProblem with uType Vector{Float64} and tType Float64. In-place: true
timespan: (0.0, 1.0)
u0: 2-element Vector{Float64}:
1.0
1.0
и теперь g(u,p,t)
выполняет запись в разреженную матрицу, а g(u,p,t)*dW
является умножением разреженных матриц.
Пример 4. Окрашенный шум
Окрашенный шум может быть определен с помощью интерфейса процесса шума. В этой части документации показано, как определить собственный процесс шума my_noise
, который может быть передан в SDEProblem.
SDEProblem(f, g, u0, tspan, noise = my_noise)
Учтите, что общие задачи с окрашенным шумом совместимы только с методами EM
и EulerHeun
. Этот вопрос рассматривается на странице решателей SDE.
Пример. Пространственно окрашенный шум в модели Хестона
Определим уравнение Хестона из финансовой математики:
Здесь у нас есть диагональная задача с шумом, которая задается следующим образом.
function f(du, u, p, t)
du[1] = μ * u[1]
du[2] = κ * (Θ - u[2])
end
function g(du, u, p, t)
du[1] = √u[2] * u[1]
du[2] = Θ * √u[2]
end
g (generic function with 1 method)
Однако шум имеет матрицу корреляции для некоторой константы ρ
. Выбираем ρ=0.2
:
ρ = 0.2
Γ = [1 ρ; ρ 1]
2×2 Matrix{Float64}:
1.0 0.2
0.2 1.0
Для решения этой задачи можно определить процесс CorrelatedWienerProcess
, который начинается с нуля (W(0)=0
), следующим образом:
tspan = (0.0, 1.0)
heston_noise = CorrelatedWienerProcess!(Γ, tspan[1], zeros(2), zeros(2))
t: 1-element Vector{Float64}:
0.0
u: 1-element Vector{Vector{Float64}}:
[0.0, 0.0]
Затем на основе этих данных строится SDE:
SDEProblem(f, g, ones(2), tspan, noise = heston_noise)
SDEProblem with uType Vector{Float64} and tType Float64. In-place: true
timespan: (0.0, 1.0)
u0: 2-element Vector{Float64}:
1.0
1.0
Конечно, для полного определения этой задачи необходимо определить константы. Конструкторы, упрощающие определение подобных моделей, можно найти в наборах инструментов моделирования. Например, задача HestonProblem
предопределена как часть инструментария финансового моделирования.