DROSC — Basque robustness sweep vs the authors’ R#
- Estimator:
Distributionally Robust Synthetic Control (DROSC) –
mlsynth.DROSC- Source:
Koo, T. & Guo, Z. (2026). “Distributionally Robust Synthetic Control: Ensuring Robustness Against Highly Correlated Controls and Weight Shifts.” arXiv:2511.02632. Reference code: taehyeonkoo/DRoSC (
helpers.R).- Replication type:
cross-validation against the authors’ own R (
limSolve::lsei) run live viaRscript, the deterministic worst-case point estimand.- Status:
verified – the estimand and the donor weights match value-for-value.
- Benchmark:
benchmarks/cases/drosc_basque.py(source).
Why this case exists#
DROSC’s worst-case estimand is defined by an inequality-constrained
optimisation, and the authors solve it with R’s limSolve::lsei. This case
confirms that mlsynth’s cvxpy port targets the same optimisation and returns the
same effect across the robustness-radius sweep, on the paper’s own empirical
application – the Abadie-Gardeazabal Basque Country / ETA-terrorism study
(\(T_0 = 15\) pre-periods, \(N = 16\) donor regions, \(T_1 = 28\)
post-periods).
The robustness sweep#
As the radius \(\lambda\) grows, the compatible-weight set widens and the
effect shrinks from the classical-synthetic-control neighbourhood toward zero.
mlsynth reproduces the authors’ DRoSC estimand at every radius:
The classical synthetic-control ATT on the same outcome-only fit is −0.895; the
effect is no longer distinguishable from zero once the robustness radius reaches
about 0.06. The worst-case estimand matches R to \(\sim 10^{-7}\) at every
radius (the residual is cvxpy-vs-lsei solver noise on the shared optimum).
The donor weights#
At \(\lambda = 0\) the moment band is tightest and the weights are pinned; mlsynth reproduces them by name to five decimals:
Donor |
mlsynth |
R |
|---|---|---|
Madrid |
0.388 |
0.388 |
Baleares |
0.274 |
0.274 |
Cataluna |
0.203 |
0.203 |
Principado De Asturias |
0.135 |
0.135 |
The perturbation union confidence interval (inference=True) is stochastic and
seed-dependent – it agrees with the R interval within Monte-Carlo error but is
not pinned value-for-value – so the benchmark cross-validates the deterministic
estimand, which is exact.
Reproduce#
python benchmarks/run_benchmarks.py --case drosc_basque
The mlsynth side reads basedata/basque_jasa.csv. The reference is the
authors’ own code, run live: benchmarks/reference/drosc_basque/reference.R
clones github.com/taehyeonkoo/DRoSC (cached), sources its unmodified
src/helpers.R, and solves with limSolve::lsei each time the case runs.
The case BenchmarkSkippeds when Rscript / limSolve / the clone is
unavailable, so a missing R toolchain never reds the suite. Provision the solver
with benchmarks/R/install_drosc.sh (limSolve from the GitHub CRAN mirror,
since CRAN is firewalled), then run the reference directly with
Rscript benchmarks/reference/drosc_basque/reference.R basedata/basque_jasa.csv