Running Chains in Parallel

Overview

exnexSurv can run multiple independent MCMC chains and, when requested, distribute them across R-level parallel workers. The C++ sampler still handles one chain per call; the R bridge coordinates the chain loop and combines the post-warmup draws.

Use:

parallel_chains must be less than or equal to chains.

Simulate data

library(exnexSurv)
library(survival)

simulate_surv_data <- function(
  theta,
  sigma2,
  beta = NULL,
  n_per_group = 40,
  censor_min = 4,
  censor_max = 12,
  seed = NULL
) {
  if (!is.null(seed)) {
    set.seed(seed)
  }

  groups <- rep(seq_along(theta), each = n_per_group)
  age_std <- rnorm(length(groups))
  mean_log_time <- rep(theta, each = n_per_group)

  if (!is.null(beta)) {
    mean_log_time <- mean_log_time + beta * age_std
  }

  log_time <- rnorm(length(groups), mean = mean_log_time, sd = sqrt(sigma2))
  true_time <- exp(log_time)
  censor_time <- runif(length(groups), min = censor_min, max = censor_max)

  data.frame(
    time = pmin(true_time, censor_time),
    event = as.integer(true_time <= censor_time),
    group = factor(groups),
    age_std = age_std
  )
}

sim_data <- simulate_surv_data(
  theta = c(1.0, 1.6, 2.1),
  sigma2 = 0.25,
  beta = -0.30,
  seed = 9201
)

Fit two chains in parallel

fit_parallel <- exnex_surv(
  Surv(time, event) ~ group + age_std,
  data = sim_data,
  iter = 60,
  warmup = 20,
  chains = 2,
  parallel_chains = 2,
  seed = 9201
)

print(fit_parallel, show_trace = FALSE)
#> <exnex_surv model>
#> Draws: 80 total post-warmup samples
#>        40 post-warmup samples per chain
#> Groups: 3 | Covariates: 1 
#> MCMC: iter = 60 , warmup = 20 , chains = 2 
#> 
#>  parameter       mean         sd        q05        q50        q95
#>    theta_1  1.0228722 0.09968104  0.8918244  1.0047708  1.1926777
#>    theta_2  1.7698594 0.08266481  1.6414429  1.7703639  1.9085733
#>    theta_3  2.2190697 0.11007270  2.0460476  2.2195088  2.3877982
#>     beta_1 -0.3805380 0.05226572 -0.4544758 -0.3867217 -0.2803758
#>     sigma2  0.3204454 0.05609064  0.2412223  0.3210869  0.4240346

The fitted object stores the combined post-warmup draws from both chains. If you used iter = 60 and warmup = 20 with chains = 2, the posterior sample table contains 80 rows in total, or 40 rows per chain.

Fit more chains than workers

fit_four_chains <- exnex_surv(
  Surv(time, event) ~ group + age_std,
  data = sim_data,
  iter = 60,
  warmup = 20,
  chains = 4,
  parallel_chains = 2,
  seed = 9201
)

print(fit_four_chains, show_trace = FALSE)
#> <exnex_surv model>
#> Draws: 160 total post-warmup samples
#>        40 post-warmup samples per chain
#> Groups: 3 | Covariates: 1 
#> MCMC: iter = 60 , warmup = 20 , chains = 4 
#> 
#>  parameter       mean         sd        q05        q50        q95
#>    theta_1  1.0167982 0.09221235  0.8949872  1.0046741  1.1821268
#>    theta_2  1.7741504 0.09281588  1.6301872  1.7726403  1.9159255
#>    theta_3  2.2131117 0.11246031  2.0201792  2.2212840  2.3811103
#>     beta_1 -0.3804203 0.05388983 -0.4628156 -0.3854931 -0.2754689
#>     sigma2  0.3114648 0.05345781  0.2313879  0.3058044  0.4207686

This runs four independent chains while scheduling only two workers at a time.

Traceplots with all chains

plot(
  fit_parallel,
  parameters = c("theta_1", "theta_2", "theta_3", "beta_1", "sigma2"),
  ask = FALSE
)

Each parameter panel shows all chains together, which makes it easier to compare mixing and overlap across chains.

Compare to sequential execution

fit_sequential <- exnex_surv(
  Surv(time, event) ~ group + age_std,
  data = sim_data,
  iter = 60,
  warmup = 20,
  chains = 2,
  parallel_chains = 1,
  seed = 9201
)

identical(fit_parallel$draws, fit_sequential$draws)
#> [1] TRUE

Changing parallel_chains affects only how the chains are scheduled, not the model itself.

Practical notes

If you want to run more chains than you want to evaluate concurrently, set parallel_chains smaller than chains. That is useful when the sampler is expensive or when you want to avoid using all available cores at once.

mirror server hosted at Truenetwork, Russian Federation.