library('nimbleSMC')
Building Particle Filters and Particle MCMC in NIMBLE
An Example of Using nimble
’s Particle Filtering Algorithms
This example shows how to construct and conduct inference on a state space model using particle filtering algorithms. nimble
currently has versions of the bootstrap filter, the auxiliary particle filter, the ensemble Kalman filter, and the Liu and West filter implemented. Additionally, particle MCMC samplers are available and can be specified for both univariate and multivariate parameters.
Model Creation
Assume \(x_{i}\) is the latent state and \(y_{i}\) is the observation at time \(i\) for \(i=1,\ldots,t\). We define our state space model as
\[x_{i} \sim N(a \cdot x_{i-1} + b, \sigma_{PN})\] \[y_{i} \sim t_{5}(x_{i}, \sigma_{OE})\]
with initial states
\[x_{1} \sim N\left(\frac{b}{1-a}, \frac{\sigma_{PN}}{\sqrt{1-a^2}}\right)\] \[y_{1} \sim t_{5}(x_{1}, \sigma_{OE})\]
and prior distributions
\[a \sim \text{Unif}(-0.999, 0.999)\] \[b \sim N(0, 1000)\]
where \(N(\mu, \sigma)\) denotes a normal distribution with mean \(\mu\) and standard deviation \(\sigma\), and \(t_{\nu}(\mu, \sigma)\) is a shifted, scaled \(t\)-distribution with center parameter \(\mu\), scale parameter \(\sigma\), and \(\nu\) degrees of freedom.
We specify and build our state space model below, using \(t=10\) time points:
set.seed(1)
## define the model
<- nimbleCode({
stateSpaceCode ~ dunif(-0.9999, 0.9999)
a ~ dnorm(0, sd = 1000)
b ~ dunif(1e-04, 1)
sigPN ~ dunif(1e-04, 1)
sigOE 1] ~ dnorm(b/(1 - a), sd = sigPN/sqrt((1-a*a)))
x[1] ~ dt(mu = x[1], sigma = sigOE, df = 5)
y[for (i in 2:t) {
~ dnorm(a * x[i - 1] + b, sd = sigPN)
x[i] ~ dt(mu = x[i], sigma = sigOE, df = 5)
y[i]
}
})
## define data, constants, and initial values
<- list(
data y = c(0.213, 1.025, 0.314, 0.521, 0.895, 1.74, 0.078, 0.474, 0.656, 0.802)
)<- list(
constants t = 10
)<- list(
inits a = 0,
b = .5,
sigPN = .1,
sigOE = .05
)
## build the model
<- nimbleModel(stateSpaceCode,
stateSpaceModel data = data,
constants = constants,
inits = inits,
check = FALSE)
Defining model
Building model
Setting data and initial values
Running calculate on model
[Note] Any error reports that follow may simply reflect missing values in model variables.
Checking model sizes and dimensions
[Note] This model is not fully initialized. This is not an error.
To see which variables are not initialized, use model$initializeInfo().
For more information on model initialization, see help(modelInitialization).
Construct and run a bootstrap filter
We next construct a bootstrap filter to conduct inference on the latent states of our state space model. Note that the bootstrap filter, along with the auxiliary particle filter and the ensemble Kalman filter, treat the top-level parameters a, b, sigPN
, and sigOE
as fixed. Therefore, the bootstrap filter below will proceed as though a = 0
, b = .5
, sigPN = .1
, and sigOE = .05
, which are the initial values that were assigned to the top-level parameters.
The bootstrap filter takes as arguments the name of the model and the name of the latent state variable within the model. The filter can also take a control list that can be used to fine-tune the algorithm’s configuration.
## build bootstrap filter and compile model and filter
<- buildBootstrapFilter(stateSpaceModel, nodes = 'x')
bootstrapFilter <- compileNimble(stateSpaceModel, bootstrapFilter) compiledList
Compiling
[Note] This may take a minute.
[Note] Use 'showCompilerOutput = TRUE' to see C++ compilation details.
## run compiled filter with 10,000 particles.
## note that the bootstrap filter returns an estimate of the log-likelihood of the model.
$bootstrapFilter$run(10000) compiledList
[1] -28.13009
Particle filtering algorithms in nimble
store weighted samples of the filtering distribution of the latent states in the mvSamples
modelValues object. Equally weighted samples are stored in the mvEWSamples
object. By default, nimble
only stores samples from the final time point.
## extract equally weighted posterior samples of x[10] and create a histogram
<- as.matrix(compiledList$bootstrapFilter$mvEWSamples)
posteriorSamples hist(posteriorSamples)
The auxiliary particle filter and ensemble Kalman filter can be constructed and run in the same manner as the bootstrap filter.
Conduct inference on top-level parameters using particle MCMC
Particle MCMC can be used to conduct inference on the posterior distribution of both the latent states and any top-level parameters of interest in a state space model. The particle marginal Metropolis-Hastings sampler can be specified to jointly sample the a
, b
, sigPN
, and sigOE
top level parameters within nimble’s MCMC framework as follows:
## create MCMC specification for the state space model
<- configureMCMC(stateSpaceModel, nodes = NULL) stateSpaceMCMCconf
===== Monitors =====
thin = 1: a, b, sigOE, sigPN
===== Samplers =====
(no samplers assigned)
===== Comments =====
[Warning] No samplers assigned for 14 nodes, use conf$getUnsampledNodes() for node names.
## add a block pMCMC sampler for a, b, sigPN, and sigOE
$addSampler(target = c('a', 'b', 'sigPN', 'sigOE'),
stateSpaceMCMCconftype = 'RW_PF_block', control = list(latents = 'x'))
## build and compile pMCMC sampler
<- buildMCMC(stateSpaceMCMCconf)
stateSpaceMCMC <- compileNimble(stateSpaceModel, stateSpaceMCMC, resetFunctions = TRUE) compiledList
Compiling
[Note] This may take a minute.
[Note] Use 'showCompilerOutput = TRUE' to see C++ compilation details.
## run compiled sampler for 5000 iterations
$stateSpaceMCMC$run(5000) compiledList
|-------------|-------------|-------------|-------------|
|-------------------------------------------------------|
## create trace plots for each parameter
par(mfrow = c(2,2))
<- coda::as.mcmc(as.matrix(compiledList$stateSpaceMCMC$mvSamples))
posteriorSamps ::traceplot(posteriorSamps[,'a'], ylab = 'a')
coda::traceplot(posteriorSamps[,'b'], ylab = 'b')
coda::traceplot(posteriorSamps[,'sigPN'], ylab = 'sigPN')
coda::traceplot(posteriorSamps[,'sigOE'], ylab = 'sigOE') coda
The above RW_PF_block
sampler uses a multivariate normal proposal distribution to sample vectors of top-level parameters. To sample a scalar top-level parameter, use the RW_PF
sampler instead.