This vignette defines a data generator, runs a Bayesian linear
regression study, and summarizes its results. It uses
LinearRegressionFitter() for exact conjugate inference
without Stan. For Stan models, use BrmsFitter() or
CmdStanFitter().
Run a study
Generate data
A data generator is a function with the signature
(data_spec, task_ctx) returning a named list.
data_spec contains one row of data_grid;
task_ctx identifies the task. Use R’s random functions
normally. Set the study seed in simulation_config(), not
inside the generator.
my_data_generator <- function(data_spec, task_ctx) {
n <- data_spec$n
x <- stats::rnorm(n)
y <- data_spec$intercept + data_spec$slope * x +
stats::rnorm(n, sd = data_spec$sigma)
list(
train = data.frame(y = y, x = x),
test = NULL,
response = "y",
true_params = c(
Intercept = data_spec$intercept,
x = data_spec$slope,
sigma = data_spec$sigma
),
vars_of_interest = c("Intercept", "x", "sigma")
)
}For common patterns (fixed truth, prior-predictive, inverse forward
sampling) use the factory constructors
fixed_truth_generator(),
prior_predictive_generator(),
ifs_generator().
Choose conditions and metrics
config <- simulation_config(
data_grid = data.frame(
n = c(50, 100),
intercept = 1,
slope = 2,
sigma = 1
),
fit_grid = data.frame(model = "linear"),
data_generator = my_data_generator,
fitter = LinearRegressionFitter(n_draws = 500L),
metrics = list(
posterior_summary_metric(),
sampler_diagnostics_metric()
),
n_replicates = 4L,
seed = 42L
)Run the simulation
result <- run_simulation(config, progress = FALSE)
#> 8 tasks = 2 data x 1 fit x 4 reps
#> ℹ Starting simulation with 8 tasks
#>
#> ✔ Simulation complete: 8/8 tasks succeeded in 0.2s
print(result)
#> <bayesim_simulation_result>
#> Config fingerprint: 0443023694ffb037043507f14667a212f03a59b2d3913eb62fdbba651afdb417
#> Tasks: 8
#> - Success: 8
#> - Failed: 0
#> - Pending: 0
#> - Skipped (policy-stopped): 0
#> Metrics: posterior_summary__mean__Intercept, posterior_summary__mean__x, posterior_summary__mean__sigma, posterior_summary__median__Intercept, posterior_summary__median__x, posterior_summary__median__sigma ...
#> Task grid: 8 rows x 7 cols
#> Total time: 0.19 sInspect task results
result$summary[c("task_id", "data_n", "status", "posterior_summary__mean__x")]
#> task_id data_n status posterior_summary__mean__x
#> 1 d001_f001_r00001 50 success 1.854738
#> 2 d001_f001_r00002 50 success 1.859731
#> 3 d001_f001_r00003 50 success 2.064685
#> 4 d001_f001_r00004 50 success 1.760973
#> 5 d002_f001_r00001 100 success 2.035980
#> 6 d002_f001_r00002 100 success 1.842911
#> 7 d002_f001_r00003 100 success 2.177546
#> 8 d002_f001_r00004 100 success 1.881039Each row is one task. Columns include task_id,
status, timing_total, the grid columns, and
one column per metric field (posterior_summary__mean__x,
truth__x, …). The truth__* columns record the
data-generating truth for parameter-recovery analysis.
Summarize results
Each sample size is a separate condition. Group by
data_n to keep their results separate. Select the slope’s
posterior mean to keep the table small:
summarize_simulation(
result,
by = "data_n",
metrics = "posterior_summary__mean__x"
)
#> # A tibble: 2 × 9
#> data_n n_reps n_failed failure_rate posterior_summary__mean__x_n_used
#> <dbl> <int> <int> <dbl> <int>
#> 1 50 4 0 0 4
#> 2 100 4 0 0 4
#> # ℹ 4 more variables: posterior_summary__mean__x_mean <dbl>,
#> # posterior_summary__mean__x_median <dbl>,
#> # posterior_summary__mean__x_sd <dbl>, posterior_summary__mean__x_mcse <dbl>The output reports the mean, median, standard deviation, and Monte
Carlo standard error (MCSE) across replicates, plus task and failure
counts. Use metric_cols(result, "posterior_summary") to
find other metric columns.
Performance measures
Use performance_measures() for bias, empirical SE, MSE,
coverage, and average model SE, each with its MCSE (Morris, White &
Crowther, 2019):
pm <- performance_measures(result, estimand = "x", by = "data_n")
pm
#> # A tibble: 12 × 7
#> data_n estimand measure value mcse n_sim truth_mode
#> <dbl> <chr> <chr> <dbl> <dbl> <int> <chr>
#> 1 50 x bias -0.115 0.0640 4 fixed
#> 2 50 x emp_se 0.128 0.0523 4 fixed
#> 3 50 x mse 0.0255 0.0112 4 fixed
#> 4 50 x model_se 0.140 0.00704 4 fixed
#> 5 50 x coverage 1 0 4 fixed
#> 6 50 x n_sim 4 NA 4 fixed
#> 7 100 x bias -0.0156 0.0767 4 fixed
#> 8 100 x emp_se 0.153 0.0627 4 fixed
#> 9 100 x mse 0.0179 0.00659 4 fixed
#> 10 100 x model_se 0.0996 0.00489 4 fixed
#> 11 100 x coverage 1 0 4 fixed
#> 12 100 x n_sim 4 NA 4 fixedThis pairs truth__x with the per-task posterior summary
to give, per condition cell, the bias and calibration of the
estimator.
Run a larger study
This example uses four replicates per condition to keep it quick. Choose more replicates for a precise estimate; the study design guide explains how MCSE helps you decide.
Add workers = 4 to run_simulation() to use
four parallel workers. For long runs, set result_path and
checkpoint_every when constructing the configuration.
Resume interrupted work with
resume_simulation("my_simulation", config = config), using
the original configuration. Script-defined generators require that
configuration because the saved manifest cannot reconstruct their
closures.
See parallel runs and checkpoints for a complete example, and reproducibility for seed and resume requirements.
Next steps
-
vignette("design-of-simulation-studies")for aims, estimands, and choosing the number of replicates -
vignette("sbc-and-calibration")for the calibration workflow -
vignette("brms-studies")for Stan-backed studies andmodel_grid() -
vignette("custom-fitters")/vignette("custom-metrics")for extending the framework -
vignette("parallel-and-hpc")andvignette("reproducibility")for large runs and the determinism guarantees