This function implements a Particle Marginal Metropolis-Hastings (PMMH) resample_algorithm to perform Bayesian inference in state-space models. It first runs a pilot chain to tune the proposal distribution and the number of particles for the particle filter, and then runs the main PMMH chain.
Usage
pmmh(
pf_wrapper,
y,
m,
init_fn,
transition_fn,
log_likelihood_fn,
log_priors,
pilot_init_params,
burn_in,
num_chains = 4,
obs_times = NULL,
resample_algorithm = c("SISAR", "SISR", "SIS"),
resample_fn = c("stratified", "systematic", "multinomial"),
param_transform = NULL,
tune_control = default_tune_control(),
verbose = FALSE,
return_latent_state_est = FALSE,
seed = NULL,
num_cores = 1,
...
)Arguments
- pf_wrapper
A particle filter wrapper function. See
particle_filterfor the particle filters implemented in this package.- y
A numeric vector or matrix of observations. Each row represents an observation at a time step. If observations are not equally spaced, use the
obs_timesargument.- m
An integer specifying the number of MCMC iterations for each chain.
- init_fn
A function to initialize the particles. Should take `num_particles` and return a matrix or vector of initial states. Additional model parameters can be passed via
....- transition_fn
A function for propagating particles. Should take `particles` and optionally `t`. Additional model parameters via
....- log_likelihood_fn
A function that returns the log-likelihood for each particle given the current observation, particles, and optionally `t`. Additional parameters via
....- log_priors
A list of functions for computing the log-prior of each parameter.
- pilot_init_params
A list of initial parameter values. Should be a list of length
num_chainswhere each element is a named vector of initial parameter values.- burn_in
An integer indicating the number of initial MCMC iterations to discard as burn-in.
- num_chains
An integer specifying the number of PMMH chains to run.
- obs_times
A numeric vector specifying observation time points. Must match the number of rows in
y, or defaults to1:nrow(y).- resample_algorithm
A character string specifying the resampling algorithm to use in the particle filter. Options are: #'
SIS: Sequential Importance Sampling (without resampling).
SISR: Sequential Importance Sampling with resampling at every time step.
SISAR: SIS with adaptive resampling based on the Effective Sample Size (ESS). Resampling is triggered when the ESS falls below a given threshold (default
particles / 2). Can be modified by specifying thethresholdargument (in...), see alsoparticle_filter.
- resample_fn
A string indicating the resampling method:
"stratified","systematic", or"multinomial". Default is"stratified".- param_transform
An optional character vector that specifies the transformation applied to each parameter before proposing. The proposal is made using a multivariate normal distribution on the transformed scale. Parameters are then mapped back to their original scale before evaluation. Currently supports
"log","logit", and"identity". IfNULL, the"identity"transformation is used for all parameters.- tune_control
A list of pilot tuning controls (e.g.,
pilot_m,pilot_reps). Seedefault_tune_control.- verbose
A logical value indicating whether to print information about pilot_run tuning. Defaults to
FALSE.- return_latent_state_est
A logical value indicating whether to return the latent state estimates for each time step. Defaults to
FALSE.- seed
An optional integer to set the seed for reproducibility.
- num_cores
An integer specifying the number of cores to use for parallel processing. Defaults to 1. Each chain is assigned to its own core, so the number of cores cannot exceed the number of chains (
num_chains). The progress information given to user is limited if using more than one core.- ...
Additional arguments passed to
init_fn,transition_fn, andlog_likelihood_fn.
Details
The PMMH resample_algorithm is essentially a Metropolis Hastings
algorithm, where instead of using the intractable marginal likelihood
\(p(y_{1:T}\mid \theta)\) it instead uses the estimated likelihood using
a particle filter (see particle_filter for available particle
filters). Values are
proposed using a multivariate normal distribution in the transformed space
(specified using `param_transform`).
The proposal covariance and the number of particles is chosen based on a
pilot run. The number of particles is chosen such that the variance of the
log-likelihood estimate at the estimated posterior mean is approximately 1
(with a minimum of 50 particles and a maximum of 1000).
References
Andrieu et al. (2010). Particle Markov chain Monte Carlo methods. Journal of the Royal Statistical Society: Series B (Statistical Methodology), 72(3):269–342. doi: 10.1111/j.1467-9868.2009.00736.x
Examples
init_fn <- function(num_particles) {
rnorm(num_particles, mean = 0, sd = 1)
}
transition_fn <- function(particles, phi, sigma_x) {
phi * particles + sin(particles) +
rnorm(length(particles), mean = 0, sd = sigma_x)
}
log_likelihood_fn <- function(y, particles, sigma_y) {
dnorm(y, mean = cos(particles), sd = sigma_y, log = TRUE)
}
log_prior_phi <- function(phi) {
dnorm(phi, mean = 0, sd = 1, log = TRUE)
}
log_prior_sigma_x <- function(sigma) {
dexp(sigma, rate = 1, log = TRUE)
}
log_prior_sigma_y <- function(sigma) {
dexp(sigma, rate = 1, log = TRUE)
}
log_priors <- list(
phi = log_prior_phi,
sigma_x = log_prior_sigma_x,
sigma_y = log_prior_sigma_y
)
# Generate data
t_val <- 10
x <- numeric(t_val)
y <- numeric(t_val)
phi <- 0.8
sigma_x <- 1
sigma_y <- 0.5
init_state <- rnorm(1, mean = 0, sd = 1)
x[1] <- phi * init_state + sin(init_state) + rnorm(1, mean = 0, sd = sigma_x)
y[1] <- x[1] + rnorm(1, mean = 0, sd = sigma_y)
for (t in 2:t_val) {
x[t] <- phi * x[t - 1] + sin(x[t - 1]) + rnorm(1, mean = 0, sd = sigma_x)
y[t] <- cos(x[t]) + rnorm(1, mean = 0, sd = sigma_y)
}
x <- c(init_state, x)
# Should use higher MCMC iterations in practice (m)
pmmh_result <- pmmh(
pf_wrapper = bootstrap_filter,
y = y,
m = 1000,
init_fn = init_fn,
transition_fn = transition_fn,
log_likelihood_fn = log_likelihood_fn,
log_priors = log_priors,
pilot_init_params = list(
c(phi = 0.8, sigma_x = 1, sigma_y = 0.5),
c(phi = 1, sigma_x = 0.5, sigma_y = 1)
),
burn_in = 100,
num_chains = 2,
param_transform = list(
phi = "identity",
sigma_x = "log",
sigma_y = "log"
),
tune_control = default_tune_control(pilot_m = 500, pilot_burn_in = 100)
)
#> Running chain 1...
#> Running pilot chain for tuning...
#> Using 50 particles for PMMH:
#> Running Particle MCMC chain with tuned settings...
#> Running chain 2...
#> Running pilot chain for tuning...
#> Using 50 particles for PMMH:
#> Running Particle MCMC chain with tuned settings...
#> PMMH Results Summary:
#> Parameter Mean SD Median 2.5% 97.5% ESS Rhat
#> phi 0.83 0.35 0.86 -0.24 1.19 30 1.072
#> sigma_x 0.60 0.70 0.42 0.07 2.92 47 1.034
#> sigma_y 0.55 0.17 0.52 0.27 0.95 146 1.023
#> Warning: Some ESS values are below 400, indicating poor mixing. Consider running the chains for more iterations.
#> Warning:
#> Some Rhat values are above 1.01, indicating that the chains have not converged.
#> Consider running the chains for more iterations and/or increase burn_in.
# Convergence warning is expected with such low MCMC iterations.
# Suppose we have data for t=1,2,3,5,6,7,8,9,10 (i.e., missing at t=4)
obs_times <- c(1, 2, 3, 5, 6, 7, 8, 9, 10)
y <- y[obs_times]
# Specify observation times in the pmmh using obs_times
pmmh_result <- pmmh(
pf_wrapper = bootstrap_filter,
y = y,
m = 1000,
init_fn = init_fn,
transition_fn = transition_fn,
log_likelihood_fn = log_likelihood_fn,
log_priors = log_priors,
pilot_init_params = list(
c(phi = 0.8, sigma_x = 1, sigma_y = 0.5),
c(phi = 1, sigma_x = 0.5, sigma_y = 1)
),
burn_in = 100,
num_chains = 2,
obs_times = obs_times,
param_transform = list(
phi = "identity",
sigma_x = "log",
sigma_y = "log"
),
tune_control = default_tune_control(pilot_m = 500, pilot_burn_in = 100)
)
#> Running chain 1...
#> Running pilot chain for tuning...
#> Using 50 particles for PMMH:
#> Running Particle MCMC chain with tuned settings...
#> Running chain 2...
#> Running pilot chain for tuning...
#> Using 50 particles for PMMH:
#> Running Particle MCMC chain with tuned settings...
#> PMMH Results Summary:
#> Parameter Mean SD Median 2.5% 97.5% ESS Rhat
#> phi 0.83 0.47 0.86 -0.74 1.77 40 1.040
#> sigma_x 0.54 0.71 0.31 0.01 2.83 56 1.065
#> sigma_y 0.62 0.27 0.54 0.31 1.45 92 1.008
#> Warning: Some ESS values are below 400, indicating poor mixing. Consider running the chains for more iterations.
#> Warning:
#> Some Rhat values are above 1.01, indicating that the chains have not converged.
#> Consider running the chains for more iterations and/or increase burn_in.
