Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d901459

Browse files
committedMay 23, 2024·
replacing #445
1 parent 4468838 commit d901459

File tree

3 files changed

+2983
-0
lines changed

3 files changed

+2983
-0
lines changed
 

‎tutorials/10-bayesian-stochastic-differential-equations/Manifest.toml

Lines changed: 2809 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[deps]
2+
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
3+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
4+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
5+
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"
6+
Turing = "fce5fe82-541a-59a6-adf8-730c64b5f9a0"
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
---
2+
title: Bayesian Estimation of Differential Equations
3+
engine: julia
4+
---
5+
6+
```{julia}
7+
#| echo: false
8+
#| output: false
9+
using Pkg;
10+
Pkg.instantiate();
11+
```
12+
13+
::: {.callout-note collapse="true"}
14+
15+
## This Part is from [Bayesian Differential Equations Notebook](../10-bayesian-differential-equations/)
16+
17+
```{julia}
18+
using Turing
19+
using DifferentialEquations
20+
21+
# Load StatsPlots for visualizations and diagnostics.
22+
using StatsPlots
23+
24+
using LinearAlgebra
25+
26+
# Set a seed for reproducibility.
27+
using Random
28+
Random.seed!(14);
29+
```
30+
31+
## The Lotka-Volterra Model
32+
33+
The Lotka–Volterra equations, also known as the predator–prey equations, are a pair of first-order nonlinear differential equations.
34+
These differential equations are frequently used to describe the dynamics of biological systems in which two species interact, one as a predator and the other as prey.
35+
The populations change through time according to the pair of equations
36+
37+
$$
38+
\begin{aligned}
39+
\frac{\mathrm{d}x}{\mathrm{d}t} &= (\alpha - \beta y(t))x(t), \\
40+
\frac{\mathrm{d}y}{\mathrm{d}t} &= (\delta x(t) - \gamma)y(t)
41+
\end{aligned}
42+
$$
43+
44+
where $x(t)$ and $y(t)$ denote the populations of prey and predator at time $t$, respectively, and $\alpha, \beta, \gamma, \delta$ are positive parameters.
45+
46+
We implement the Lotka-Volterra model and simulate it with parameters $\alpha = 1.5$, $\beta = 1$, $\gamma = 3$, and $\delta = 1$ and initial conditions $x(0) = y(0) = 1$.
47+
48+
```{julia}
49+
# Define Lotka-Volterra model.
50+
function lotka_volterra(du, u, p, t)
51+
# Model parameters.
52+
α, β, γ, δ = p
53+
# Current state.
54+
x, y = u
55+
56+
# Evaluate differential equations.
57+
du[1] = (α - β * y) * x # prey
58+
du[2] = (δ * x - γ) * y # predator
59+
60+
return nothing
61+
end
62+
63+
# Define initial-value problem.
64+
u0 = [1.0, 1.0]
65+
p = [1.5, 1.0, 3.0, 1.0]
66+
tspan = (0.0, 10.0)
67+
prob = ODEProblem(lotka_volterra, u0, tspan, p)
68+
69+
# Plot simulation.
70+
plot(solve(prob, Tsit5()))
71+
```
72+
73+
We generate noisy observations to use for the parameter estimation tasks in this tutorial.
74+
With the [`saveat` argument](https://docs.sciml.ai/latest/basics/common_solver_opts/) we specify that the solution is stored only at `0.1` time units.
75+
To make the example more realistic we add random normally distributed noise to the simulation.
76+
77+
```{julia}
78+
sol = solve(prob, Tsit5(); saveat=0.1)
79+
odedata = Array(sol) + 0.8 * randn(size(Array(sol)))
80+
81+
# Plot simulation and noisy observations.
82+
plot(sol; alpha=0.3)
83+
scatter!(sol.t, odedata'; color=[1 2], label="")
84+
```
85+
86+
Alternatively, we can use real-world data from Hudson’s Bay Company records (an Stan implementation with slightly different priors can be found here: https://mc-stan.org/users/documentation/case-studies/lotka-volterra-predator-prey.html).
87+
88+
:::
89+
90+
## Inference of a Stochastic Differential Equation
91+
92+
A [Stochastic Differential Equation (SDE)](https://diffeq.sciml.ai/stable/tutorials/sde_example/) is a differential equation that has a stochastic (noise) term in the expression of the derivatives.
93+
Here we fit a stochastic version of the Lokta-Volterra system.
94+
95+
We use a quasi-likelihood approach in which all trajectories of a solution are compared instead of a reduction such as mean, this increases the robustness of fitting and makes the likelihood more identifiable.
96+
We use SOSRI to solve the equation.
97+
98+
```{julia}
99+
u0 = [1.0, 1.0]
100+
tspan = (0.0, 10.0)
101+
function multiplicative_noise!(du, u, p, t)
102+
x, y = u
103+
du[1] = p[5] * x
104+
return du[2] = p[6] * y
105+
end
106+
p = [1.5, 1.0, 3.0, 1.0, 0.1, 0.1]
107+
108+
function lotka_volterra!(du, u, p, t)
109+
x, y = u
110+
α, β, γ, δ = p
111+
du[1] = dx = α * x - β * x * y
112+
return du[2] = dy = δ * x * y - γ * y
113+
end
114+
115+
prob_sde = SDEProblem(lotka_volterra!, multiplicative_noise!, u0, tspan, p)
116+
117+
ensembleprob = EnsembleProblem(prob_sde)
118+
data = solve(ensembleprob, SOSRI(); saveat=0.1, trajectories=1000)
119+
plot(EnsembleSummary(data))
120+
```
121+
122+
```{julia}
123+
@model function fitlv_sde(data, prob)
124+
# Prior distributions.
125+
σ ~ InverseGamma(2, 3)
126+
α ~ truncated(Normal(1.3, 0.5); lower=0.5, upper=2.5)
127+
β ~ truncated(Normal(1.2, 0.25); lower=0.5, upper=2)
128+
γ ~ truncated(Normal(3.2, 0.25); lower=2.2, upper=4)
129+
δ ~ truncated(Normal(1.2, 0.25); lower=0.5, upper=2)
130+
ϕ1 ~ truncated(Normal(0.12, 0.3); lower=0.05, upper=0.25)
131+
ϕ2 ~ truncated(Normal(0.12, 0.3); lower=0.05, upper=0.25)
132+
133+
# Simulate stochastic Lotka-Volterra model.
134+
p = [α, β, γ, δ, ϕ1, ϕ2]
135+
predicted = solve(prob, SOSRI(); p=p, saveat=0.1)
136+
137+
# Early exit if simulation could not be computed successfully.
138+
if predicted.retcode !== :Success
139+
Turing.@addlogprob! -Inf
140+
return nothing
141+
end
142+
143+
# Observations.
144+
for i in 1:length(predicted)
145+
data[:, i] ~ MvNormal(predicted[i], σ^2 * I)
146+
end
147+
148+
return nothing
149+
end;
150+
```
151+
152+
The probabilistic nature of the SDE solution makes the likelihood function noisy which poses a challenge for NUTS since the gradient is changing with every calculation.
153+
Therefore we use NUTS with a low target acceptance rate of `0.25` and specify a set of initial parameters.
154+
SGHMC might be a more suitable algorithm to be used here.
155+
156+
```{julia}
157+
model_sde = fitlv_sde(odedata, prob_sde)
158+
159+
setadbackend(:forwarddiff)
160+
chain_sde = sample(
161+
model_sde,
162+
NUTS(0.25),
163+
5000;
164+
initial_params=[1.5, 1.3, 1.2, 2.7, 1.2, 0.12, 0.12],
165+
progress=false,
166+
)
167+
plot(chain_sde)
168+
```

0 commit comments

Comments
 (0)
Please sign in to comment.