For reference, this is the full workflow:
using Catalyst
sir = @reaction_network begin
γ, S + I --> 2I
ν, I --> R
end
using OrdinaryDiffEqDefault, Plots
u0 = [:S => 999.0, :I => 1.0, :R => 0.0]
p_true = [:γ => 0.0005, :ν => 0.1]
oprob_true = ODEProblem(sir, u0, 100.0, p_true)
sol_true = solve(oprob_true)
plot(sol_true; label = ["S (true)" "I (true)" "R (true)"], lw = 4)
using Distributions
σI = 10.0
t_measurement = 1.0:2:100.0
I_observed = sol_true(t_measurement; idxs = :I)
I_observed = [rand(Normal(I, σI)) for I in I_observed]
plot!(t_measurement, I_observed; label = "I (measured)", color = 2)
using Turing, SymbolicIndexingInterface
setp_oop = SymbolicIndexingInterface.setp_oop(oprob_true, [:γ, :ν])
@model function sir_likelihood(I_observed, oprob_base, setp_oop, saveat)
# Defines the parameters we wish to estimate and their prior distributions.
γ ~ LogUniform(0.00001, 0.001)
ν ~ LogUniform(0.01, 1.0)
σI ~ LogUniform(0.1, 100.0)
# Simulate the model for parameter values γ, ν. Saves the solution at the measurement times.
p = setp_oop(oprob_base, [γ, ν])
oprob_base = remake(oprob_base; p)
sol = solve(oprob_base; saveat, verbose = false, maxiters = 10000)
# If simulation was unsuccessful, the likelihood is -Inf.
if !SciMLBase.successful_retcode(sol)
Turing.@addlogprob! -Inf
return nothing
end
# Computes the likelihood of the observations.
for idx in eachindex(I_observed)
I_observed[idx] ~ Normal(sol[:I][idx], σI)
end
end
n_steps = 1000
n_chains = 4
sir_model = sir_likelihood(I_observed, oprob_true, setp_oop, t_measurement)
chain = sample(sir_model, NUTS(), MCMCThreads(), n_steps, n_chains; progress = false)
The result should be a 3d posterior over the estimated parameters γ, ν, and σI, respectively(?). Imagine there is a correlation between γ and ν in this distribution. In this case, wouldn’t sampling a value of γ and one of ν independently yield a different result from sampling a single point in my posterior distribution and extracting its γ and ν values? I.e. there there are two different interpretations of the sampled point
first.(sample.((chain[:γ], chain[:ν]), 1))
where I am uncertain which of them it is.