SCMO — Synthetic Control with Multiple Outcomes (Tian et al. 2026; Sun et al. 2025)#
- Estimator:
Synthetic Control with Multiple Outcomes (SCMO) —
mlsynth.SCMO- Source:
Tian, W., Lee, S., & Panchenko, V. (2026), “Synthetic controls with multiple outcomes,” Econometrics Journal (the concatenated variant); and Sun, L., Ben-Michael, E., & Feller, A. (2025), “Using Multiple Outcomes to Improve the Synthetic Control Method,” Review of Economics and Statistics (the averaged variant).
- Replication type:
Path A — Tian et al.’s German-reunification balance table reproduced cell by cell — and Path B — the concatenated simulation (Tian Table 1, also the Sun et al.
Simulation1.Routput) and the averaged regime contrast (Sun et al. Appendix D).- Status:
Verified — empirical balance and both simulations reproduced.
Validation strategy#
The two SCMO papers share the German-reunification illustration and tell the
same story from two angles: matching the synthetic control on several related
outcomes — rather than a single long outcome trajectory — sharpens the
identification of the latent factors and reduces post-treatment bias. The
concatenated scheme (Tian-Lee-Panchenko, following the same stacking as Sun
et al.) stacks the standardized pre-period outcomes; the averaged scheme
(Sun-Ben-Michael-Feller) matches their per-period average, which cancels
idiosyncratic noise when the outcomes share a common factor.
Path A — German reunification balance (Tian et al. Table 2)#
On basedata/germany_augmented.csv (West Germany + 16 OECD donors), SCMO
matches West Germany to the donors on nine economic indicators in the single
year 1989. The fitted synthetic West Germany reproduces Tian et al.’s printed
1989 balance table cell by cell — for the concatenated (multiple-outcomes)
synthetic, reconstructed from res.donor_weights:
Outcome (1989) |
West Germany |
Synthetic (multiple) |
mlsynth |
|---|---|---|---|
GDP per capita |
18994.0 |
19029.8 |
19029.8 |
CPI |
2.8 |
3.1 |
3.06 |
Trade openness |
57.7 |
59.1 |
59.06 |
Total tax revenue |
36.2 |
34.1 |
34.07 |
Real GDP growth |
3.9 |
4.1 |
4.12 |
The single-outcome synthetic reproduces the “Synthetic (single outcome)” column
(1989 GDP per capita \(19075.9\)). The concatenated SC — fit on one year’s
nine indicators, never shown the GDP path — tracks West Germany’s pre-1990 GDP to
a root-mean-squared error of \(110\) (vs. \(74\) for the conventional SC
fit directly to 30 years of GDP). The post-1990 effect is reported only
graphically in the paper (Tian et al. Figure 1), so no ATT number is asserted
against the paper; mlsynth’s deterministic ATTs (concatenated \(-1463\),
averaged \(-1720\)) ride along as regression guards. Durable case:
scmo_germany.
Path B — concatenated simulation (Tian et al. Table 1)#
Tian et al.’s Section-3 factor model — identical to the Sun et al. replication
package’s Simulation1.R — draws \(N = 30\) units whose outcomes share the
unit predictors. As the number of related outcomes \(K\) grows, the
post-treatment bias falls while the pre-treatment fit rises toward the
true noise floor (rather than overfitting to near-zero). mlsynth reproduces all
nine cells of Table 1 across \(T_0 \in \{1, 5, 10\}\) and
\(K \in \{1, 5, 10\}\) (e.g. at \(T_0 = 5\) the bias is
\(1.21 / 1.04 / 1.00\), pre-fit \(0.46 / 0.95 / 1.02\)), at
\(M = 250\) draws (the paper uses 5,000). The DGP lives in
mlsynth.utils.scmo_helpers.simulation.simulate_tian(). Durable case:
scmo_concatenated_mc.
Path B — averaged regime contrast (Sun et al. Appendix D)#
Sun et al. report their Monte Carlo as box plots (Figures D.1, D.2), so the
benchmark matches the published geometry rather than numeric cells. Under a
common factor shared across outcomes (rho = 1), the multi-outcome schemes
beat the separate single-outcome SC and averaging reduces bias; under purely
idiosyncratic factors (rho = 0), the outcomes share no signal and
averaging hurts (the separate SC is best). mlsynth reproduces this
common-vs-idiosyncratic adaptivity (common \(T_0 = 10, K = 10\): averaged
bias \(0.91\) < separate \(1.03\); idiosyncratic: averaged \(1.23\)
> separate \(1.00\)). The DGP lives in
mlsynth.utils.scmo_helpers.simulation.simulate_sun(). Durable case:
scmo_averaged_mc.