



| DGP ID | synth_nonlinear_heteroskedastic |
| Version | 1.6.0 |
| Status | experimental |
| Difficulty | ★★★☆☆ (3/5) |
| Stress Profile | overlap: moderatenoise: heteroskedasticlinearity: smootheffect: constanttarget: both |
Monotone nonlinear confounding + severe heteroskedasticity. OLS fails because it fits a plane to a cubic signal and cannot adapt to variance that explodes in the tails of the covariate distribution.
Covariates: [ X_1 (0, 1^2), X_2 (0, 1^2), X_3 [-2, 2], X_4 (0.4) ]
Outcome (control): [ _0(X) = 1 + 0.5 X_1^3 + 1.5 X_2^2 - 1.0 X_4 ]
Treatment Effect: [ (X) ]
Propensity: [ p(X) = (0.5 X_1 - 0.5 X_2) ]
Heteroskedastic noise: [ !(0, ^2(X)),(X) = 0.1 + 1.0(0.5 X_2) ]
structural_te among treated units.cs_get_oracle_qst()).



Under the v1.6.0 cubic/exponential regime (with overlap restored), OLS (lm_att) exhibits significant structural bias. While it avoids the variance explosion of severe overlap violations, the linear model cannot adapt to the cubic outcome surface. In quick validation (n = 1000, seeds = 1:10), lm_att shows mean bias around 0.32 (32% error), while oracle_att remains exact.
| estimator_id | mean_bias | rmse |
|---|---|---|
| lm_att | 0.321 | 0.404 |
| oracle_att | 0.000 | 0.000 |
R/dgp-synth-nonlinear-heteroskedastic.Rcs_dgp_registry() entry for synth_nonlinear_heteroskedasticcs_get_oracle_qst("synth_nonlinear_heteroskedastic", version = "1.6.0")Source code for: dgp_synth_nonlinear_heteroskedastic_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)
X3 <- stats::runif(n, min = -2, max = 2)
X4 <- stats::rbinom(n, size = 1L, prob = 0.4)
mu0 <- 1 + 0.5 * X1^3 + 1.5 * X2^2 - 1 * X4
tau <- rep(1, n)
sigma <- 0.1 + 1 * exp(0.5 * X2)
eps0 <- stats::rnorm(n, mean = 0, sd = sigma)
if (isTRUE(oracle_only)) {
eps1 <- eps0
}
else {
eps1 <- stats::rnorm(n, mean = 0, sd = sigma)
}
p <- stats::plogis(0.5 * X1 - 0.5 * X2)
w <- stats::rbinom(n, size = 1L, prob = p)
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_nonlinear_heteroskedastic",
version = "1.6.0")
}
else {
tibble::tibble(tau = cs_tau_oracle, value = rep(NA_real_,
length(cs_tau_oracle)))
}
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_nonlinear_heteroskedastic",
version = "1.6.0", type = "synthetic", params = list(n = n,
seed = seed), structural_te = tau))
cs_check_dgp_synthetic(out)
out
}
<bytecode: 0x559e6d339d30>
<environment: namespace:CausalStress>