update(S) <- S - n_SI
update(I) <- I + n_SI - n_IR
update(R) <- R + n_IR
update(incidence) <- incidence + n_SI
initial(S) <- N - I0
initial(I) <- I0
initial(R) <- 0
initial(incidence, zero_every = 1) <- 0
p_SI <- 1 - exp(-beta * I / N * dt)
p_IR <- 1 - exp(-gamma * dt)
n_SI <- Binomial(S, p_SI)
n_IR <- Binomial(I, p_IR)
N <- parameter(1000)
I0 <- parameter(10)
beta <- parameter(0.2)
gamma <- parameter(0.1)
We need:
You should download incidence.csv
We will fit cases
here to incidence
in our model.
Run the system with beta = 0.4
and gamma = 0.2
\[ \Pr(\mathrm{data} | \mathrm{model}) \]
perhaps:
\[ \Pr(\mathrm{observed\ cases}) \sim \mathrm{Poisson}(\mathrm{modelled\ cases}) \]
update(S) <- S - n_SI
update(I) <- I + n_SI - n_IR
update(R) <- R + n_IR
update(incidence) <- incidence + n_SI
initial(S) <- N - I0
initial(I) <- I0
initial(R) <- 0
initial(incidence, zero_every = 1) <- 0
p_SI <- 1 - exp(-beta * I / N * dt)
p_IR <- 1 - exp(-gamma * dt)
n_SI <- Binomial(S, p_SI)
n_IR <- Binomial(I, p_IR)
N <- parameter(1000)
I0 <- parameter(10)
beta <- parameter(0.2)
gamma <- parameter(0.1)
cases <- data()
cases ~ Poisson(incidence)
dust_likelihood_run(filter, list(beta = 0.4, gamma = 0.2),
save_trajectories = TRUE, index_state = idx)
#> [1] -115.1948
y <- dust_likelihood_last_trajectories(filter)
matplot(data$time, t(drop(y)), type = "l", col = "#00000033", lty = 1,
xlab = "Time", ylab = "Incidence")
points(data, pch = 19, col = "red")
(aka, what is really going on here?)
AKA, the particle filter
Assuming a given \(\theta\), at each time step \(t\), BSSMC:
Allow to explores efficiently the state space by progressively integrating the data points
Produces a MC approximation of \(p(Y_{1:T}|\theta)\) the marginal likelihood
dust2
monty
mcstate
We have a marginal likelihood estimator from our particle filter:
How do we sample from beta
and gamma
?
We need:
beta
and gamma
, pars
pars
pars
(and back again)Our solution, “packers”
packer <- monty_packer(c("beta", "gamma"))
packer
#>
#> ── <monty_packer> ──────────────────────────────────────────────────────────────
#> ℹ Packing 2 parameters: 'beta' and 'gamma'
#> ℹ Use '$pack()' to convert from a list to a vector
#> ℹ Use '$unpack()' to convert from a vector to a list
#> ℹ See `?monty_packer()` for more information
We can transform from \(\theta\) to a named list:
Bind additional data
Cope with vector-valued parameters in \(\theta\)
packer <- monty_packer(array = c(beta = 3, gamma = 3))
packer
#>
#> ── <monty_packer> ──────────────────────────────────────────────────────────────
#> ℹ Packing 6 parameters: 'beta[1]', 'beta[2]', 'beta[3]', 'gamma[1]', 'gamma[2]', and 'gamma[3]'
#> ℹ Use '$pack()' to convert from a list to a vector
#> ℹ Use '$unpack()' to convert from a vector to a list
#> ℹ See `?monty_packer()` for more information
packer$unpack(c(0.2, 0.21, 0.22, 0.1, 0.11, 0.12))
#> $beta
#> [1] 0.20 0.21 0.22
#>
#> $gamma
#> [1] 0.10 0.11 0.12
Another DSL, similar to odin’s:
prior <- monty_dsl({
beta ~ Exponential(mean = 0.5)
gamma ~ Exponential(mean = 0.3)
})
prior
#>
#> ── <monty_model> ───────────────────────────────────────────────────────────────
#> ℹ Model has 2 parameters: 'beta' and 'gamma'
#> ℹ This model:
#> • can compute gradients
#> • can be directly sampled from
#> • accepts multiple parameters
#> ℹ See `?monty_model()` for more information
This is a “monty model”
compute this density manually:
Combine a filter and a packer
packer <- monty_packer(c("beta", "gamma"))
likelihood <- dust_likelihood_monty(filter, packer)
likelihood
#>
#> ── <monty_model> ───────────────────────────────────────────────────────────────
#> ℹ Model has 2 parameters: 'beta' and 'gamma'
#> ℹ This model:
#> • is stochastic
#> ℹ See `?monty_model()` for more information
Combine a likelihood and a prior to make a posterior
\[ \underbrace{\Pr(\theta | \mathrm{data})}_{\mathrm{posterior}} \propto \underbrace{\Pr(\mathrm{data} | \theta)}_\mathrm{likelihood} \times \underbrace{P(\theta)}_{\mathrm{prior}} \]
(remember that addition is multiplication on a log scale)
A diagonal variance-covariance matrix (uncorrelated parameters)
Use this to create a “random walk” sampler:
samples <- monty_sample(posterior, sampler, 1000, n_chains = 3)
samples
#>
#> ── <monty_samples: 2 parameters x 1000 samples x 3 chains> ─────────────────────
#> ℹ Parameters: 'beta' and 'gamma'
#> ℹ Conversion to other types is possible:
#> → ! posterior::as_draws_array() [package installed, but not loaded]
#> → ! posterior::as_draws_df() [package installed, but not loaded]
#> → ! coda::as.mcmc.list() [package installed, but not loaded]
#> ℹ See `?monty_sample()` and `vignette("samples")` for more information
Diagnostics can be used from the posterior
package
## Note: as_draws_df converts samples$pars, and drops anything else in samples
samples_df <- posterior::as_draws_df(samples)
posterior::summarise_draws(samples_df)
#> # A tibble: 2 × 10
#> variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 beta 0.930 0.925 0.131 0.114 0.736 1.13 1.06 93.1 77.7
#> 2 gamma 0.848 0.830 0.212 0.188 0.577 1.22 1.07 83.2 90.1
You can use the posterior
package in conjunction with bayesplot
(and then also ggplot2
)
samples_df <- posterior::as_draws_df(samples)
posterior::summarise_draws(samples_df)
#> # A tibble: 2 × 10
#> variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 beta 0.916 0.909 0.114 0.114 0.744 1.12 1.01 351. 607.
#> 2 gamma 0.810 0.786 0.184 0.172 0.561 1.15 1.02 270. 440.
Two places to parallelise
e.g., 4 threads per filter x 2 workers = 8 total cores in use
Use the n_threads
argument, here for 4 threads
requires that you have OpenMP; this is very annoying on macOS
Use monty_runner_callr
, here for 2 workers
Pass runner
through to monty_sample
:
(sorry, this is broken unless your model is in a package!)
Then queue these up on a cluster, e.g., using hipercow
:
And retrieve the result
(sorry, also broken unless your model is in a package!)
This is useful because most of your chain is not interesting due to the autocorrelation.
While running
After running