Skip to contents

This guide is the ordinary CausalStress workflow. It uses exported functions and estimators shipped with the package. A run produces estimator outputs and a typed score surface; cs_collect_scores() is the canonical way to collect that surface. The older ATT- and QST-specific collectors remain compatibility projections.

Discover governed components

Suites are named collections, not a scientific endorsement of every member. Inspect the DGP status and estimator requirements before running a study.

suites <- cs_suite_registry()
suites[, c("suite_id", "description")]
# A tibble: 5 × 2
  suite_id  description
  <chr>     <chr>
1 placebo   All placebo (sharp-null) DGPs
2 heavytail Heavy-tail signal and placebo heavy-tail DGPs
3 signal    Signal-focused DGPs (baseline, QTE heterogeneity, nonlinear heteros…
4 stress    Stress-test DGPs (overlap and tilt stressors)
5 all       All available synthetic DGPs                                        
baseline <- cs_get_dgp("synth_baseline")
baseline[c("dgp_id", "version", "status", "type")]
# A tibble: 1 × 4
  dgp_id         version status type
  <chr>          <chr>   <chr>  <chr>
1 synth_baseline 1.6.0   stable synthetic
lm_descriptor <- cs_get_estimator("lm_att")
lm_descriptor[c(
  "estimator_id", "type", "oracle", "supports_qst", "version",
  "source", "requires_pkgs"
)]
$estimator_id
[1] "lm_att"

$type
[1] "gcomp"

$oracle
[1] FALSE

$supports_qst
[1] FALSE

$version
[1] "0.2.1"

$source
[1] "core"

$requires_pkgs
character(0)

Run once, then collect typed scores

cs_run_single() is the smallest ordinary entry point. The result keeps typed estimator outputs, canonical scores, compatibility projections (att and qst), bootstrap draws when requested, metadata, and provenance.

one_run <- cs_run_single(
  dgp_id = "synth_baseline",
  estimator_id = "lm_att",
  n = 200,
  seed = 17
)

names(one_run)
[1] "outputs"    "scores"     "att"        "qst"        "boot_draws"
[6] "meta"       "provenance"
one_scores <- cs_collect_scores(one_run)
one_scores |>
  select(
    estimand_target_id, metric_id, score_status,
    estimate, truth, error, non_comparable_reason
  )
# A tibble: 1 × 7
  estimand_target_id metric_id   score_status estimate truth   error
  <chr>              <chr>       <chr>           <dbl> <dbl>   <dbl>
1 att                point_error scored           1.04  1.09 -0.0480
# ℹ 1 more variable: non_comparable_reason <chr>

Each score row has an estimand_target_id. CausalStress compares an estimator output only with truth for that same target:

  • ATT is the mean structural effect among treated units when that mean is a valid target for the DGP regime.
  • ATE is the finite-sample mean structural effect over all generated units.
  • QST is a quantile-specific distributional contrast among treated units, indexed by tau.
  • CATE is an explicitly staged target. Wave 1 records whether an estimator produced it, but does not yet score a CATE curve.

Targets are never silently cross-scored. A CATE estimate is not an ATT estimate, and an ATT estimate is not a QST estimate.

Request ATT and ATE explicitly

The built-in oracle is useful for testing benchmark truth plumbing. It is not an ordinary estimator: its descriptor permits the runner to grant governed structural_te through the Airlock.

oracle_run <- cs_run_single(
  dgp_id = "synth_baseline",
  estimator_id = "oracle_att",
  n = 200,
  seed = 17,
  config = list(estimand_targets = c("att", "ate"))
)

cs_collect_scores(oracle_run) |>
  select(estimand_target_id, metric_id, score_status, estimate, truth, error)
# A tibble: 2 × 6
  estimand_target_id metric_id   score_status estimate truth error
  <chr>              <chr>       <chr>           <dbl> <dbl> <dbl>
1 att                point_error scored          1.09  1.09      0
2 ate                point_error scored          0.974 0.974     0

QST truth is defined on potential-outcome distributions. This direct DGP call is a truth-definition demonstration; do not pass y0, y1, p, or structural_te to an ordinary estimator.

truth_example <- dgp_synth_baseline(n = 300, seed = 19)
cs_true_qst(
  y0 = truth_example$df$y0,
  y1 = truth_example$df$y1,
  w = truth_example$df$w,
  tau = c(0.25, 0.50, 0.75)
)
# A tibble: 3 × 2
    tau value
  <dbl> <dbl>
1  0.25 0.693
2  0.5  1.11
3  0.75 1.42 

No dependency-free estimator shipped with CausalStress currently produces QST. The optional GenGC estimator does. Its example is deliberately opt-in so the ordinary article remains executable without optional estimator packages.

run_optional_qst <- requireNamespace("GenGC", quietly = TRUE) &&
  identical(tolower(Sys.getenv("CAUSALSTRESS_RUN_OPTIONAL_DOCS")), "true")

if (!run_optional_qst) {
  message(
    "Optional QST example skipped. Install GenGC and set ",
    "CAUSALSTRESS_RUN_OPTIONAL_DOCS=true to run it."
  )
}
Optional QST example skipped. Install GenGC and set CAUSALSTRESS_RUN_OPTIONAL_DOCS=true to run it.
qst_run <- cs_run_single(
  dgp_id = "synth_qte1",
  estimator_id = "gengc",
  n = 300,
  seed = 19,
  status = "experimental",
  tau = c(0.25, 0.50, 0.75),
  config = list(estimand_targets = "qst")
)

cs_collect_scores(qst_run) |>
  select(estimand_target_id, tau, metric_id, score_status, estimate, truth, error)

Make staged targets visible

A mixed request can preserve executable ATT evidence while explicitly showing that CATE scoring is not implemented. The CATE row is evidence about the current contract; it is not an ATT result under another name.

staged <- cs_run_single(
  dgp_id = "synth_baseline",
  estimator_id = "lm_att",
  n = 200,
  seed = 23,
  config = list(estimand_targets = c("att", "cate"))
)

cs_collect_scores(staged) |>
  select(
    estimand_target_id, metric_id, score_status,
    estimate, non_comparable_reason
  )
# A tibble: 2 × 5
  estimand_target_id metric_id   score_status   estimate non_comparable_reason
  <chr>              <chr>       <chr>             <dbl> <chr>
1 att                point_error scored             1.25 <NA>
2 cate               point_error non_comparable    NA    target_not_implemented

Repeat over seeds and summarize

Use cs_run_seeds() for one DGP-estimator pair and cs_run_grid() for ordinary cross-product work. Keep typed scores for target-aware analysis; use the summary helpers only where the target and moment regime make the aggregate meaningful.

runs <- cs_run_grid(
  dgp_ids = "synth_baseline",
  estimator_ids = c("lm_att", "ipw_att"),
  n = 200,
  seeds = 1:3,
  show_progress = FALSE
)
Running batch: synth_baseline x lm_att
Running batch: synth_baseline x ipw_att
typed_scores <- cs_collect_scores(runs)
typed_scores |>
  count(estimator_id, estimand_target_id, score_status)
# A tibble: 2 × 4
  estimator_id estimand_target_id score_status     n
  <chr>        <chr>              <chr>        <int>
1 ipw_att      att                scored           3
2 lm_att       att                scored           3
summary <- cs_summarise_runs(runs)
summary
# A tibble: 2 × 14
  dgp_id         estimator_id     n oracle supports_qst n_runs mean_true_att
  <chr>          <chr>        <int> <lgl>  <lgl>         <int>         <dbl>
1 synth_baseline ipw_att        200 FALSE  FALSE             3          1.12
2 synth_baseline lm_att         200 FALSE  FALSE             3          1.12
# ℹ 7 more variables: mean_est_att <dbl>, mean_error <dbl>, sd_error <dbl>,
#   mean_abs_error <dbl>, max_abs_error <dbl>, mean_att_covered <dbl>,
#   mean_att_ci_width <dbl>

Do not use mean-based ATT RMSE, coverage, or rankings for synth_heavytail. That DGP intentionally probes an estimand boundary under Cauchy-mixture noise: its governed structural contrast is a signal anchor, not a conventional mean potential-outcome ATT. Run ATT estimators there to study breakdown, but use QST for valid distributional comparisons rather than an ATT shootout.

Inspect provenance

Scientific identities live in the result metadata and score records; non-scientific execution details live in provenance.

cs_meta_flatten(one_run) |>
  select(
    dgp_id, dgp_version, estimator_id, estimator_version,
    config_fingerprint, fit_fingerprint
  )
# A tibble: 1 × 6
  dgp_id         dgp_version estimator_id estimator_version config_fingerprint
  <chr>          <chr>       <chr>        <chr>             <chr>
1 synth_baseline 1.6.0       lm_att       0.2.1             aadd190389820d49ea5…
# ℹ 1 more variable: fit_fingerprint <chr>
cs_provenance(one_run)[c("run_time_dgp", "run_time_est", "run_time_total")]
$run_time_dgp
[1] 0.004334927

$run_time_est
[1] 0.00509572

$run_time_total
[1] 0.03819466

Persist completed runs and resume strictly

Persistence is opt-in. Without a board, results are returned but not stored. With a pins board, completed results are written as RDS-backed pins. Resume is also opt-in: skip_existing = TRUE reuses only schema-4 artifacts whose configuration fingerprint matches exactly. A missing/old schema or a mismatch fails closed. force = TRUE or skip_existing = FALSE recomputes instead.

board_path <- tempfile("causalstress-board-")
dir.create(board_path)
board <- pins::board_folder(board_path)

first_pass <- cs_run_seeds(
  dgp_id = "synth_baseline",
  estimator_id = "lm_att",
  n = 150,
  seeds = 1:2,
  board = board,
  show_progress = FALSE
)

resumed <- cs_run_seeds(
  dgp_id = "synth_baseline",
  estimator_id = "lm_att",
  n = 150,
  seeds = 1:3,
  board = board,
  skip_existing = TRUE,
  show_progress = FALSE
)

resumed |>
  select(seed, config_fingerprint)
# A tibble: 3 × 2
   seed config_fingerprint
  <int> <chr>
1     1 d48750694cbdfa12bdb8665b5a46f19da64308ccfb13557906e8f5e9a0c2f853
2     2 348c3ae6dde770b0de1a4d7c8236830422a739d8ae5ae986a6de1d19dbed80ba
3     3 740daaf9a345bfd9aa6e6040a916eab98938ce92cddd84d953c33cfd09cf3aca
cs_audit(board) |>
  select(dgp_id, estimator_id, n, seed)
# A tibble: 3 × 4
  dgp_id         estimator_id     n  seed
  <chr>          <chr>        <int> <int>
1 synth_baseline lm_att         150     1
2 synth_baseline lm_att         150     2
3 synth_baseline lm_att         150     3

Persistence protects completed artifacts, not an in-progress estimator call or an entire campaign transaction. Experimental parallel persistence additionally uses worker staging followed by controlled single-writer gathering.

Advanced planned batching

cs_plan_campaign() plus cs_run_batch() is the advanced lifecycle for explicit plans and batch artifacts. It is distinct from ordinary single, seed, and grid execution. cs_run_campaign() currently supports both a direct grid mode and a planned-batch mode; this guide does not redefine or deprecate either public contract.

plan <- cs_plan_campaign(
  dgp_list = c("synth_baseline"),
  estimator_list = c("lm_att", "ipw_att"),
  n_seeds = 1:100,
  batch_size = 20,
  strategy_map = list(defaults = list(n = 1000))
)

batch_1 <- cs_run_batch(
  plan = plan,
  batch_id = 1,
  staging_dir = "staging_batches"
)