DGP Dossier: synth_placebo_nonlinear

1. Identity & Status

DGP ID synth_placebo_nonlinear
Version 1.3.0
Status experimental
Difficulty (2/5)
Stress Profile  overlap: moderatenoise: gaussianlinearity: smootheffect: constanttarget: both

2. What This DGP Stresses (Intent)

Intent: The “Hallucination Test.” The outcome surface is smooth but highly nonlinear (\(\mu_0(X) = \sin(X_1) + \cos(X_2)\)). There is no treatment effect.

The Scientific Question: “Does the estimator mistake background structure for a signal?” Flexible ML estimators (like BART or Forests) risk overfitting the “wiggles” of the outcome surface and attributing some of that variance to the treatment indicator. This tests for “False Discovery via Overfitting.”

3. Identification Assumptions (Explicit)

  • Selection on observables holds.
  • Overlap is moderate (propensity bounded).
  • SUTVA holds.
  • Sharp Null: \(Y_1 \equiv Y_0\).

4. Mathematical Specification

Covariates: \(X_1, X_2, X_3, X_4, X_5 \sim \mathcal{N}(0, 1^2)\) independently.

Outcome (control): \[\mu_0(X) = \sin(X_1) + \cos(X_2)\]

Propensity: \[p(X) = \text{expit}(0.5 X_1 - 0.5 X_2)\]

Treatment effect (sharp null): \[\tau(X) \equiv 0\]

Noise is Gaussian: \(\varepsilon \sim \mathcal{N}(0, 0.5^2)\) and \(Y_1 \equiv Y_0\) pathwise.

5. Oracle Truth Definition

  • True ATT is zero by construction.
  • True QST is identically zero on the canonical grid.

6. Visual Diagnostics (n = 5000)

7. Empirical Validation

We validate only the oracle estimator to keep builds fast.

Oracle validation (n=1000, seeds=1:20)
mean_bias rmse
0 0

8. Failure Mode Summary

  • Overfitting: ML methods may capture the sine/cosine waves but misattribute residuals to the treatment, resulting in non-zero estimates.
  • Model Misspecification: Linear estimators (OLS/IPW) will fail to fit the surface, potentially leading to bias if the nonlinearity correlates with propensity.

9. Implementation Reference

  • Code: R/dgp-synth-placebo-nonlinear.R (v1.3.0)
  • Registry: cs_dgp_registry() entry for synth_placebo_nonlinear
  • Oracle: true_att = 0, true_qst = 0

10. Validation Checklist

11. Changelog

  • v1.0 dossier: Initial placebo dossier

Appendix: Implementation

Source code for: dgp_synth_placebo_nonlinear_v130

function (n, seed = NULL, include_truth = TRUE, oracle_only = FALSE) 
{
    if (!is.null(seed)) {
        cs_set_rng(seed)
    }
    X1 <- stats::rnorm(n, mean = 0, sd = 1)
    X2 <- stats::rnorm(n, mean = 0, sd = 1)
    X3 <- stats::rnorm(n, mean = 0, sd = 1)
    X4 <- stats::rnorm(n, mean = 0, sd = 1)
    X5 <- stats::rnorm(n, mean = 0, sd = 1)
    mu0 <- sin(X1) + cos(X2)
    tau <- rep(0, n)
    p <- stats::plogis(0.5 * X1 - 0.5 * X2)
    w <- stats::rbinom(n, size = 1L, prob = p)
    eps <- stats::rnorm(n, mean = 0, sd = 0.5)
    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)
    true_att <- cs_true_att(structural_te = tau, w = w)
    true_qst <- if (isTRUE(include_truth)) 
        cs_get_oracle_qst("synth_placebo_nonlinear", version = "1.3.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, X5 = X5), true_att = true_att, true_qst = true_qst, 
        meta = list(dgp_id = "synth_placebo_nonlinear", version = "1.3.0", 
            type = "synthetic", params = list(n = n, seed = seed), 
            structural_te = tau))
    cs_check_dgp_synthetic(out)
    out
}
<bytecode: 0x5651a528e7a8>
<environment: namespace:CausalStress>