Author: Frédéric Gosselin
Date: 2026-03-07

# Variations in using runMCMCbtadjust with Nimble: samplers

#### Frédéric Gosselin

#### 2026-03-07

# Introduction

This file is meant to present the possibilities of the function `runMCMC_btadjust` in the `runMCMCbtadjust` package when using Bayesian models written with the `Nimble` language, and the capabilities of the packages `nimble`, `nimbleHMC` and `nimbleAPT` in terms of MCMC samplers. The aim of the `runMCMC_btadjust` function is to run a Markov Chain Monte Carlo (MCMC) for a specified Bayesian model while adapting automatically the burn-in and thinning parameters to meet pre-specified targets in terms of MCMC convergence and number of effective values of MCMC outputs - where the term “number of effective values” for the MCMC outputs refers to sample size adjusted for autocorrelation. This is done in only one call to the function that repeatedly calls the MCMC until criteria for convergence and number of effective values are met. This allows to obtain a MCMC output that is out of the transient phase of the MCMC (convergence) and that contains a pre-specified number of nearly independent draws from the posterior distribution (number of effective values).

This function has four main advantages: (i) it saves the analyst’s programming time since he/she does not have to repeatedly diagnose and re-run MCMCs until desired levels of convergence and number of effective values are reached; (ii) it allows a minimal, normalized quality control of MCMC outputs by allowing to meet pre-specified levels in terms of convergence and number of quasi-independent values; (iii) it may save computer’s time when compared to cases where we have to restart the MCMC from the beginning if it has not converged or reached the specified number of effective values (as e.g. with `runMCMC` function in `NIMBLE`); and (iv) it can be applied with different MCMC R languages, with a stronger integration with `NIMBLE`.

We will here restrict our attention on the `NIMBLE` language and show a first axis of strong integration with `NIMBLE`. Indeed, the last versions of the package `nimble` provide a still improved flexibility for the user, especially in terms of MCMC samplers since `nimble` allows the user to choose the MCMC sampler parameter by parameter, which is one of its great strength. We will demonstrate the way we can use these possibilities on a very simple, yet problematic statistical model. The simulated data we wish to model correspond to a simple linear model with a strongly uncentered explanatory variable - a situation which is known to pose problems with classical MCMCs due top strong correlation of the Intercept and slope parameters.

    set.seed(1)
    nobs<-1000
    x<-rnorm(nobs)+100
    y<-y1000<-rnorm(n=length(x),mean=x,sd=1)

We will analyse these data with the same likelihood function than the one used to generate the data and rather non-informative priors, and with data and initial values that of course include the explanatory variable and its associated slope.

    library(runMCMCbtadjust)
    library(nimble)
    library(parallel)
    library(coda)

    ModelData <-list(y = y)
    ModelConsts <- list(x=x, nobs = length(y))

     ModelCode<-nimbleCode(
      {
        # Priors
        Intercept ~ dnorm(0,sd=100)
        Slope ~ dnorm(0,sd=100)
        population.sd ~ dunif(0,100)

        # Normal distribution parameterized by precision = 1/variance in Nimble
        population.variance <- population.sd * population.sd
        precision <- 1 / population.variance

        # Likelihood
        for(i in 1:nobs){
          meany[i]<-Intercept+Slope*x[i]
          y[i] ~ dnorm(meany[i], precision)
        }
      })

     ModelInits <- function()
    {list (Intercept = rnorm(1,0,1), Slope = rnorm(1,0,1), population.sd = runif(1, 1, 30))}

    ### put here to pass CRAN tests: https://stackoverflow.com/questions/41307178/error-processing-vignette-failed-with-diagnostics-4-simultaneous-processes-spa
    options(mc.cores=2)

    ### adapted the number of chains for the same reason
    Nchains <- 2

    set.seed(1)
    Inits<-lapply(1:Nchains,function(x){ModelInits()})

    #specifying the names of parameters to analyse and save:
    params <- c("Intercept", "Slope", "population.sd")

# Default Nimble samplers

We now fit this model with the `runMCMC_btadjust` function. As we infer there will be some convergence issues we restrict the duration of the fit to two minutes (or 120 seconds with parameter `time.max`). We will keep this restriction for all the attempts we will make in this vignette. This is a source of non reproducibility of results since the result depends on the speed of the computer - so that the reader should not be too surprised if he/she finds other results than those commented hereafter. In this specific case we also turn to FALSE `print.thinmult` as it would otherwise imply the printing of a much too long output.

    out.mcmc.base<-runMCMC_btadjust(code=ModelCode, constants = ModelConsts, data = ModelData, MCMC_language="Nimble",
        Nchains=Nchains, params=params, inits=Inits,
        niter.min=1000, niter.max=Inf,
        nburnin.min=100, nburnin.max=Inf,
        thin.min=1, thin.max=Inf,
        conv.max=1.05, neff.min=1000,
        control=list(neff.method="Coda", time.max=120, print.diagnostics=FALSE,print.thinmult=FALSE),
        control.MCMC=list(parallelize=TRUE, parallelizeInitExpr= expression(if(MCMC_language=="Nimble"){library(nimble);if(control.MCMC$APT) {library(nimbleAPT)}} else {NULL}),n.adapt=1000))
    #> [1] "control$seed is NULL. Replaced by 1"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "Number of planned new iterations non-positive: end of MCMC cycles"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Main MCMC sampling finished."
    #> [1] "###################################################################################"
    #> Warning in system.time({: The MCMC did not converge
    #> Warning in system.time({: The expected effective sample size was not reached
    #> [1] "###################################################################################"
    #> [1] "MCMC has NOT reached the required level of convergence."
    #> [1] "###################################################################################"
    #> [1] "MCMC has NOT reached the required level of effective values."
    #> [1] "###################################################################################"

The model finally did not converge and did not reach its required level of number of effective values.

# Diagnosing correlation among parameters

We now use the built-in `findMCMC_strong_corrs` function to identify where the model has problems in terms of autocorrelation.

    knitr::kable(findMCMC_strong_corrs(out.mcmc.base),align="r",caption=paste0("Pairs of parameters that are strongly correlated"))

Pairs of parameters that are strongly correlated
dimnames(Table)[[1]][Temp[, 1]] | dimnames(Table)[[1]][Temp[, 2]] | Table[Temp]
--- | --- | ---
Slope | Intercept | -0.9999592
Intercept | Slope | -0.9999592

As expected, there is a very strong negative correlation between `Intercept` and `Slope` - this case was indeed built for this.

# Block samplers

We will therefore now try to fit the same model but with a sampler that allows parameters Intercept and Slope to be correlated. Indeed, so far, by default, Nimble used independent samplers for each of the parameters. We will now use a “RW_block” (Random walk block) sampler for `Intercept` and `Slope` to be updated together with a correlated update. This will be done through the component `confModel.expression.toadd` added in the parameter `control.MCMC` of the `runMCMC_btadjust` function. As its name suggests, it will be an expression to add in the step of model configuration for `NIMBLE` - with the `NIMBLE` function `configureMCMC` . The syntax is given below:

    sampler.expression.toadd<-expression(
      {ConfModel[[i]]$removeSamplers(c("Intercept","Slope"))
      ConfModel[[i]]$addSampler(target = c("Intercept","Slope"),type = "RW_block")}  )

    out.mcmc.RWblock<-runMCMC_btadjust(code=ModelCode, constants = ModelConsts, data = ModelData, MCMC_language="Nimble",
        Nchains=Nchains, params=params, inits=Inits,
        niter.min=1000, niter.max=Inf,
        nburnin.min=100, nburnin.max=Inf,
        thin.min=1, thin.max=Inf,
        conv.max=1.05, neff.min=1000,
        control=list(neff.method="Coda", time.max=120, print.diagnostics=FALSE),
        control.MCMC=list(confModel.expression.toadd=sampler.expression.toadd, parallelize=TRUE, parallelizeInitExpr= expression(if(MCMC_language=="Nimble"){library(nimble);if(control.MCMC$APT) {library(nimbleAPT)}} else {NULL}), n.adapt=1000))
    #> [1] "control$seed is NULL. Replaced by 1"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "Number of planned new iterations non-positive: end of MCMC cycles"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Main MCMC sampling finished."
    #> [1] "###################################################################################"
    #> Warning in system.time({: The MCMC did not converge
    #> Warning in system.time({: The expected effective sample size was not reached
    #> [1] "###################################################################################"
    #> [1] "MCMC has NOT reached the required level of convergence."
    #> [1] "###################################################################################"
    #> [1] "MCMC has NOT reached the required level of effective values."
    #> [1] "###################################################################################"

In this case, we even do not reach convergence. This is confirmed by the traceplot of the Slope parameter (not shown here due to problems with CRAN; you can run the code in the FALSE condition below to see it).

    if (TRUE) {traceplot(out.mcmc.RWblock[,"Slope"],main="Traceplot of Slope")}

plot of chunk traceplot with Nimble second run, with RW_block

Indeed, the two trajectories are completely separate showing a complete lack of convergence. Such is also the case for the Intercept parameter, but not for population.sd. This means that the Random Walk block sampler did even worse than the original samplers. This is very likely due to a note/warning issued by Nimble but which we did not see due to the parallelization, that we can read without it. It reads:

    Assigning an RW_block sampler to nodes with very different scales can result in low MCMC efficiency.  If all nodes assigned to RW_block are not on a similar scale, we recommend providing an informed value for the "propCov" control list argument, or using the AFSS sampler instead.

We will actually try the second alternative: use the AF slice sampler: this is done in what follows:

    sampler.expression.toadd<-expression(
      {ConfModel[[i]]$removeSamplers(c("Intercept","Slope"))
      ConfModel[[i]]$addSampler(target = c("Intercept","Slope"),type = "AF_slice")}  )

    out.mcmc.AFslice<-runMCMC_btadjust(code=ModelCode, constants = ModelConsts, data = ModelData, MCMC_language="Nimble",
        Nchains=Nchains, params=params, inits=Inits,
        niter.min=1000, niter.max=Inf,
        nburnin.min=100, nburnin.max=Inf,
        thin.min=1, thin.max=Inf,
        conv.max=1.05, neff.min=1000,
        control=list(neff.method="Coda", time.max=120, print.diagnostics=FALSE),
        control.MCMC=list(confModel.expression.toadd=sampler.expression.toadd, parallelize=TRUE, parallelizeInitExpr= expression(if(MCMC_language=="Nimble"){library(nimble);if(control.MCMC$APT) {library(nimbleAPT)}} else {NULL}), n.adapt=1000))
    #> [1] "control$seed is NULL. Replaced by 1"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  8.632"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  9 :"
    #> [1] "Testing multiplier of thin:  8 :"
    #> [1] "Testing multiplier of thin:  7 :"
    #> [1] "Testing multiplier of thin:  6 :"
    #> [1] "Testing multiplier of thin:  5 :"
    #> [1] "Testing multiplier of thin:  4 :"
    #> [1] "Testing multiplier of thin:  3 :"
    #> [1] "Testing multiplier of thin:  2 :"
    #> [1] "Retained multiplier of thin:  2 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Main MCMC sampling finished."
    #> [1] "###################################################################################"
    #> [1] "Final max raw multiplier of thin:  2.616"
    #> [1] "###################################################################################"
    #> [1] "Testing final multiplier of thin:  3 :"
    #> [1] "Retained final multiplier of thin:  3"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "MCMC has reached the required level of convergence."
    #> [1] "###################################################################################"
    #> [1] "MCMC has reached the required level of effective values."
    #> [1] "###################################################################################"

This now works very fine with both convergence and reaching the required number of effective values. The estimates are also fine with the true parameters we know, since the true values are within the credibility intervals:

    summary(out.mcmc.AFslice)
    #>
    #> Iterations = 101:5525
    #> Thinning interval = 6
    #> Number of chains = 2
    #> Sample size per chain = 905
    #>
    #> 1. Empirical mean and standard deviation for each variable,
    #>    plus standard error of the mean:
    #>
    #>                 Mean      SD  Naive SE Time-series SE
    #> Intercept     0.0701 3.27826 0.0770556      0.0812963
    #> Slope         0.9994 0.03276 0.0007701      0.0008128
    #> population.sd 1.0295 0.02336 0.0005491      0.0006255
    #>
    #> 2. Quantiles for each variable:
    #>
    #>                  2.5%     25%     50%   75% 97.5%
    #> Intercept     -6.1550 -2.1738 0.08901 2.264 6.636
    #> Slope          0.9340  0.9775 0.99940 1.022 1.062
    #> population.sd  0.9841  1.0139 1.02830 1.046 1.076

In line with the above, I have recently shifted my practice of block sampling to AF_slice sampler which in my experience behaves nicely in such cases of correlated parameters.

# Hamiltonian Monte Carlo sampler

We will in the sequel try two other possibilities provided by Nimble. The first one is to use an Hamiltonian sampler - similar in principle to the one provided by `STAN` - which is a priori well suited to such badly behaving cases. This will require rather numerous steps to do so. First, this will require the loading of the new `nimbleHMC` library as well as modifications of `nimbleOptions` in the component `parallelizeInitExpr` of parameter `control.MCMC`; this is done in what follows in the variable `newParallelizeInitExpr`. Second, we need to refer to the HMC sampler in the `confModel.expression.toadd` component. Third, we need to turn to TRUE the `buildDerivs` component of parameter `control.MCMC`. This actually means that the log posterior density should be derivable relative to all the parameters to use this sampler - or at least relative to the parameters on which this sampler is applied. This is what we now perform:

    newParallelizeInitExpr<-expression({
          library(nimble)
          library(nimbleHMC)
          nimbleOptions(MCMCusePredictiveDependenciesInCalculations = TRUE)
          nimbleOptions(MCMCorderPosteriorPredictiveSamplersLast = FALSE)
          nimbleOptions(enableDerivs = TRUE)
          })

    sampler.expression.toadd<-expression(
      {ConfModel[[i]]$removeSamplers(c("Intercept","Slope"))
       nimbleHMC::addHMC(ConfModel[[i]], target=c("Intercept","Slope"))
      }  )

    out.mcmc.HMC<-runMCMC_btadjust(code=ModelCode, constants = ModelConsts, data = ModelData, MCMC_language="Nimble",
        Nchains=Nchains, params=params, inits=Inits,
        niter.min=1000, niter.max=Inf,
        nburnin.min=100, nburnin.max=Inf,
        thin.min=1, thin.max=Inf,
        conv.max=1.05, neff.min=1000,
        control=list(neff.method="Coda", time.max=120, print.diagnostics=FALSE),
        control.MCMC=list(confModel.expression.toadd=sampler.expression.toadd, parallelizeInitExpr= newParallelizeInitExpr,buildDerivs=TRUE, parallelize=TRUE,parallelizeInitExpr= newParallelizeInitExpr, n.adapt=1000))
    #> [1] "control$seed is NULL. Replaced by 1"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  6.885"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  7 :"
    #> [1] "Testing multiplier of thin:  6 :"
    #> [1] "Testing multiplier of thin:  5 :"
    #> [1] "Testing multiplier of thin:  4 :"
    #> [1] "Testing multiplier of thin:  3 :"
    #> [1] "Testing multiplier of thin:  2 :"
    #> [1] "Retained multiplier of thin:  1 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  8.267"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  8 :"
    #> [1] "Testing multiplier of thin:  7 :"
    #> [1] "Testing multiplier of thin:  6 :"
    #> [1] "Testing multiplier of thin:  5 :"
    #> [1] "Testing multiplier of thin:  4 :"
    #> [1] "Testing multiplier of thin:  3 :"
    #> [1] "Testing multiplier of thin:  2 :"
    #> [1] "Retained multiplier of thin:  2 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "Number of planned new iterations non-positive: end of MCMC cycles"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Main MCMC sampling finished."
    #> [1] "###################################################################################"
    #> [1] "Final max raw multiplier of thin:  8.267"
    #> [1] "###################################################################################"
    #> [1] "Testing final multiplier of thin:  8 :"
    #> [1] "Testing final multiplier of thin:  7 :"
    #> [1] "Testing final multiplier of thin:  6 :"
    #> [1] "Testing final multiplier of thin:  5 :"
    #> [1] "Testing final multiplier of thin:  4 :"
    #> [1] "Testing final multiplier of thin:  3 :"
    #> [1] "Testing final multiplier of thin:  2 :"
    #> [1] "Retained final multiplier of thin:  1"
    #> [1] "###################################################################################"
    #> Warning in system.time({: The expected effective sample size was not reached
    #> [1] "###################################################################################"
    #> [1] "MCMC has reached the required level of convergence."
    #> [1] "###################################################################################"
    #> [1] "MCMC has NOT reached the required level of effective values."
    #> [1] "###################################################################################"

With these adaptations, within two minutes, the Hamiltonian Monte Carlo sampler does not go very far; it converges but does not reach the required number of effective values. This is due to a longer time per iteration and a longer time of MCMC preparation than with other samplers. The associated summary is given below:

    summary(out.mcmc.HMC)
    #>
    #> Iterations = 352:1415
    #> Thinning interval = 1
    #> Number of chains = 2
    #> Sample size per chain = 1064
    #>
    #> 1. Empirical mean and standard deviation for each variable,
    #>    plus standard error of the mean:
    #>
    #>                  Mean      SD  Naive SE Time-series SE
    #> Intercept     -0.2073 3.27153 0.0709194       0.212671
    #> Slope          1.0022 0.03270 0.0007089       0.002124
    #> population.sd  1.0283 0.02279 0.0004940       0.001144
    #>
    #> 2. Quantiles for each variable:
    #>
    #>                  2.5%     25%     50%   75% 97.5%
    #> Intercept     -6.3483 -2.4632 -0.3973 1.859 6.676
    #> Slope          0.9333  0.9817  1.0039 1.025 1.063
    #> population.sd  0.9870  1.0121  1.0279 1.044 1.073

    effectiveSize(out.mcmc.HMC)
    #>     Intercept         Slope population.sd
    #>      257.4119      257.7739      410.8059

# Adaptive parallel tempering

Our final try in terms of Nimble samplers will be the Adaptive Parallel Tempering (Miasojedow et al. (2013)) provided by the `nimbleAPT` library. This is also a type of sampler that can help adapt tricky situations, especially multiple local maxima (or near-maxima) of the log posterior density. This is however unsure turning to APT by itself will be able to treat the correlation issue we have to deal with. The code to turn to APT is rather simple - and here we do not have to change the sampler as it is already included in the component `APT=TRUE` of `control.MCMC` . An important characteristic of `nimbleAPT` is that samplers for all parameters should be within the APT family: it is not possible at present to mix APT and non-APT samplers for different parameters - we initially wished to remove the APT sampler from parameter `population.sd` and replace it by the traditional random walk one to be more comparable to the ones above which also kept the random walk sampler for `population.sd` but this turned out to be impossible.

    out.mcmc.APT<-runMCMC_btadjust(code=ModelCode, constants = ModelConsts, data = ModelData, MCMC_language="Nimble",
        Nchains=Nchains, params=params, inits=Inits,
        niter.min=1000, niter.max=Inf,
        nburnin.min=100, nburnin.max=Inf,
        thin.min=1, thin.max=Inf,
        conv.max=1.05, neff.min=1000,
        control=list(neff.method="Coda", time.max=120, print.diagnostics=FALSE),
        control.MCMC=list(APT=TRUE, parallelize=TRUE, parallelizeInitExpr= expression(if(MCMC_language=="Nimble"){library(nimble);if(control.MCMC$APT) {library(nimbleAPT)}} else {NULL}), n.adapt=1000))
    #> [1] "control$seed is NULL. Replaced by 1"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  315.449"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  315 :"
    #> [1] "Testing multiplier of thin:  252 :"
    #> [1] "Testing multiplier of thin:  201 :"
    #> [1] "Testing multiplier of thin:  160 :"
    #> [1] "Testing multiplier of thin:  128 :"
    #> [1] "Testing multiplier of thin:  102 :"
    #> [1] "Testing multiplier of thin:  81 :"
    #> [1] "Testing multiplier of thin:  64 :"
    #> [1] "Testing multiplier of thin:  51 :"
    #> [1] "Testing multiplier of thin:  40 :"
    #> [1] "Testing multiplier of thin:  32 :"
    #> [1] "Retained multiplier of thin:  32 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  65.107"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  65 :"
    #> [1] "Testing multiplier of thin:  52 :"
    #> [1] "Testing multiplier of thin:  41 :"
    #> [1] "Testing multiplier of thin:  32 :"
    #> [1] "Testing multiplier of thin:  25 :"
    #> [1] "Testing multiplier of thin:  20 :"
    #> [1] "Testing multiplier of thin:  19 :"
    #> [1] "Testing multiplier of thin:  18 :"
    #> [1] "Testing multiplier of thin:  17 :"
    #> [1] "Testing multiplier of thin:  16 :"
    #> [1] "Testing multiplier of thin:  15 :"
    #> [1] "Testing multiplier of thin:  14 :"
    #> [1] "Testing multiplier of thin:  13 :"
    #> [1] "Testing multiplier of thin:  12 :"
    #> [1] "Testing multiplier of thin:  11 :"
    #> [1] "Testing multiplier of thin:  10 :"
    #> [1] "Testing multiplier of thin:  9 :"
    #> [1] "Testing multiplier of thin:  8 :"
    #> [1] "Testing multiplier of thin:  7 :"
    #> [1] "Testing multiplier of thin:  6 :"
    #> [1] "Testing multiplier of thin:  5 :"
    #> [1] "Testing multiplier of thin:  4 :"
    #> [1] "Testing multiplier of thin:  3 :"
    #> [1] "Testing multiplier of thin:  2 :"
    #> [1] "Retained multiplier of thin:  2 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  27.292"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  27 :"
    #> [1] "Testing multiplier of thin:  21 :"
    #> [1] "Testing multiplier of thin:  20 :"
    #> [1] "Testing multiplier of thin:  19 :"
    #> [1] "Testing multiplier of thin:  18 :"
    #> [1] "Testing multiplier of thin:  17 :"
    #> [1] "Testing multiplier of thin:  16 :"
    #> [1] "Testing multiplier of thin:  15 :"
    #> [1] "Testing multiplier of thin:  14 :"
    #> [1] "Testing multiplier of thin:  13 :"
    #> [1] "Testing multiplier of thin:  12 :"
    #> [1] "Testing multiplier of thin:  11 :"
    #> [1] "Testing multiplier of thin:  10 :"
    #> [1] "Testing multiplier of thin:  9 :"
    #> [1] "Testing multiplier of thin:  8 :"
    #> [1] "Testing multiplier of thin:  7 :"
    #> [1] "Testing multiplier of thin:  6 :"
    #> [1] "Testing multiplier of thin:  5 :"
    #> [1] "Testing multiplier of thin:  4 :"
    #> [1] "Testing multiplier of thin:  3 :"
    #> [1] "Testing multiplier of thin:  2 :"
    #> [1] "Retained multiplier of thin:  1 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  32.162"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  32 :"
    #> [1] "Testing multiplier of thin:  25 :"
    #> [1] "Testing multiplier of thin:  20 :"
    #> [1] "Testing multiplier of thin:  19 :"
    #> [1] "Testing multiplier of thin:  18 :"
    #> [1] "Testing multiplier of thin:  17 :"
    #> [1] "Testing multiplier of thin:  16 :"
    #> [1] "Testing multiplier of thin:  15 :"
    #> [1] "Testing multiplier of thin:  14 :"
    #> [1] "Testing multiplier of thin:  13 :"
    #> [1] "Testing multiplier of thin:  12 :"
    #> [1] "Testing multiplier of thin:  11 :"
    #> [1] "Testing multiplier of thin:  10 :"
    #> [1] "Testing multiplier of thin:  9 :"
    #> [1] "Testing multiplier of thin:  8 :"
    #> [1] "Testing multiplier of thin:  7 :"
    #> [1] "Testing multiplier of thin:  6 :"
    #> [1] "Testing multiplier of thin:  5 :"
    #> [1] "Testing multiplier of thin:  4 :"
    #> [1] "Testing multiplier of thin:  3 :"
    #> [1] "Testing multiplier of thin:  2 :"
    #> [1] "Retained multiplier of thin:  1 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "Number of planned new iterations non-positive: end of MCMC cycles"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Main MCMC sampling finished."
    #> [1] "###################################################################################"
    #> [1] "Final max raw multiplier of thin:  32.162"
    #> [1] "###################################################################################"
    #> [1] "Testing final multiplier of thin:  32 :"
    #> [1] "Testing final multiplier of thin:  25 :"
    #> [1] "Testing final multiplier of thin:  20 :"
    #> [1] "Testing final multiplier of thin:  19 :"
    #> [1] "Testing final multiplier of thin:  18 :"
    #> [1] "Testing final multiplier of thin:  17 :"
    #> [1] "Testing final multiplier of thin:  16 :"
    #> [1] "Testing final multiplier of thin:  15 :"
    #> [1] "Testing final multiplier of thin:  14 :"
    #> [1] "Testing final multiplier of thin:  13 :"
    #> [1] "Testing final multiplier of thin:  12 :"
    #> [1] "Testing final multiplier of thin:  11 :"
    #> [1] "Testing final multiplier of thin:  10 :"
    #> [1] "Testing final multiplier of thin:  9 :"
    #> [1] "Testing final multiplier of thin:  8 :"
    #> [1] "Testing final multiplier of thin:  7 :"
    #> [1] "Testing final multiplier of thin:  6 :"
    #> [1] "Testing final multiplier of thin:  5 :"
    #> [1] "Testing final multiplier of thin:  4 :"
    #> [1] "Testing final multiplier of thin:  3 :"
    #> [1] "Testing final multiplier of thin:  2 :"
    #> [1] "Retained final multiplier of thin:  1"
    #> [1] "###################################################################################"
    #> Warning in system.time({: The expected effective sample size was not reached
    #> [1] "###################################################################################"
    #> [1] "MCMC has reached the required level of convergence."
    #> [1] "###################################################################################"
    #> [1] "MCMC has NOT reached the required level of effective values."
    #> [1] "###################################################################################"

This model did not converge nor allow us to reach the required number of effective values. The traceplot of the Slope parameter indeed indicates a difficulty of convergence, which is however less extreme that in the case of the above (untempered) RW block sampler (not shown here due to problems with CRAN; you can run the code in the FALSE condition below to see it).

    if (FALSE) {traceplot(out.mcmc.APT[,"Slope"],main="Traceplot of Slope")}

By default, when turning `APT` to `TRUE` , `runMCMC_btadjust` puts a tempered random walk sampler - called `sampler_RW_tempered` - on each of the model’s parameters. Staying in the realm of APT, we try an additional tempered sampler provided by the `nimbleAPT` library, the tempered block sampler - called `sampler_RW_block_tempered` - instead of the independent random walk samplers, still through the parameter `confModel.expression.toadd`:

    sampler.expression.toadd<-expression(
      {ConfModel[[i]]$removeSamplers(c("Intercept","Slope"))
        ConfModel[[i]]$addSampler(target = c("Intercept","Slope"),type = "sampler_RW_block_tempered",control=list(temperPriors=FALSE))
      })

    out.mcmc.blockAPT<-runMCMC_btadjust(code=ModelCode, constants = ModelConsts, data = ModelData, MCMC_language="Nimble",
        Nchains=Nchains, params=params, inits=Inits,
        niter.min=1000, niter.max=Inf,
        nburnin.min=100, nburnin.max=Inf,
        thin.min=1, thin.max=Inf,
        conv.max=1.05, neff.min=1000,
        control=list(neff.method="Coda", time.max=120, print.diagnostics=FALSE),
        control.MCMC=list(confModel.expression.toadd=sampler.expression.toadd, APT=TRUE, parallelize=TRUE, parallelizeInitExpr= expression(if(MCMC_language=="Nimble"){library(nimble);if(control.MCMC$APT) {library(nimbleAPT)}} else {NULL}), n.adapt=1000))
    #> [1] "control$seed is NULL. Replaced by 1"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Non convergence"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  88.885"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  89 :"
    #> [1] "Testing multiplier of thin:  71 :"
    #> [1] "Testing multiplier of thin:  56 :"
    #> [1] "Testing multiplier of thin:  44 :"
    #> [1] "Testing multiplier of thin:  35 :"
    #> [1] "Testing multiplier of thin:  28 :"
    #> [1] "Testing multiplier of thin:  22 :"
    #> [1] "Testing multiplier of thin:  20 :"
    #> [1] "Testing multiplier of thin:  19 :"
    #> [1] "Testing multiplier of thin:  18 :"
    #> [1] "Testing multiplier of thin:  17 :"
    #> [1] "Testing multiplier of thin:  16 :"
    #> [1] "Testing multiplier of thin:  15 :"
    #> [1] "Testing multiplier of thin:  14 :"
    #> [1] "Testing multiplier of thin:  13 :"
    #> [1] "Testing multiplier of thin:  12 :"
    #> [1] "Retained multiplier of thin:  12 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  12.135"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  12 :"
    #> [1] "Testing multiplier of thin:  11 :"
    #> [1] "Testing multiplier of thin:  10 :"
    #> [1] "Testing multiplier of thin:  9 :"
    #> [1] "Testing multiplier of thin:  8 :"
    #> [1] "Testing multiplier of thin:  7 :"
    #> [1] "Testing multiplier of thin:  6 :"
    #> [1] "Retained multiplier of thin:  6 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  1.879"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  2 :"
    #> [1] "Retained multiplier of thin:  1 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Raw multiplier of thin:  1.948"
    #> [1] "###################################################################################"
    #> [1] "Testing multiplier of thin:  2 :"
    #> [1] "Retained multiplier of thin:  1 :"
    #> [1] "###################################################################################"
    #> [1] "Case of niter update: Convergence and trying to reach end of MCMC at the end of next cycle"
    #> [1] "###################################################################################"
    #> [1] "Number of planned new iterations non-positive: end of MCMC cycles"
    #> [1] "###################################################################################"
    #> [1] "###################################################################################"
    #> [1] "Main MCMC sampling finished."
    #> [1] "###################################################################################"
    #> [1] "Final max raw multiplier of thin:  1.948"
    #> [1] "###################################################################################"
    #> [1] "Testing final multiplier of thin:  2 :"
    #> [1] "Retained final multiplier of thin:  1"
    #> [1] "###################################################################################"
    #> Warning in system.time({: The expected effective sample size was not reached
    #> [1] "###################################################################################"
    #> [1] "MCMC has reached the required level of convergence."
    #> [1] "###################################################################################"
    #> [1] "MCMC has NOT reached the required level of effective values."
    #> [1] "###################################################################################"

This one converges but does not allow to reach a sufficient number of effective values. The associated summary is given below:

    summary(out.mcmc.blockAPT)
    #>
    #> Iterations = 2274:63618
    #> Thinning interval = 72
    #> Number of chains = 2
    #> Sample size per chain = 853
    #>
    #> 1. Empirical mean and standard deviation for each variable,
    #>    plus standard error of the mean:
    #>
    #>                 Mean      SD  Naive SE Time-series SE
    #> Intercept     -0.186 3.38349 0.0819172       0.115256
    #> Slope          1.002 0.03382 0.0008189       0.001152
    #> population.sd  1.028 0.02304 0.0005578       0.000518
    #>
    #> 2. Quantiles for each variable:
    #>
    #>                  2.5%     25%     50%   75% 97.5%
    #> Intercept     -6.9065 -2.4940 0.01236 2.020 6.259
    #> Slope          0.9376  0.9798 0.99997 1.025 1.069
    #> population.sd  0.9845  1.0127 1.02733 1.043 1.074

    effectiveSize(out.mcmc.blockAPT)
    #>     Intercept         Slope population.sd
    #>      875.9018      875.7931     2028.2980

Summaries are fine in this case in the sense that the true parameters are within the credibility intervals.

# Comparison of the above results

Let us now compare the different above models in terms of their rapidly analyzed performances (convergence, reaching the number of effective values, duration…).

Comparison of the efficiency of the different types of samplers:
| default | RW.block | Slice.block | HMC | APT | APT.block
--- | --- | --- | --- | --- | --- | ---
converged | 0.000e+00 | 0.000e+00 | 1.000e+00 | 1.000e+00 | 1.000e+00 | 1.000e+00
neffs.reached | 0.000e+00 | 0.000e+00 | 1.000e+00 | 0.000e+00 | 0.000e+00 | 0.000e+00
final.Nchains | 2.000e+00 | 2.000e+00 | 2.000e+00 | 2.000e+00 | 2.000e+00 | 2.000e+00
burnin | 1.000e+02 | 1.000e+02 | 1.010e+02 | 3.520e+02 | 1.410e+02 | 2.274e+03
thin | 2.640e+02 | 1.400e+02 | 6.000e+00 | 1.000e+00 | 6.400e+01 | 7.200e+01
niter.tot | 1.949e+05 | 1.404e+05 | 5.529e+03 | 1.416e+03 | 4.423e+04 | 6.367e+04
Nvalues | 1.410e+03 | 2.006e+03 | 1.810e+03 | 2.128e+03 | 1.378e+03 | 1.706e+03
neff.min | 2.221e+01 | 9.290e+00 | 1.410e+03 | 2.574e+02 | 4.284e+01 | 8.758e+02
neff.median | 2.229e+01 | 1.070e+01 | 1.637e+03 | 2.578e+02 | 4.287e+01 | 8.759e+02
duration | 1.244e+02 | 1.838e+02 | 4.679e+01 | 1.253e+02 | 1.264e+02 | 1.251e+02
duration.MCMC.preparation | 2.685e+01 | 3.255e+01 | 3.380e+01 | 6.849e+01 | 2.435e+01 | 2.815e+01
duration.MCMC.transient | 5.618e+01 | 2.183e+01 | 6.145e-01 | 1.652e+01 | 1.847e+00 | 3.672e+00
duration.MCMC.asymptotic | 0.000e+00 | 0.000e+00 | 3.029e+00 | 1.300e+01 | 7.128e+01 | 6.880e+01

We notice that 4 methods reached convergence - the block slice sampler, the Hamiltonian sampler, the APT sampler and the block APT sampler - but only one reached the required number of effective values: the block slice sampler. On the whole it therefore appears that block slice sampling clearly performs best on this case. The block APT sampler comes second since it converged and reached final numbers of effective values that were far greater than the default setting. The Hamiltonian was stopped soon in terms of number of iterations but its number of effective values was rather promising.

# Conclusion

We have here shown how to use the capabilities of `runMCMC_btadjust()` in terms of coupling with `NIMBLE` for changing and controlling MCMC samplers. This is something that I have found very powerful in various contexts, with also the possibility to write your own samplers. There are other samplers provided by Nimble that the user may find useful (cf. Nimble user guide, Nimble web site: https://r-nimble.org/ and Nimble users mailing list). This is a real strength of `NIMBLE` that `runMCMC_btadjust()` permits to use quite easily.

# Acknowledgements

I wished to thank David Pleydell for help on the package `nimbleAPT` and the `NIMBLE` users mailing list.

The initial development of this package (up to version 1.1.1) was performed within the GAMBAS project funded by the French Agence Nationale pour la Recherche (ANR-18-CE02-0025) (cf. https://gambas.cirad.fr/).

# References

Miasojedow, B., E. Moulines, and M. Vihola. 2013. An adaptive parallel tempering algorithm. Journal of Computational and Graphical Statistics 22:649–664.