# The bkmrhat package: diagnostics and multi-chain inference in Bayesian kernel machine regression

## Alexander Keil

### 2025-11-19

## Introduction to `bkmr` and `bkmrhat`

`bkmr` is a package to implement Bayesian kernel machine regression (BKMR) using Markov chain Monte Carlo (MCMC). Notably, `bkmr` is missing some key features in Bayesian inference and MCMC diagnostics: 1) no facility for running multiple chains in parallel 2) no inference across multiple chains 3) limited posterior summary of parameters 4) limited diagnostics. The `bkmrhat` package is a lightweight set of function that fills in each of those gaps by enabling post-processing of `bkmr` output in other packages and building a small framework for parallel processing.

## How to use the `bkmrhat` package

1. Fit a BKMR model for a single chain using the `kmbaryes` function from `bkmr`, or use multiple parallel chains `kmbayes_parallel` from `bkmrhat`
2. Perform diagnostics on single or multiple chains using the `kmbayes_diagnose` function (uses functions from the `rstan` package) OR convert the BKMR fit(s) to `mcmc` (one chain) or `mcmc.list` (multiple chains) objects from the `coda` package using `as.mcmc` or `as.mcmc.list` from the `bkmrhat` package. The `coda` package has a whole host of inference and diagnostic procedures (but may lag behind some of the diagnostics functions from `rstan`).
3. Perform posterior summaries using `coda` functions or combine chains from a `kmbayes_parallel` fit using `kmbayes_combine`. Final posterior inferences can be made on the combined object, which enables use of `bkmr` package functions for visual summaries of independent and joint effects of exposures in the `bkmr` model.

First, simulate some data from the `bkmr` function

    library("bkmr")
    library("bkmrhat")
    library("coda")

    set.seed(111)
    dat <- bkmr::SimData(n = 50, M = 5, ind=1:3, Zgen="realistic")
    y <- dat$y
    Z <- dat$Z
    X <- cbind(dat$X, rnorm(50))
    head(cbind(y,Z,X))

    ##               y          z1          z2          z3          z4         z5
    ## [1,]  4.1379128 -0.06359282 -0.02996246 -0.14190647 -0.44089352 -0.1878732
    ## [2,] 12.0843607 -0.07308834  0.32021690  1.08838691  0.29448354 -1.4609837
    ## [3,]  7.8859254  0.59604857  0.20602329  0.46218114 -0.03387906 -0.7615902
    ## [4,]  1.1609768  1.46504863  2.48389356  1.39869461  1.49678590  0.2837234
    ## [5,]  0.4989372 -0.37549639  0.01159884  1.17891641 -0.05286516 -0.1680664
    ## [6,]  5.0731242 -0.36904566 -0.49744932 -0.03330522  0.30843805  0.6814844
    ##
    ## [1,]  1.0569172 -1.0503824
    ## [2,]  4.8158570  0.3251424
    ## [3,]  2.6683461 -2.1048716
    ## [4,] -0.7492096 -0.9551027
    ## [5,] -0.5428339 -0.5306399
    ## [6,]  1.6493251  0.8274405

### Example 1: single vs multi-chains

There is some overhead in parallel processing when using the `future` package, so the payoff when using parallel processing may vary by the problem. Here it is about a 2-4x speedup, but you can see more benefit at higher iterations. Note that this may not yield as many usable iterations as a single large chain if a substantial burnin period is needed, but it will enable useful convergence diagnostics. Note that the future package can implement sequential processing, which effectively turns the kmbayes_parallel into a loop, but still has all other advantages of multiple chains.

    # enable parallel processing (up to 4 simultaneous processes here)
    future::plan(strategy = future::multisession)

    # single run of 4000 observations from bkmr package
    set.seed(111)
    system.time(kmfit <- suppressMessages(kmbayes(y = y, Z = Z, X = X, iter = 4000, verbose = FALSE, varsel = FALSE)))

    ##    user  system elapsed
    ##   4.714   0.450   5.211

    # 4 runs of 1000 observations from bkmrhat package
    set.seed(111)
    system.time(kmfit5 <- suppressMessages(kmbayes_parallel(nchains=4, y = y, Z = Z, X = X, iter = 1000, verbose = FALSE, varsel = FALSE)))

    ## Chain 1
    ## Chain 2
    ## Chain 3
    ## Chain 4

    ##    user  system elapsed
    ##   0.359   0.048   9.585

### Example 2: Diagnostics

The diagnostics from the rstan package come from the `monitor` function (see the help files for that function in the rstan pacakge)

    # Using rstan functions (set burnin/warmup to zero for comparability with coda numbers given later
    #  posterior summaries should be performed after excluding warmup/burnin)
    singlediag = kmbayes_diagnose(kmfit, warmup=0, digits_summary=2)

    ## Single chain

    ## Inference for the input samples (1 chains: each with iter = 4000; warmup = 0):
    ##
    ##            Q5  Q50  Q95 Mean  SD  Rhat Bulk_ESS Tail_ESS
    ## beta1     1.9  2.0  2.1  2.0 0.0  1.00     2820     3194
    ## beta2     0.0  0.1  0.3  0.1 0.1  1.00     3739     3535
    ## lambda    3.9 10.0 22.3 11.2 5.9  1.00      346      222
    ## r1        0.0  0.0  0.1  0.0 0.1  1.01      129      173
    ## r2        0.0  0.0  0.1  0.0 0.1  1.00      182      181
    ## r3        0.0  0.0  0.0  0.0 0.0  1.01      158      112
    ## r4        0.0  0.0  0.1  0.0 0.1  1.03      176      135
    ## r5        0.0  0.0  0.0  0.0 0.1  1.00      107      114
    ## sigsq.eps 0.2  0.3  0.5  0.4 0.1  1.00     1262     1563
    ##
    ## For each parameter, Bulk_ESS and Tail_ESS are crude measures of
    ## effective sample size for bulk and tail quantities respectively (an ESS > 100
    ## per chain is considered good), and Rhat is the potential scale reduction
    ## factor on rank normalized split chains (at convergence, Rhat <= 1.05).

    # Using rstan functions (multiple chains enable R-hat)
    multidiag = kmbayes_diagnose(kmfit5, warmup=0, digits_summary=2)

    ## Parallel chains

    ## Inference for the input samples (4 chains: each with iter = 1000; warmup = 0):
    ##
    ##            Q5 Q50  Q95 Mean  SD  Rhat Bulk_ESS Tail_ESS
    ## beta1     1.9 2.0  2.1  2.0 0.0  1.00     2334     1512
    ## beta2     0.0 0.1  0.3  0.1 0.1  1.00     3228     3643
    ## lambda    4.1 9.9 23.7 11.4 6.1  1.01      384      235
    ## r1        0.0 0.0  0.6  0.1 0.2  1.11       32       28
    ## r2        0.0 0.0  0.1  0.0 0.1  1.02      184      138
    ## r3        0.0 0.0  0.1  0.0 0.1  1.03      101       87
    ## r4        0.0 0.0  0.1  0.0 0.1  1.03      142       89
    ## r5        0.0 0.0  0.1  0.0 0.2  1.09       37       53
    ## sigsq.eps 0.2 0.3  0.5  0.3 0.1  1.00      899     1148
    ##
    ## For each parameter, Bulk_ESS and Tail_ESS are crude measures of
    ## effective sample size for bulk and tail quantities respectively (an ESS > 100
    ## per chain is considered good), and Rhat is the potential scale reduction
    ## factor on rank normalized split chains (at convergence, Rhat <= 1.05).

    # using coda functions, not using any burnin (for demonstration only)
    kmfitcoda = as.mcmc(kmfit, iterstart = 1)
    kmfit5coda = as.mcmc.list(kmfit5, iterstart = 1)

    # single chain trace plot
    traceplot(kmfitcoda)

The trace plots look typical, and fine, but trace plots don’t give a full picture of convergence. Note that there is apparent quick convergence for a couple of parameters demonstrated by movement away from the starting value and concentration of the rest of the samples within a narrow band.

Seeing visual evidence that different chains are sampling from the same marginal distributions is reassuring about the stability of the results.

    # multiple chain trace plot
    traceplot(kmfit5coda)

Now examine “cross correlation”, which can help identify highly correlated parameters in the posterior, which can be problematic for MCMC sampling. Here there is a block {r3,r4,r5} which appear to be highly correlated. All other things equal, having highly correlated parameters in the posterior means that more samples are needed than would be needed with uncorrelated parameters.

    # multiple cross-correlation plot (combines all samples)
    crosscorr(kmfit5coda)

    ##                  beta1       beta2       lambda          r1          r2
    ## beta1      1.000000000  0.06063846  0.009619123  0.11863388  0.16213875
    ## beta2      0.060638461  1.00000000 -0.085205911 -0.08886474 -0.08044259
    ## lambda     0.009619123 -0.08520591  1.000000000 -0.03205822 -0.01564349
    ## r1         0.118633883 -0.08886474 -0.032058222  1.00000000  0.53623405
    ## r2         0.162138749 -0.08044259 -0.015643492  0.53623405  1.00000000
    ## r3         0.137314004 -0.05691727 -0.016089644  0.56704044  0.82325738
    ## r4         0.208176153 -0.07359633 -0.037404909  0.68444590  0.79050903
    ## r5         0.092149921 -0.05934214 -0.022712957  0.28395735  0.53975111
    ## sigsq.eps -0.029864448  0.04941527 -0.302048386 -0.13153370 -0.14208652
    ##                    r3          r4          r5   sigsq.eps
    ## beta1      0.13731400  0.20817615  0.09214992 -0.02986445
    ## beta2     -0.05691727 -0.07359633 -0.05934214  0.04941527
    ## lambda    -0.01608964 -0.03740491 -0.02271296 -0.30204839
    ## r1         0.56704044  0.68444590  0.28395735 -0.13153370
    ## r2         0.82325738  0.79050903  0.53975111 -0.14208652
    ## r3         1.00000000  0.74555066  0.46137436 -0.12123327
    ## r4         0.74555066  1.00000000  0.51099917 -0.11946345
    ## r5         0.46137436  0.51099917  1.00000000 -0.02434892
    ## sigsq.eps -0.12123327 -0.11946345 -0.02434892  1.00000000

    crosscorr.plot(kmfit5coda)

Now examine “autocorrelation” to identify parameters that have high correlation between subsequent iterations of the MCMC sampler, which can lead to inefficient MCMC sampling. All other things equal, having highly autocorrelated parameters in the posterior means that more samples are needed than would be needed with low-autocorrelation parameters.

    # multiple chain trace plot
    #autocorr(kmfit5coda) # lots of output
    autocorr.plot(kmfit5coda)

Graphical tools can be limited, and are sometimes difficult to use effectively with scale parameters (of which `bkmr` has many). Additionally, no single diagnostic is perfect, leading many authors to advocate the use of multiple, complementary diagnostics. Thus, more formal diagnostics are helpful.

Gelman’s r-hat diagnostic gives an interpretable diagnostic: the expected reduction in the standard error of the posterior means if you could run the chains to an infinite size. These give some idea about when is a fine idea to stop sampling. There are rules of thumb about using r-hat to stop sampling that are available from several authors (for example you can consult the help files for `rstan` and `coda`).

Effective sample size is also useful - it estimates the amount of information in your chain, expressed in terms of the number of independent posterior samples it would take to match that information (e.g. if we could just sample from the posterior directly).

    # Gelman's r-hat using coda estimator (will differ from rstan implementation)
    gelman.diag(kmfit5coda)

    ## Potential scale reduction factors:
    ##
    ##           Point est. Upper C.I.
    ## beta1           1.00       1.01
    ## beta2           1.00       1.00
    ## lambda          1.01       1.03
    ## r1              1.07       1.15
    ## r2              1.06       1.12
    ## r3              1.09       1.16
    ## r4              1.03       1.08
    ## r5              1.03       1.05
    ## sigsq.eps       1.00       1.00
    ##
    ## Multivariate psrf
    ##
    ## 1.04

    # effective sample size
    effectiveSize(kmfitcoda)

    ##      beta1      beta2     lambda         r1         r2         r3         r4
    ## 2411.61996 2865.78264  431.49111   87.11091  260.29419  328.45388  181.61903
    ##         r5  sigsq.eps
    ##  123.06100 1719.70470

    effectiveSize(kmfit5coda)

    ##     beta1     beta2    lambda        r1        r2        r3        r4        r5
    ## 2585.9305 3682.2160  429.5677  100.1964  189.3471  120.6366  167.5633  181.6534
    ## sigsq.eps
    ## 1393.7627

### Example 3: Posterior summaries

Posterior kernel marginal densities, 1 chain

    # posterior kernel marginal densities using `mcmc` and `mcmc` objects
    densplot(kmfitcoda)

Posterior kernel marginal densities, multiple chains combined. Look for multiple modes that may indicate non-convergence of some chains

    # posterior kernel marginal densities using `mcmc` and `mcmc` objects
    densplot(kmfit5coda)

Other diagnostics from the `coda` package are available here.

Finally, the chains from the original `kmbayes_parallel` fit can be combined into a single chain (see the help files for how to deal with burn-in, the default in `bkmr` is to use the first half of the chain, which is respected here). The `kmbayes_combine` function smartly first combines the burn-in iterations and then combines the iterations after burnin, such that the burn-in rules of subsequent functions within the `bkmr` package are respected. Note that unlike the `as.mcmc.list` function, this function combines all iterations into a single chain, so trace plots will not be good diagnotistics in this combined object, and it should be used once one is assured that all chains have converged and the burn-in is acceptable.

With this combined set of samples, you can follow any of the post-processing functions from the `bkmr` functions, which are described here: https://jenfb.github.io/bkmr/overview.html. For example, see below the estimation of the posterior mean difference along a series of quantiles of all exposures in Z.

    # posterior summaries using `mcmc` and `mcmc` objects
    summary(kmfitcoda)

    ##
    ## Iterations = 1:4000
    ## Thinning interval = 1
    ## Number of chains = 1
    ## Sample size per chain = 4000
    ##
    ## 1. Empirical mean and standard deviation for each variable,
    ##    plus standard error of the mean:
    ##
    ##               Mean      SD  Naive SE Time-series SE
    ## beta1      1.98349 0.04392 0.0006945      0.0008944
    ## beta2      0.11999 0.08532 0.0013490      0.0015937
    ## lambda    11.18052 5.90540 0.0933726      0.2842911
    ## r1         0.03010 0.06385 0.0010095      0.0068408
    ## r2         0.03656 0.05690 0.0008997      0.0035270
    ## r3         0.02131 0.04065 0.0006427      0.0022429
    ## r4         0.02855 0.06641 0.0010500      0.0049278
    ## r5         0.02904 0.09543 0.0015089      0.0086023
    ## sigsq.eps  0.35283 0.08228 0.0013009      0.0019840
    ##
    ## 2. Quantiles for each variable:
    ##
    ##               2.5%     25%      50%      75%    97.5%
    ## beta1      1.90086 1.95474  1.98287  2.01177  2.07218
    ## beta2     -0.04513 0.06141  0.12001  0.17716  0.28713
    ## lambda     3.23941 7.01812 10.00979 14.12075 25.93332
    ## r1         0.01022 0.01232  0.01807  0.02767  0.09818
    ## r2         0.01018 0.01433  0.02172  0.04049  0.12353
    ## r3         0.01015 0.01180  0.01488  0.02198  0.05655
    ## r4         0.01040 0.01299  0.01670  0.02582  0.08533
    ## r5         0.01025 0.01219  0.01532  0.01951  0.07021
    ## sigsq.eps  0.22855 0.29302  0.34057  0.39833  0.54838

    summary(kmfit5coda)

    ##
    ## Iterations = 1:1000
    ## Thinning interval = 1
    ## Number of chains = 4
    ## Sample size per chain = 1000
    ##
    ## 1. Empirical mean and standard deviation for each variable,
    ##    plus standard error of the mean:
    ##
    ##               Mean      SD  Naive SE Time-series SE
    ## beta1      1.98298 0.04612 0.0007293      0.0009553
    ## beta2      0.11772 0.08773 0.0013871      0.0014504
    ## lambda    11.40130 6.12844 0.0968991      0.2986803
    ## r1         0.08451 0.23939 0.0037851      0.0552047
    ## r2         0.04603 0.10130 0.0016017      0.0076008
    ## r3         0.04565 0.13176 0.0020833      0.0122476
    ## r4         0.04559 0.13140 0.0020776      0.0131784
    ## r5         0.04645 0.15955 0.0025227      0.0254687
    ## sigsq.eps  0.34965 0.08363 0.0013224      0.0023475
    ##
    ## 2. Quantiles for each variable:
    ##
    ##               2.5%     25%     50%      75%   97.5%
    ## beta1      1.89623 1.95258 1.98244  2.01235  2.0742
    ## beta2     -0.05300 0.05883 0.11761  0.17403  0.2905
    ## lambda     3.67891 7.12616 9.92129 14.40938 27.3759
    ## r1         0.01028 0.01329 0.02010  0.03206  1.0863
    ## r2         0.01046 0.01444 0.02281  0.04011  0.1913
    ## r3         0.01057 0.01260 0.01542  0.02247  0.4567
    ## r4         0.01017 0.01199 0.01658  0.02697  0.5292
    ## r5         0.01022 0.01208 0.01451  0.02095  0.6181
    ## sigsq.eps  0.22134 0.29111 0.33894  0.39542  0.5413

    # highest posterior density intervals using `mcmc` and `mcmc` objects
    HPDinterval(kmfitcoda)

    ##                 lower       upper
    ## beta1      1.90086937  2.07231225
    ## beta2     -0.04051413  0.29012992
    ## lambda     2.68866745 22.62238656
    ## r1         0.01002094  0.06754473
    ## r2         0.01002597  0.09683241
    ## r3         0.01003504  0.04392585
    ## r4         0.01000511  0.06510500
    ## r5         0.01005733  0.04755863
    ## sigsq.eps  0.21411781  0.52229778
    ## attr(,"Probability")
    ## [1] 0.95

    HPDinterval(kmfit5coda)

    ## [[1]]
    ##                 lower       upper
    ## beta1      1.89901711  2.06836515
    ## beta2     -0.05823444  0.27896900
    ## lambda     3.49272910 24.19963619
    ## r1         0.01005653  0.24866062
    ## r2         0.01060567  0.10852257
    ## r3         0.01007904  0.11839418
    ## r4         0.01000154  0.09244707
    ## r5         0.01000340  0.03816433
    ## sigsq.eps  0.21686487  0.51812163
    ## attr(,"Probability")
    ## [1] 0.95
    ##
    ## [[2]]
    ##                 lower      upper
    ## beta1      1.89943022  2.0753674
    ## beta2     -0.05153992  0.2882669
    ## lambda     3.43119031 23.1473823
    ## r1         0.01057793  1.2923038
    ## r2         0.01081145  0.1262868
    ## r3         0.01083266  0.1137877
    ## r4         0.01015433  0.3104132
    ## r5         0.01068435  0.0511815
    ## sigsq.eps  0.21825793  0.5414359
    ## attr(,"Probability")
    ## [1] 0.95
    ##
    ## [[3]]
    ##                 lower       upper
    ## beta1      1.89832712  2.07555916
    ## beta2     -0.04869572  0.28362661
    ## lambda     3.48106754 23.29163500
    ## r1         0.01010213  0.08172045
    ## r2         0.01018210  0.12211655
    ## r3         0.01021310  0.14258896
    ## r4         0.01047863  0.06584484
    ## r5         0.01005888  0.03750793
    ## sigsq.eps  0.21408996  0.50909708
    ## attr(,"Probability")
    ## [1] 0.95
    ##
    ## [[4]]
    ##                 lower       upper
    ## beta1      1.89141784  2.07007189
    ## beta2     -0.04577330  0.30009627
    ## lambda     3.90931112 24.33540839
    ## r1         0.01009055  0.07067172
    ## r2         0.01005591  0.08111086
    ## r3         0.01039775  0.07175424
    ## r4         0.01007359  0.06401300
    ## r5         0.01004367  0.98114725
    ## sigsq.eps  0.21926000  0.54019887
    ## attr(,"Probability")
    ## [1] 0.95

    # combine multiple chains into a single chain
    fitkmccomb = kmbayes_combine(kmfit5)

    # For example:
    summary(fitkmccomb)

    ## Fitted object of class 'bkmrfit'
    ## Iterations: 4000
    ## Outcome family: gaussian
    ## Model fit on: 2025-11-19 10:57:04.138257
    ## Running time:  1.18362 secs
    ##
    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param      rate
    ## 1 lambda 0.4258565
    ## 2     r1 0.2245561
    ## 3     r2 0.2633158
    ## 4     r3 0.1647912
    ## 5     r4 0.1845461
    ## 6     r5 0.1312828
    ##
    ## Parameter estimates (based on iterations 2001-4000):
    ##       param     mean      sd    q_2.5   q_97.5
    ## 1     beta1  1.98139 0.04338  1.89747  2.06594
    ## 2     beta2  0.12214 0.08642 -0.04635  0.28978
    ## 3 sigsq.eps  0.35259 0.08181  0.22589  0.53979
    ## 4        r1  0.02356 0.01873  0.01028  0.07269
    ## 5        r2  0.03062 0.02677  0.01099  0.11466
    ## 6        r3  0.02344 0.02086  0.01103  0.09542
    ## 7        r4  0.02128 0.01811  0.01015  0.07353
    ## 8        r5  0.01655 0.01042  0.01000  0.04604
    ## 9    lambda 11.36624 5.98372  3.77510 26.86412

    ## NULL

    mean.difference <- suppressWarnings(OverallRiskSummaries(fit = fitkmccomb, y = y, Z = Z, X = X,
                                          qs = seq(0.25, 0.75, by = 0.05),
                                          q.fixed = 0.5, method = "exact"))
    mean.difference

    ##    quantile        est         sd
    ## 1      0.25 -0.7171653 0.11869197
    ## 2      0.30 -0.5786133 0.09642004
    ## 3      0.35 -0.3865093 0.07915782
    ## 4      0.40 -0.2705121 0.04634351
    ## 5      0.45 -0.1507966 0.02668551
    ## 6      0.50  0.0000000 0.00000000
    ## 7      0.55  0.2162210 0.04196557
    ## 8      0.60  0.3341817 0.05137196
    ## 9      0.65  0.5160848 0.08286888
    ## 10     0.70  0.8825217 0.14407776
    ## 11     0.75  0.9750881 0.15359942

    with(mean.difference, {
      plot(quantile, est, pch=19, ylim=c(min(est - 1.96*sd), max(est + 1.96*sd)),
           axes=FALSE, ylab= "Mean difference", xlab = "Joint quantile")
      segments(x0=quantile, x1=quantile, y0 = est - 1.96*sd, y1 = est + 1.96*sd)
      abline(h=0)
      axis(1)
      axis(2)
      box(bty='l')
    })

# Example 4: diagnostics and inference when variable selection is used (Bayesian model averaging over the scale parameters of the kernel function)

These results parallel previous session and are given here without comment, other than to note that no fixed effects (X variables) are included, and that it is useful to check the posterior inclusion probabilities to ensure they are similar across chains.

    set.seed(111)
    system.time(kmfitbma.list <- suppressWarnings(kmbayes_parallel(nchains=4, y = y, Z = Z, X = X, iter = 1000, verbose = FALSE, varsel = TRUE)))

    ## Chain 1

    ## Iteration: 100 (10% completed; 0.04698 secs elapsed)

    ## Iteration: 200 (20% completed; 0.09187 secs elapsed)

    ## Iteration: 300 (30% completed; 0.13672 secs elapsed)

    ## Iteration: 400 (40% completed; 0.18241 secs elapsed)

    ## Iteration: 500 (50% completed; 0.23537 secs elapsed)

    ## Iteration: 600 (60% completed; 0.28523 secs elapsed)

    ## Iteration: 700 (70% completed; 0.33103 secs elapsed)

    ## Iteration: 800 (80% completed; 0.37859 secs elapsed)

    ## Iteration: 900 (90% completed; 0.42699 secs elapsed)

    ## Iteration: 1000 (100% completed; 0.47529 secs elapsed)

    ## Chain 2

    ## Iteration: 100 (10% completed; 0.0522 secs elapsed)

    ## Iteration: 200 (20% completed; 0.22663 secs elapsed)

    ## Iteration: 300 (30% completed; 0.27335 secs elapsed)

    ## Iteration: 400 (40% completed; 0.31918 secs elapsed)

    ## Iteration: 500 (50% completed; 0.36388 secs elapsed)

    ## Iteration: 600 (60% completed; 0.41142 secs elapsed)

    ## Iteration: 700 (70% completed; 0.45521 secs elapsed)

    ## Iteration: 800 (80% completed; 0.50126 secs elapsed)

    ## Iteration: 900 (90% completed; 0.54405 secs elapsed)

    ## Iteration: 1000 (100% completed; 0.58985 secs elapsed)

    ## Chain 3

    ## Iteration: 100 (10% completed; 0.05363 secs elapsed)

    ## Iteration: 200 (20% completed; 0.10388 secs elapsed)

    ## Iteration: 300 (30% completed; 0.15206 secs elapsed)

    ## Iteration: 400 (40% completed; 0.19918 secs elapsed)

    ## Iteration: 500 (50% completed; 0.24638 secs elapsed)

    ## Iteration: 600 (60% completed; 0.29031 secs elapsed)

    ## Iteration: 700 (70% completed; 0.33389 secs elapsed)

    ## Iteration: 800 (80% completed; 0.38077 secs elapsed)

    ## Iteration: 900 (90% completed; 0.42459 secs elapsed)

    ## Iteration: 1000 (100% completed; 0.47101 secs elapsed)

    ## Chain 4

    ## Iteration: 100 (10% completed; 0.05046 secs elapsed)

    ## Iteration: 200 (20% completed; 0.09532 secs elapsed)

    ## Iteration: 300 (30% completed; 0.14506 secs elapsed)

    ## Iteration: 400 (40% completed; 0.1892 secs elapsed)

    ## Iteration: 500 (50% completed; 0.23772 secs elapsed)

    ## Iteration: 600 (60% completed; 0.28319 secs elapsed)

    ## Iteration: 700 (70% completed; 0.34597 secs elapsed)

    ## Iteration: 800 (80% completed; 0.38828 secs elapsed)

    ## Iteration: 900 (90% completed; 0.43416 secs elapsed)

    ## Iteration: 1000 (100% completed; 0.4771 secs elapsed)

    ##    user  system elapsed
    ##   0.345   0.053   0.866

    bmadiag = kmbayes_diagnose(kmfitbma.list)

    ## Parallel chains

    ## Inference for the input samples (4 chains: each with iter = 1000; warmup = 500):
    ##
    ##            Q5  Q50  Q95 Mean  SD  Rhat Bulk_ESS Tail_ESS
    ## beta1     1.9  2.0  2.0  2.0 0.0  1.01      508     1735
    ## beta2     0.0  0.1  0.3  0.1 0.1  1.00     1634     1800
    ## lambda    5.3 11.8 31.3 14.2 8.6  1.03      105      111
    ## r1        0.0  0.0  0.1  0.0 0.0  1.13       35       54
    ## r2        0.0  0.0  0.1  0.0 0.0  1.18       20       40
    ## r3        0.0  0.0  0.0  0.0 0.0  1.02      130      100
    ## r4        0.0  0.0  0.1  0.0 0.0  1.06       53       33
    ## r5        0.0  0.0  0.0  0.0 0.0  1.03      140       78
    ## sigsq.eps 0.3  0.4  0.5  0.4 0.1  1.00      627     1494
    ##
    ## For each parameter, Bulk_ESS and Tail_ESS are crude measures of
    ## effective sample size for bulk and tail quantities respectively (an ESS > 100
    ## per chain is considered good), and Rhat is the potential scale reduction
    ## factor on rank normalized split chains (at convergence, Rhat <= 1.05).

    # posterior exclusion probability of each chain
    lapply(kmfitbma.list, function(x) t(ExtractPIPs(x)))

    ## [[1]]
    ##          [,1]    [,2]    [,3]    [,4]    [,5]
    ## variable "z1"    "z2"    "z3"    "z4"    "z5"
    ## PIP      "0.992" "0.764" "0.312" "0.732" "0.548"
    ##
    ## [[2]]
    ##          [,1]    [,2]    [,3]    [,4]    [,5]
    ## variable "z1"    "z2"    "z3"    "z4"    "z5"
    ## PIP      "0.698" "0.946" "0.436" "0.588" "0.552"
    ##
    ## [[3]]
    ##          [,1]    [,2]    [,3]    [,4]    [,5]
    ## variable "z1"    "z2"    "z3"    "z4"    "z5"
    ## PIP      "0.938" "0.590" "0.240" "0.766" "0.330"
    ##
    ## [[4]]
    ##          [,1]    [,2]    [,3]    [,4]    [,5]
    ## variable "z1"    "z2"    "z3"    "z4"    "z5"
    ## PIP      "0.730" "0.882" "0.342" "0.750" "0.492"

    kmfitbma.comb = kmbayes_combine(kmfitbma.list)
    summary(kmfitbma.comb)

    ## Fitted object of class 'bkmrfit'
    ## Iterations: 4000
    ## Outcome family: gaussian
    ## Model fit on: 2025-11-19 10:57:13.078278
    ## Running time:  0.47544 secs
    ##
    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##               param      rate
    ## 1            lambda 0.4851213
    ## 2 r/delta (overall) 0.3308327
    ## 3 r/delta  (move 1) 0.4426478
    ## 4 r/delta  (move 2) 0.2216163
    ##
    ## Parameter estimates (based on iterations 2001-4000):
    ##       param     mean      sd    q_2.5   q_97.5
    ## 1     beta1  1.97474 0.04443  1.88866  2.06270
    ## 2     beta2  0.11984 0.08669 -0.05194  0.28878
    ## 3 sigsq.eps  0.37670 0.08844  0.24078  0.58243
    ## 4        r1  0.02127 0.01734  0.00000  0.06506
    ## 5        r2  0.02408 0.03050  0.00000  0.10093
    ## 6        r3  0.00541 0.00986  0.00000  0.02558
    ## 7        r4  0.02036 0.02397  0.00000  0.08360
    ## 8        r5  0.00869 0.01187  0.00000  0.03734
    ## 9    lambda 14.16323 8.61424  4.79547 39.76603
    ##
    ## Posterior inclusion probabilities:
    ##   variable    PIP
    ## 1       z1 0.8395
    ## 2       z2 0.7955
    ## 3       z3 0.3325
    ## 4       z4 0.7090
    ## 5       z5 0.4805

    ## NULL

    ExtractPIPs(kmfitbma.comb) # posterior inclusion probabilities

    ##   variable    PIP
    ## 1       z1 0.8395
    ## 2       z2 0.7955
    ## 3       z3 0.3325
    ## 4       z4 0.7090
    ## 5       z5 0.4805

    mean.difference2 <- suppressWarnings(OverallRiskSummaries(fit = kmfitbma.comb, y = y, Z = Z, X = X,                                       qs = seq(0.25, 0.75, by = 0.05),
                                          q.fixed = 0.5, method = "exact"))
    mean.difference2

    ##    quantile        est         sd
    ## 1      0.25 -0.6146968 0.12440724
    ## 2      0.30 -0.4915129 0.10102886
    ## 3      0.35 -0.3181185 0.08864045
    ## 4      0.40 -0.2356821 0.04858855
    ## 5      0.45 -0.1399487 0.02775610
    ## 6      0.50  0.0000000 0.00000000
    ## 7      0.55  0.1969654 0.04878284
    ## 8      0.60  0.2991031 0.06074668
    ## 9      0.65  0.4678363 0.09732989
    ## 10     0.70  0.8152414 0.17315362
    ## 11     0.75  0.8922859 0.17206280

    with(mean.difference2, {
      plot(quantile, est, pch=19, ylim=c(min(est - 1.96*sd), max(est + 1.96*sd)),
           axes=FALSE, ylab= "Mean difference", xlab = "Joint quantile")
      segments(x0=quantile, x1=quantile, y0 = est - 1.96*sd, y1 = est + 1.96*sd)
      abline(h=0)
      axis(1)
      axis(2)
      box(bty='l')
    })

### Example 5: Parallel posterior summaries as diagnostics

`bkmrhat` also has ported versions of the native posterior summarization functions to compare how these summaries vary across parallel chains. Note that these should serve as diagnostics, and final posterior inference should be done on the combined chain. The easiest of these functions to demonstrate is the `OverallRiskSummaries_parallel` function, which simply runs `OverallRiskSummaries` (from the `bkmr` package) on each chain and combines the results. Notably, this function fixes the y-axis at zero for the median, so it under-represents overall predictive variation across chains, but captures variation in effect estimates across the chains. Ideally, that variation is negligible - e.g. if you see differences between chains that would result in different interpretations, you should re-fit the model with more iterations. In this example, the results are reasonably consistent across chains, but one might want to run more iterations if, say, the differences seen across the upper error bounds are of such a magnitude as to be practically meaningful.

    set.seed(111)
    system.time(kmfitbma.list <- suppressWarnings(kmbayes_parallel(nchains=4, y = y, Z = Z, X = X, iter = 1000, verbose = FALSE, varsel = TRUE)))

    ## Chain 1

    ## Iteration: 100 (10% completed; 0.04437 secs elapsed)

    ## Iteration: 200 (20% completed; 0.08926 secs elapsed)

    ## Iteration: 300 (30% completed; 0.13463 secs elapsed)

    ## Iteration: 400 (40% completed; 0.18461 secs elapsed)

    ## Iteration: 500 (50% completed; 0.22756 secs elapsed)

    ## Iteration: 600 (60% completed; 0.27209 secs elapsed)

    ## Iteration: 700 (70% completed; 0.42183 secs elapsed)

    ## Iteration: 800 (80% completed; 0.47734 secs elapsed)

    ## Iteration: 900 (90% completed; 0.52534 secs elapsed)

    ## Iteration: 1000 (100% completed; 0.57117 secs elapsed)

    ## Chain 2

    ## Iteration: 100 (10% completed; 0.0453 secs elapsed)

    ## Iteration: 200 (20% completed; 0.09534 secs elapsed)

    ## Iteration: 300 (30% completed; 0.14516 secs elapsed)

    ## Iteration: 400 (40% completed; 0.19311 secs elapsed)

    ## Iteration: 500 (50% completed; 0.23682 secs elapsed)

    ## Iteration: 600 (60% completed; 0.40315 secs elapsed)

    ## Iteration: 700 (70% completed; 0.44757 secs elapsed)

    ## Iteration: 800 (80% completed; 0.49265 secs elapsed)

    ## Iteration: 900 (90% completed; 0.54188 secs elapsed)

    ## Iteration: 1000 (100% completed; 0.58522 secs elapsed)

    ## Chain 3

    ## Iteration: 100 (10% completed; 0.04957 secs elapsed)

    ## Iteration: 200 (20% completed; 0.09726 secs elapsed)

    ## Iteration: 300 (30% completed; 0.14525 secs elapsed)

    ## Iteration: 400 (40% completed; 0.32493 secs elapsed)

    ## Iteration: 500 (50% completed; 0.36995 secs elapsed)

    ## Iteration: 600 (60% completed; 0.41537 secs elapsed)

    ## Iteration: 700 (70% completed; 0.46255 secs elapsed)

    ## Iteration: 800 (80% completed; 0.50596 secs elapsed)

    ## Iteration: 900 (90% completed; 0.54883 secs elapsed)

    ## Iteration: 1000 (100% completed; 0.59305 secs elapsed)

    ## Chain 4

    ## Iteration: 100 (10% completed; 0.17464 secs elapsed)

    ## Iteration: 200 (20% completed; 0.22162 secs elapsed)

    ## Iteration: 300 (30% completed; 0.26513 secs elapsed)

    ## Iteration: 400 (40% completed; 0.3083 secs elapsed)

    ## Iteration: 500 (50% completed; 0.4671 secs elapsed)

    ## Iteration: 600 (60% completed; 0.51094 secs elapsed)

    ## Iteration: 700 (70% completed; 0.55433 secs elapsed)

    ## Iteration: 800 (80% completed; 0.59765 secs elapsed)

    ## Iteration: 900 (90% completed; 0.65502 secs elapsed)

    ## Iteration: 1000 (100% completed; 0.69891 secs elapsed)

    ##    user  system elapsed
    ##   0.349   0.051   1.092

    meandifference_par = OverallRiskSummaries_parallel(kmfitbma.list, y = y, Z = Z, X = X ,qs = seq(0.25, 0.75, by = 0.05), q.fixed = 0.5, method = "exact")

    ## Chain 1
    ## Chain 2
    ## Chain 3
    ## Chain 4

    head(meandifference_par)

    ##   quantile        est         sd chain
    ## 1     0.25 -0.6191983 0.12051657     1
    ## 2     0.30 -0.5019924 0.09747662     1
    ## 3     0.35 -0.3067245 0.08058220     1
    ## 4     0.40 -0.2311694 0.04357326     1
    ## 5     0.45 -0.1454666 0.02463054     1
    ## 6     0.50  0.0000000 0.00000000     1

    nchains = length(unique(meandifference_par$chain))

    with(meandifference_par, {
      plot.new()
      plot.window(ylim=c(min(est - 1.96*sd), max(est + 1.96*sd)),
                  xlim=c(min(quantile), max(quantile)),
           ylab= "Mean difference", xlab = "Joint quantile")
      for(cch in seq_len(nchains)){
        width = diff(quantile)[1]
        jit = runif(1, -width/5, width/5)
       points(jit+quantile[chain==cch], est[chain==cch], pch=19, col=cch)
       segments(x0=jit+quantile[chain==cch], x1=jit+quantile[chain==cch], y0 = est[chain==cch] - 1.96*sd[chain==cch], y1 = est[chain==cch] + 1.96*sd[chain==cch], col=cch)
      }
      abline(h=0)
      axis(1)
      axis(2)
      box(bty='l')
      legend("bottom", col=1:nchains, pch=19, lty=1, legend=paste("chain", 1:nchains), bty="n")
    })

    regfuns_par = PredictorResponseUnivar_parallel(kmfitbma.list, y = y, Z = Z, X = X ,qs = seq(0.25, 0.75, by = 0.05), q.fixed = 0.5, method = "exact")

    ## Chain 1
    ## Chain 2
    ## Chain 3
    ## Chain 4

    head(regfuns_par)

    ##   variable         z       est        se chain
    ## 1       z1 -2.186199 -1.435648 0.5915233     1
    ## 2       z1 -2.082261 -1.379339 0.5677579     1
    ## 3       z1 -1.978323 -1.321802 0.5442158     1
    ## 4       z1 -1.874385 -1.263092 0.5208719     1
    ## 5       z1 -1.770446 -1.203272 0.4977049     1
    ## 6       z1 -1.666508 -1.142409 0.4746991     1

    nchains = length(unique(meandifference_par$chain))

    # single variable
    with(regfuns_par[regfuns_par$variable=="z1",], {
      plot.new()
      plot.window(ylim=c(min(est - 1.96*se), max(est + 1.96*se)),
                  xlim=c(min(z), max(z)),
           ylab= "Predicted Y", xlab = "Z")
      pc = c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7", "#999999")
      pc2 = c("#0000001A", "#E69F001A", "#56B4E91A", "#009E731A", "#F0E4421A", "#0072B21A", "#D55E001A", "#CC79A71A", "#9999991A")
      for(cch in seq_len(nchains)){
       ribbonX = c(z[chain==cch], rev(z[chain==cch]))
       ribbonY = c(est[chain==cch] + 1.96*se[chain==cch], rev(est[chain==cch] - 1.96*se[chain==cch]))
       polygon(x=ribbonX, y = ribbonY, col=pc2[cch], border=NA)
       lines(z[chain==cch], est[chain==cch], pch=19, col=pc[cch])
      }
      axis(1)
      axis(2)
      box(bty='l')
      legend("bottom", col=1:nchains, pch=19, lty=1, legend=paste("chain", 1:nchains), bty="n")
    })

### Example 6: Continuing a fit

Sometimes you just need to run more samples in an existing chain. For example, you run a bkmr fit for 3 days, only to find you don’t have enough samples. A “continued” fit just means that you can start off at the last iteration you were at and just keep building on an existing set of results by lengthening the Markov chain. Unfortunately, due to how the `kmbayes` function accepts starting values (for the official install version), you can’t quite do this exactly in many cases (The function will relay a message and possible solutions, if any. `bkmr` package authors are aware of this issue). The `kmbayes_continue` function continues a `bkmr` fit as well as the `bkmr` package will allow. The `r` parameters from the fit must all be initialized at the same value, so `kmbayes_continue` starts a new MCMC fit at the final values of all parameters from the prior bkmr fit, but sets all of the `r` parameters to the mean at the last iteration from the previous fit. Additionally, if `h.hat` parameters are estimated, these are fixed to be above zero to meet similar constraints, either by fixing them at their posterior mean or setting to a small positive value. One should inspect trace plots to see whether this will cause issues (e.g. if the traceplots demonstrate different patterns in the samples before and after the continuation). Here’s an example with a quick check of diagnostics of the first part of the chain, and the combined chain (which could be used for inference or extended again, if necessary). We caution users that this function creates 2 distinct, if very similar Markov chains, and to use appropriate caution if traceplots differ before and after each continuation. Nonetheless, in many cases one can act as though all samples are from the same Markov chain.

Note that if you install the developmental version of the `bkmr` package you can continue fits from exactly where they left off, so you get a true, single Markov chain. You can install that via the commented code below

    # install dev version of bkmr to allow true continued fits.
    #install.packages("devtools")
    #devtools::install_github("jenfb/bkmr")

    set.seed(111)
    # run 100 initial iterations for a model with only 2 exposures
    Z2 = Z[,1:2]
    kmfitbma.start <- suppressWarnings(kmbayes(y = y, Z = Z2, X = X, iter = 500, verbose = FALSE, varsel = FALSE))

    ## Iteration: 50 (10% completed; 0.02397 secs elapsed)

    ## Iteration: 100 (20% completed; 0.05547 secs elapsed)

    ## Iteration: 150 (30% completed; 0.08043 secs elapsed)

    ## Iteration: 200 (40% completed; 0.10795 secs elapsed)

    ## Iteration: 250 (50% completed; 0.13285 secs elapsed)

    ## Iteration: 300 (60% completed; 0.16364 secs elapsed)

    ## Iteration: 350 (70% completed; 0.18805 secs elapsed)

    ## Iteration: 400 (80% completed; 0.21684 secs elapsed)

    ## Iteration: 450 (90% completed; 0.24108 secs elapsed)

    ## Iteration: 500 (100% completed; 0.27007 secs elapsed)

    kmbayes_diag(kmfitbma.start)

    ## Single chain

    ## Inference for the input samples (1 chains: each with iter = 500; warmup = 250):
    ##
    ##            Q5  Q50  Q95 Mean  SD  Rhat Bulk_ESS Tail_ESS
    ## beta1     1.9  2.0  2.0  2.0 0.0  1.02      184      238
    ## beta2     0.0  0.1  0.2  0.1 0.1  1.00      259      197
    ## lambda    6.4 15.0 33.2 17.0 8.2  1.03       32       38
    ## r1        0.0  0.0  0.0  0.0 0.0  1.20        5       16
    ## r2        0.0  0.0  0.1  0.0 0.0  1.01       26       28
    ## sigsq.eps 0.3  0.4  0.5  0.4 0.1  1.00      195      157
    ##
    ## For each parameter, Bulk_ESS and Tail_ESS are crude measures of
    ## effective sample size for bulk and tail quantities respectively (an ESS > 100
    ## per chain is considered good), and Rhat is the potential scale reduction
    ## factor on rank normalized split chains (at convergence, Rhat <= 1.05).

    ##                  mean     se_mean         sd        2.5%         25%
    ## beta1      1.96002469 0.002996717 0.04071499  1.88732666  1.93049212
    ## beta2      0.09934846 0.005601868 0.09100445 -0.06425935  0.03866907
    ## lambda    17.03096186 1.480162990 8.16066439  5.99261263 10.62650587
    ## r1         0.02445179 0.006963033 0.01503557  0.01009566  0.01650405
    ## r2         0.02931652 0.003839555 0.02294888  0.01050284  0.01300914
    ## sigsq.eps  0.38177766 0.006062528 0.08483668  0.25337749  0.32965181
    ##                   50%         75%       97.5% n_eff      Rhat valid          Q5
    ## beta1      1.96168526  1.98761915  2.04042002   192 1.0174270     1  1.89496869
    ## beta2      0.09494353  0.15892943  0.26004214   263 0.9979147     1 -0.04566104
    ## lambda    14.97716478 22.17297877 35.85230190    32 1.0322926     1  6.39370841
    ## r1         0.02078562  0.03098429  0.04827171    12 1.1969882     1  0.01009566
    ## r2         0.02369039  0.03766762  0.09155390    38 1.0072685     1  0.01213386
    ## sigsq.eps  0.36834066  0.42589940  0.61832629   187 0.9989252     1  0.26493273
    ##                   Q50         Q95    MCSE_Q2.5    MCSE_Q25    MCSE_Q50
    ## beta1      1.96168526  2.02239974 0.0061439984 0.003896226 0.004052288
    ## beta2      0.09494353  0.24851228 0.0125216874 0.009226277 0.006555085
    ## lambda    14.97716478 33.24311222 1.2308679562 0.873763048 1.767460797
    ## r1         0.02078562  0.04809447 0.0006957382 0.002532962 0.004430201
    ## r2         0.02369039  0.07473096 0.0011501569 0.001816037 0.004755380
    ## sigsq.eps  0.36834066  0.53371298 0.0112954790 0.004960890 0.004978161
    ##              MCSE_Q75  MCSE_Q97.5     MCSE_SD Bulk_ESS Tail_ESS
    ## beta1     0.001302716 0.010539232 0.002122356      184      238
    ## beta2     0.008043386 0.008643991 0.003965505      259      197
    ## lambda    3.451601827 1.237140963 1.056843473       32       38
    ## r1        0.006131920 0.001462048 0.005269340        5       16
    ## r2        0.002075801 0.013178244 0.002737455       26       28
    ## sigsq.eps 0.006630407 0.039435542 0.004293256      195      157

    # run 2000 additional iterations
    moreiterations = kmbayes_continue(kmfitbma.start, iter=2000)

    ## Validating control.params...

    ## Validating starting.values...

    ## Iteration: 201 (10% completed; 0.12364 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param  rate
    ## 1 lambda 0.430
    ## 2     r1 0.245
    ## 3     r2 0.235

    ## Iteration: 401 (20% completed; 0.24282 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param   rate
    ## 1 lambda 0.4675
    ## 2     r1 0.1850
    ## 3     r2 0.2500

    ## Iteration: 601 (30% completed; 0.36783 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param      rate
    ## 1 lambda 0.4966667
    ## 2     r1 0.1633333
    ## 3     r2 0.2333333

    ## Iteration: 801 (40% completed; 0.4825 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param    rate
    ## 1 lambda 0.49625
    ## 2     r1 0.18500
    ## 3     r2 0.25250

    ## Iteration: 1001 (50% completed; 0.59947 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param  rate
    ## 1 lambda 0.489
    ## 2     r1 0.192
    ## 3     r2 0.243

    ## Iteration: 1201 (60% completed; 0.72636 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param      rate
    ## 1 lambda 0.4908333
    ## 2     r1 0.1900000
    ## 3     r2 0.2416667

    ## Iteration: 1401 (70% completed; 0.84681 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param      rate
    ## 1 lambda 0.4921429
    ## 2     r1 0.1928571
    ## 3     r2 0.2457143

    ## Iteration: 1601 (80% completed; 0.9701 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param     rate
    ## 1 lambda 0.490625
    ## 2     r1 0.199375
    ## 3     r2 0.246250

    ## Iteration: 1801 (90% completed; 1.08822 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param      rate
    ## 1 lambda 0.4888889
    ## 2     r1 0.1983333
    ## 3     r2 0.2516667

    ## Iteration: 2001 (100% completed; 1.20897 secs elapsed)

    ## Acceptance rates for Metropolis-Hastings algorithm:
    ##    param   rate
    ## 1 lambda 0.4940
    ## 2     r1 0.2005
    ## 3     r2 0.2485

    kmbayes_diag(moreiterations)

    ## Single chain

    ## Inference for the input samples (1 chains: each with iter = 2500; warmup = 1250):
    ##
    ##            Q5  Q50  Q95 Mean  SD  Rhat Bulk_ESS Tail_ESS
    ## beta1     1.9  2.0  2.0  2.0 0.0     1     1226     1188
    ## beta2     0.0  0.1  0.2  0.1 0.1     1     1342     1153
    ## lambda    4.8 11.6 27.8 13.5 7.2     1      132      118
    ## r1        0.0  0.0  0.1  0.0 0.0     1       36       60
    ## r2        0.0  0.0  0.1  0.0 0.0     1       63       74
    ## sigsq.eps 0.3  0.4  0.5  0.4 0.1     1      687      907
    ##
    ## For each parameter, Bulk_ESS and Tail_ESS are crude measures of
    ## effective sample size for bulk and tail quantities respectively (an ESS > 100
    ## per chain is considered good), and Rhat is the potential scale reduction
    ## factor on rank normalized split chains (at convergence, Rhat <= 1.05).

    ##                  mean     se_mean         sd        2.5%        25%         50%
    ## beta1      1.95902186 0.001313130 0.04611042  1.86406579 1.92824507  1.95995430
    ## beta2      0.10364082 0.002472674 0.09078254 -0.08028629 0.04483808  0.10217365
    ## lambda    13.45858048 0.575042840 7.18975272  4.63158214 8.06723496 11.62070182
    ## r1         0.02558935 0.002505863 0.01684645  0.01051616 0.01389383  0.02000374
    ## r2         0.03152169 0.002840685 0.02568259  0.01042071 0.01378317  0.02173483
    ## sigsq.eps  0.39671746 0.003232309 0.08256885  0.26948029 0.33468348  0.38833815
    ##                   75%       97.5% n_eff     Rhat valid          Q5         Q50
    ## beta1      1.99013424  2.05059125  1236 1.000076     1  1.88233558  1.95995430
    ## beta2      0.16648882  0.27919590  1353 1.001507     1 -0.04502216  0.10217365
    ## lambda    17.07843757 32.36093597   156 1.000211     1  4.81793593 11.62070182
    ## r1         0.03034907  0.07050527    45 1.004470     1  0.01097881  0.02000374
    ## r2         0.03773703  0.10918759    80 1.001841     1  0.01069795  0.02173483
    ## sigsq.eps  0.44446826  0.59030414   650 1.000453     1  0.28159002  0.38833815
    ##                   Q95    MCSE_Q2.5     MCSE_Q25    MCSE_Q50    MCSE_Q75
    ## beta1      2.03392196 0.0034878092 0.0018218271 0.001705603 0.001924244
    ## beta2      0.24728033 0.0063559462 0.0036553685 0.002849542 0.003241571
    ## lambda    27.83773681 0.2173104821 0.6064432977 0.646607402 0.583887847
    ## r1         0.06248365 0.0004568228 0.0017076967 0.002442041 0.003929030
    ## r2         0.07985574 0.0002483599 0.0009401994 0.002913607 0.004161742
    ## sigsq.eps  0.54458362 0.0028952538 0.0027590593 0.003419585 0.004708623
    ##            MCSE_Q97.5      MCSE_SD Bulk_ESS Tail_ESS
    ## beta1     0.004277831 0.0009293335     1226     1188
    ## beta2     0.009449827 0.0017774270     1342     1153
    ## lambda    1.708604468 0.4073777680      132      118
    ## r1        0.005777434 0.0017834748       36       60
    ## r2        0.011995755 0.0020158792       63       74
    ## sigsq.eps 0.007480563 0.0023104526      687      907

    TracePlot(moreiterations, par="beta")
    TracePlot(moreiterations, par="r")

    # move to sequential processing (end of vignette)
    future::plan(strategy = future::sequential)

### Acknowledgments

Thanks to Haotian “Howie” Wu for invaluable feedback on early versions of the package.