DGP Dossier: synth_heavytail

1. Identity & Status

DGP ID synth_heavytail
Version 1.6.0
Status stable
Difficulty (3/5)
Stress Profile  overlap: moderatenoise: heavylinearity: lineareffect: lineartarget: both

2. What This DGP Stresses (Intent)

Purpose: Penalize estimators that rely on L2 loss under heavy-tailed noise.

This DGP is identical to the baseline linear design except for the outcome noise, which is a Gaussian/Cauchy mixture. The Cauchy component makes the population mean of observed outcomes undefined. Consequently, a conventional superpopulation mean potential-outcome ATT does not exist, and estimators that rely on L2 loss or outcome means do not converge to such an estimand.

CausalStress deliberately keeps running ATT estimators because their breakdown is the evidence this boundary DGP is designed to produce. The package’s true_att is a governed finite-sample structural signal anchor, not a conventional superpopulation mean potential-outcome ATT. It must not be used for an ATT estimator shootout. QST is the well-defined distributional comparison target in this regime.

3. Identification Assumptions (Explicit)

  • Unconfoundedness holds by construction (treatment is assigned by a known propensity score).
  • Overlap is moderate (p(X) bounded away from 0 and 1 for most of the mass).
  • The governed structural signal anchor is the finite-sample mean of tau(X_i) over the run’s treated units (Constitution Article I), independent of realized noise. Because the population outcome means do not exist, this is not a conventional superpopulation mean potential-outcome ATT.

4. Mathematical Specification

Covariates [ X (0, I_5) ]

Propensity [ p(X) = (0.5 X_1 - 0.5 X_2) ]

Baseline Outcome and Treatment Effect [ _0(X) = 1 + X_1 + 0.5 X_2 ] [ (X) = 1 + 0.5 X_1 ]

Noise Mixture (Heavy Tails) [ 0.8 (0, 0.5^2) + 0.2 (0, 1) ] where (_0) and (_1) are drawn independently.

Moment note: The Cauchy component has undefined mean and variance. Therefore the population mean of realized outcomes is undefined, and any L2/MSE estimator is not well-posed in the population.

Potential Outcomes [ Y_0 = _0(X) + _0, Y_1 = _0(X) + (X) + _1 ]

5. Oracle Truth Definition

  • Finite-sample structural signal anchor (true_att): (N_{treated}^{-1}_{i:W_i=1}(X_i)), computed from the deterministic signal ((X)). This governed diagnostic anchor is finite even though the conventional superpopulation mean potential-outcome ATT is not.
  • QST (Distributional): computed on the canonical tau grid via oracle Monte Carlo as specified in cs_get_oracle_qst().

6. Visual Diagnostics (n = 5000)

7. Empirical Validation

We run light validation with n = 1000 and seeds = 1:20. The goal is to show extreme sensitivity of OLS under L2 loss, not to rank ATT estimators. We report finite-replicate diagnostics relative to the structural signal anchor, centered on medians and a quantile; the sample maximum is descriptive only. These are not conventional ATT bias or RMSE. We do not run heavier robust estimators here to keep doc builds fast.

Finite-replicate diagnostics against the structural signal anchor (n=1000, seeds=1:20)
estimator_id n_runs median_anchor_deviation median_abs_anchor_deviation p90_abs_anchor_deviation max_abs_anchor_deviation
lm_att 20 0.343 0.354 10.699 38.555
oracle_att 20 0.000 0.000 0.000 0.000

Interpretation: OLS exhibits non-convergence and extreme seed sensitivity under the Cauchy mixture. Mean bias, RMSE, conventional coverage, and winner rankings are not defined here. The structural oracle reproduces the governed signal anchor; this confirms scorer consistency, not existence of a conventional mean ATT.

8. Failure Mode Summary

  • L2/mean-based ATT estimators: run them to diagnose non-convergence and extreme sensitivity; do not rank them or report conventional bias/RMSE/coverage.
  • Structural true_att: a governed finite-sample signal anchor for diagnostics, not a conventional superpopulation mean potential-outcome ATT in this no-mean regime.
  • QST/quantile targets: well-defined under the Cauchy mixture and the valid distributional comparison surface.

9. Implementation Reference

  • Generator: R/dgp-synth-heavytail.R
  • Oracle truth: cs_get_oracle_qst("synth_heavytail", version = "1.6.0")
  • Metadata: inst/dgp_meta/synth_heavytail.yml

10. Validation Checklist

11. Changelog

  • v1.3.0 (2026-01-06): Initial dossier for heavy-tail stress DGP.
  • v1.6.0: Implemented Common Random Numbers (CRN) for Oracle generation. This reduces Monte Carlo variance of the QST contrast; it does not eliminate empirical-quantile sampling uncertainty. Also enforces regime consistency (Normal/Cauchy) for counterfactuals during Oracle evaluation.

Appendix: Implementation

Source code for: dgp_synth_heavytail_v160

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)
    if (!isTRUE(oracle_only)) {
        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 <- 1 + X1 + 0.5 * X2
    tau <- 1 + 0.5 * X1
    p <- stats::plogis(0.5 * X1 - 0.5 * X2)
    w <- stats::rbinom(n, size = 1, prob = p)
    mix_ind0 <- stats::rbinom(n, size = 1, prob = 0.8)
    eps0 <- ifelse(mix_ind0 == 1L, stats::rnorm(n, mean = 0, 
        sd = 0.5), stats::rcauchy(n, location = 0, scale = 1))
    if (isTRUE(oracle_only)) {
        eps1 <- eps0
    }
    else {
        mix_ind1 <- stats::rbinom(n, size = 1, prob = 0.8)
        eps1 <- ifelse(mix_ind1 == 1L, stats::rnorm(n, mean = 0, 
            sd = 0.5), stats::rcauchy(n, location = 0, scale = 1))
    }
    y0 <- mu0 + eps0
    y1 <- mu0 + tau + eps1
    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_heavytail", version = "1.6.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_heavytail", version = "1.6.0", 
            type = "synthetic", params = list(n = n, seed = seed), 
            structural_te = tau))
    cs_check_dgp_synthetic(out)
    out
}
<bytecode: 0x55aaf61f6638>
<environment: namespace:CausalStress>