DGP Dossier: synth_placebo_kangschafer

1. Identity & Status

DGP ID synth_placebo_kangschafer
Version 1.4.0
Status experimental
Difficulty (5/5)
Stress Profile  overlap: moderatenoise: gaussianlinearity: smootheffect: constanttarget: both

2. What This DGP Stresses (Intent)

Intent: The “Blindfold Test” (Misspecification). Based on the classic Kang & Schafer (2007) design. The true selection mechanism relies on latent variables \(Z\), but the analyst only observes nonlinear transformations \(X\). This guarantees the propensity model (Linear in \(X\)) is misspecified.

The Scientific Question: “Does Double Robustness hold when the propensity model is dead wrong?” Since \(\hat{p}(X)\) is wrong, the estimator must rely on the outcome model (\(\hat{\mu}_0(X)\)) to save the day. This stresses the “Double Robust” property of modern estimators (AIPW, TMLE, DR-Learners).

3. Identification Assumptions (Explicit)

  • Selection on observables holds in the latent Z space.
  • Observed covariates are nonlinear transforms of Z, creating misspecification.
  • Sharp null: Y1 == Y0 pathwise.

4. Mathematical Specification

Latent covariates: [ Z = (Z_1, Z_2, Z_3, Z_4)^_4(0, I_4) ]

Observed covariates (nonlinear transforms): [ X_1 = (Z_1/2),X_2 = + 10, ] [ X_3 = (Z_1 Z_3 / 25 + 0.6)^3,X_4 = (Z_2 + Z_4 + 20)^2 ]

Propensity in latent space: [ p(Z) = (-Z_1 + 0.5 Z_2 - 0.25 Z_3 - 0.1 Z_4) ]

Outcome in latent space: [ _0(Z) = 210 + 27.4 Z_1 + 13.7 Z_2 + 13.7 Z_3 + 13.7 Z_4 ]

Sharp null effect: [ (Z) ,Y_1 = Y_0 ]

Noise: [ (0, 1^2) ]

5. Oracle Truth Definition

  • True ATT is exactly 0 (sharp null).
  • True QST is identically 0 on the oracle tau grid.

6. Visual Diagnostics (n = 5000)

7. Empirical Validation

Oracle should be zero. IPW is expected to be noisy or biased under misspecification.

Empirical validation (n=1000, seeds=1:20)
estimator_id mean_bias rmse
ipw_att -6.871 7.052
oracle_att 0.000 0.000

Interpretation: The oracle remains at zero as expected. IPW can show erratic behavior because the propensity model is slightly misspecified in the observed covariate space, producing unstable weights.

8. Failure Mode Summary

  • Weight Explosion: IPW estimators will likely produce extreme weights due to the misspecified propensity fit, leading to massive variance.
  • Model Dependence: If the estimator cannot rely on the outcome model to correct the propensity errors, bias will be significant.

9. Implementation Reference

  • Generator: R/dgp-synth-placebo-kangschafer.R
  • Registry: cs_dgp_registry() entry for synth_placebo_kangschafer
  • Oracle truth: cs_get_oracle_qst("synth_placebo_kangschafer", version = "1.4.0")

10. Validation Checklist

11. Changelog

  • v1.4.0 (2026-01-06): Revised dossier with Kang-Schafer citation and misspecification narrative.

Appendix: Implementation

Source code for: dgp_synth_placebo_kangschafer_v140

function (n, seed = NULL, include_truth = TRUE, oracle_only = FALSE) 
{
    if (!is.null(seed)) {
        cs_set_rng(seed)
    }
    Z1 <- stats::rnorm(n, mean = 0, sd = 1)
    Z2 <- stats::rnorm(n, mean = 0, sd = 1)
    Z3 <- stats::rnorm(n, mean = 0, sd = 1)
    Z4 <- stats::rnorm(n, mean = 0, sd = 1)
    X1 <- exp(Z1/2)
    X2 <- Z2/(1 + exp(Z1)) + 10
    X3 <- (Z1 * Z3/25 + 0.6)^3
    X4 <- (Z2 + Z4 + 20)^2
    lin_ps <- -Z1 + 0.5 * Z2 - 0.25 * Z3 - 0.1 * Z4
    p <- stats::plogis(lin_ps)
    w <- stats::rbinom(n, size = 1L, prob = p)
    mu0 <- 210 + 27.4 * Z1 + 13.7 * Z2 + 13.7 * Z3 + 13.7 * Z4
    eps <- stats::rnorm(n, mean = 0, sd = 1)
    y0 <- mu0 + eps
    y1 <- y0
    if (isTRUE(oracle_only)) {
        return(list(df = tibble::tibble(w = w, y0 = y0, y1 = y1)))
    }
    y <- ifelse(w == 1L, y1, y0)
    tau <- rep(0, n)
    true_att <- cs_true_att(structural_te = tau, w = w)
    true_qst <- if (isTRUE(include_truth)) 
        cs_get_oracle_qst("synth_placebo_kangschafer", version = "1.4.0")
    else NULL
    out <- list(df = tibble::tibble(y = y, w = w, y0 = y0, y1 = y1, 
        p = p, structural_te = tau, X1 = X1, X2 = X2, X3 = X3, 
        X4 = X4), true_att = true_att, true_qst = true_qst, meta = list(dgp_id = "synth_placebo_kangschafer", 
        version = "1.4.0", type = "synthetic", params = list(n = n, 
            seed = seed), structural_te = tau))
    cs_check_dgp_synthetic(out)
    out
}
<bytecode: 0x564437c8d368>
<environment: namespace:CausalStress>