Skip to contents

Repeatedly simulate known-shift datasets from a createSimulationTemplate() object and analyze each replicate with searchOptimalConfiguration() to estimate expected shift recovery performance. The default "original" generator reproduces the published simulation operations. The "empirical" generator remains available explicitly for full-covariance draws and an empirically calibrated integration-rate robustness scenario.

Usage

runShiftRecoverySimulationStudy(
  template,
  n_replicates,
  tree_tip_count = NULL,
  simulation_options = list(),
  search_options = list(),
  fuzzy_distance = 2,
  weighted = TRUE,
  num_cores = 1,
  seed = NULL
)

Arguments

template

A bifrost_simulation_template returned by createSimulationTemplate().

n_replicates

Integer number of simulation replicates to run.

tree_tip_count

Optional integer tip count for random subtree sampling. If NULL, each replicate uses the full empirical tree.

simulation_options

Named list of additional arguments passed to simulateShiftedDataset(), including covariance and integration-rate trade-off controls. The manuscript-compatible "original" generator is used by default; request simulation_generator = "empirical" for the full-covariance option. The simulation_generator entry must contain exactly one supported string. For replicated studies, simulation_options$seed is not allowed; use the wrapper-level seed argument instead.

search_options

Named list of arguments passed to searchOptimalConfiguration(). These override the default response-only intercept-only search settings assembled from template. If formula is omitted, the wrapper inherits template$search_formula, which is intercept-only in the empirically calibrated simulation workflow.

fuzzy_distance

Integer node distance used for fuzzy matching in evaluateShiftRecovery().

weighted

Logical; if TRUE, compute weighted recovery summaries using IC weights when available.

num_cores

Integer number of workers used across replicate analyses. When greater than one, outer replicate parallelism takes precedence and each inner search is forced to search_options$num_cores = 1.

seed

Optional integer random seed. When supplied, the wrapper restores the caller's previous RNG state before returning.

Value

A list of class bifrost_simulation_study containing the simulated datasets, raw search results, per-replicate summaries, a study-level recovery summary, and the output of evaluateShiftRecovery().

Details

By default, the search configuration uses uncertaintyweights_par = TRUE when weighted = TRUE, so shift-recovery summaries can report both unweighted and IC-weighted metrics.

Regardless of the global calibration model used to build template, the downstream search is intentionally restricted to intercept-only formulas. This keeps the study focused on shift recovery in the simulated response block rather than re-estimating predictor effects within each replicate. The simulated trait_data supplied to each search result therefore contains only the regenerated response variables. Templates calibrated with covariates are supported; those covariates influence the fitted means and residual covariance used to generate each response block, but they are not re-fit within each simulated search replicate.

Failed search replicates are retained in the output, but their inferred-shift metrics are recorded as NA and excluded from recovery evaluation. Their error messages and candidate counts remain available for diagnosis, and completion and failure rates are reported separately. Reproducibility for replicated studies is controlled by the wrapper-level seed argument. A supplied seed deterministically creates separate per-replicate simulation and search streams, so serial and parallel runs use the same replicate-level random draws. Parallel execution also uses future.seed = TRUE; per-replicate simulator seeds are intentionally disallowed here. When num_cores > 1, namespace-level workers receive only their current replicate and compact shared settings; replicate call records use a literal template placeholder so the complete calibration template is not serialized again inside every simulated object.

Examples

if (FALSE) { # \dontrun{
set.seed(1)
tr <- ape::rtree(40)
X <- matrix(rnorm(40 * 3), ncol = 3)
rownames(X) <- tr$tip.label
tmpl <- createSimulationTemplate(tr, X, formula = "trait_data ~ 1", method = "LL")

recovery_study <- runShiftRecoverySimulationStudy(
  tmpl,
  n_replicates = 2,
  tree_tip_count = 25,
  simulation_options = list(
    num_shifts = 2,
    min_shift_tips = 3,
    max_shift_tips = 8,
    scale_mode = "proportional"
  ),
  search_options = list(
    formula = "trait_data ~ 1",
    min_descendant_tips = 3,
    shift_acceptance_threshold = 5,
    num_cores = 1,
    IC = "GIC",
    method = "LL"
  ),
  num_cores = 1,
  seed = 2
)

recovery_study
} # }