Synthetic control and experimental-design estimators for causal inference on panel data.
Project description
mlsynth
mlsynth is a Python framework for synthetic control causal inference and
synthetic-control-based experimental design. It bundles 46 modern estimators
under a single typed Config / .fit() / typed-results interface, so swapping
between, say, Forward DiD, TASC, and SPCD is a one-line change.
Documentation · Which estimator should I use? (decision tree)
Install
pip install -U git+https://github.com/jgreathouse9/mlsynth.git
mlsynth supports Python 3.9 and later. The base install pulls in every core
dependency and runs every estimator except two that lean on heavier, specialised
backends. Those two backends are packaged as optional extras, so you only
install the weight you actually use:
| Extra | Adds | Needed for |
|---|---|---|
design |
pyscipopt (the SCIP mixed-integer solver) |
the experimental-design estimators SYNDES and MAREX, whose market-selection step is a MIQP |
bayes |
numpyro (JAX-based MCMC) |
SPOTSYNTH's Bayesian synthetic-control mode |
all |
both of the above | the full feature set |
Request an extra with the usual bracket syntax (quote it so the shell does not glob the brackets):
# SCIP solver for SYNDES / MAREX
pip install -U "mlsynth[design] @ git+https://github.com/jgreathouse9/mlsynth.git"
# NumPyro for SPOTSYNTH's Bayesian mode
pip install -U "mlsynth[bayes] @ git+https://github.com/jgreathouse9/mlsynth.git"
# everything
pip install -U "mlsynth[all] @ git+https://github.com/jgreathouse9/mlsynth.git"
A few nuances worth knowing:
- The two extra backends are imported lazily, so
import mlsynthand importing any estimator class always works on the base install. The extra is consulted only when you actually call the design optimiser (SYNDES/MAREX) orSPOTSYNTH's Bayesian path; without it those raise a clear error pointing you at the missing package, while everything else runs unchanged. pyscipoptships prebuilt wheels (bundling SCIP) for the common platforms, somlsynth[design]is normally a plainpip installwith no separate SCIP system install.- The test suite is a development artifact and is not shipped in the installed
package — clone the repository if you want to run
pytest.
Quickstart
import numpy as np
import pandas as pd
from mlsynth import FDID
rng = np.random.default_rng(0)
N, T1, T2 = 60, 24, 12 # 60 controls, 24 pre, 12 post
T = T1 + T2
def factors(T, rng, burn=200): # f1: AR(1); f2: ARMA(1,1); f3: MA(2)
Tt = T + burn; v = rng.standard_normal((Tt, 3)); f = np.zeros((Tt, 3))
for t in range(1, Tt): f[t, 0] = 0.8 * f[t-1, 0] + v[t, 0]
for t in range(1, Tt): f[t, 1] = -0.6 * f[t-1, 1] + v[t, 1] + 0.8 * v[t-1, 1]
f[1, 2] = v[1, 2] + 0.9 * v[0, 2]
for t in range(2, Tt): f[t, 2] = v[t, 2] + 0.9 * v[t-1, 2] + 0.4 * v[t-2, 2]
f[0, 2] = v[0, 2]
return f[burn:]
sf = factors(T, rng).sum(1) # common factor path
y_tr = 1 + sf + rng.standard_normal(T) # treated: loading 1, true ATT = 0
loads = np.where(np.arange(N) < N // 2, 1.0, 2.0) # first 30 match, last 30 mismatch
Y = 1 + np.outer(sf, loads) + rng.standard_normal((T, N))
rows = [{"unit": "treated", "time": t, "gdp": y_tr[t], "treat": int(t >= T1)}
for t in range(T)]
for j in range(N):
rows += [{"unit": f"c{j}", "time": t, "gdp": Y[t, j], "treat": 0} for t in range(T)]
df = pd.DataFrame(rows)
res = FDID({"df": df, "outcome": "gdp", "treat": "treat",
"unitid": "unit", "time": "time", "display_graphs": False}).fit()
sel = res.fdid.selected_names
matching = sum(int(s[1:]) < N // 2 for s in sel)
print(f"FDID: ATT={res.fdid.att:+.3f} R2={res.fdid.r_squared:.3f} "
f"selected {len(sel)} donors, {matching} from the matching group")
print(f"DID : ATT={res.did.att:+.3f} R2={res.did.r_squared:.3f} (all {N} donors)")
The same five df/outcome/unitid/time/treat fields work for every
estimator. Swap FDID for TASC, CLUSTERSC, BVSS, or any other class in the
table below; only the class name and any estimator-specific hyperparameters
change.
Estimators
mlsynth implements 46 estimator classes spanning the full synthetic-control
landscape. Several classes expose multiple methods through one configuration
(e.g. PDA covers four donor-selection / relaxation variants; RESCM covers
relaxed- and $L_\infty$-balanced variants; PROXIMAL dispatches several
proximal estimators). Each Class below links to its documentation page; the
estimator name links to the source paper. Not sure which one fits your problem?
Walk the decision tree.
Canonical & convex-hull
| Estimator | Reference | Class |
|---|---|---|
| Synthetic Control Method (vanilla SCM) | Abadie & Gardeazabal (2003); Abadie, Diamond & Hainmueller (2010), JASA 105(490):493–505 | VanillaSC |
| Two-Step Synthetic Control | Li & Shankar (2024), Management Science 70(6):3734–3755 | TSSC |
| Modified Unbiased Synthetic Control | Bottmer, Imbens, Spiess & Warnick (2024), JBES 42(2):762–773 | MUSC |
| Matching & Synthetic Control (MASC) | Kellogg, Mogstad, Pouliot & Torgovitsky (2021), JASA | MASC |
Donor selection / forward
| Estimator | Reference | Class |
|---|---|---|
| Forward Difference-in-Differences | Li (2024), Marketing Science 43(2):267–279 | FDID |
| Optimal Initial Donor Selection (FSCM) | Cerulli (2024), Economics Letters 244:111976 | FSCM |
| Panel Data Approach (HCW) | Hsiao, Ching & Wan (2012), J. Applied Econometrics | PDA |
| L1-PDA | Li & Bell (2017), J. Econometrics 197(1):65–75 | PDA |
| Forward-Selected Panel Data Approach | Shi & Huang (2023), J. Econometrics 234(2):512–535 | PDA |
| L2-Relaxation | Shi & Wang (2024) | PDA |
High-dimensional / robust / relaxed-hull
| Estimator | Reference | Class |
|---|---|---|
| Principal Component Regression SC | Agarwal et al. (2021), JASA 116(536); Amjad, Shah & Shen (2018) | CLUSTERSC |
| Robust PCA Synthetic Control | Bayani (2022), CUNY Academic Works | CLUSTERSC |
| CLUSTERSC (donor clustering) | Rho, Tang, Bergam, Cummings & Misra (2024), arXiv:2503.21629 | CLUSTERSC |
| Sparse Synthetic Control (L1 predictor selection) | Vives-i-Bastida (2023), Predictor Selection for Synthetic Controls | SparseSC |
| Multivariate Square-root Lasso SC | Shen, Song & Abadie | MSQRT |
| Relaxed Balanced Synthetic Control | Liao, Shi & Zheng (2025), arXiv:2508.01793 | RESCM |
| $L_\infty$ Synthetic Control | Wang, Xing & Ye (2025), arXiv:2510.26053 | RESCM |
Factor / time-series / Bayesian
| Estimator | Reference | Class |
|---|---|---|
| Factor Model Approach | Li & Sonnier (2023), JMR 60(3):449–472 | FMA |
| Harmonic Synthetic Control | Liu & Xu (2026), The Harmonic Synthetic Control Method | HSC |
| Time-Aware Synthetic Control | Rho, Illick, Narasipura, Abadie, Hsu & Misra (2026), arXiv:2601.03099 | TASC |
| Synthetic Business Cycle | Shi, Xi & Xie (2025), arXiv:2505.22388 | SBC |
| Bayesian SC with Soft Simplex Constraint | Xu & Zhou (2025), arXiv:2503.06454 | BVSS |
| Dynamic SC for Auto-Regressive Processes | Zheng & Chen (2024), JRSS-B 86(1):155–176 | DSCAR |
SDID family / staggered adoption
| Estimator | Reference | Class |
|---|---|---|
| Synthetic Difference-in-Differences | Arkhangelsky, Athey, Hirshberg, Imbens & Wager (2021), AER 111(12):4088–4118 | SDID |
| Sequential Synthetic Difference-in-Differences | Arkhangelsky & Samkov (2025), arXiv:2404.00164 | SequentialSDID |
| Partially Pooled SCM (staggered) | Ben-Michael, Feller & Rothstein (2022), JRSS-B 84(2):351–381 | PPSCM |
| Staggered Synthetic Control | Cao, Lu & Wu | SSC |
| Rolling-Transformation DiD | Lee & Wooldridge (2026), J. Applied Econometrics | ROLLDID |
Spillover / interference (SUTVA)
| Estimator | Reference | Class |
|---|---|---|
| SCM with Spillover Effects | Cao & Dowd (2023) | SPILLSYNTH |
| Spatial Synthetic Difference-in-Differences | Serenini & Masek (2024), SSRN 4736857 | SpSyDiD |
| Imperfect Synthetic Controls | Powell (2026), J. Applied Econometrics 41(3):253–264 | ISCM |
| Spillover-Detecting Synthetic Control | Gilligan-Lee (2025); Zeitler et al. (2023) | SPOTSYNTH |
Multiple outcomes / interventions / proximal
| Estimator | Reference | Class |
|---|---|---|
| SCM with Multiple Outcomes | Tian, Lee & Panchenko (2023), arXiv:2304.02272 | SCMO |
| Synthetic Interventions | Agarwal, Shah & Shen (2026), Operations Research | SI |
| Proximal SCM Framework | Shi, Li, Miao, Hu & Tchetgen Tchetgen (2023), arXiv:2108.13935 | PROXIMAL |
| Proximal SC with Surrogates | Liu, Tchetgen Tchetgen & Varjão (2023), arXiv:2308.09527 | PROXIMAL |
| Single Proxy Synthetic Control | Park & Tchetgen Tchetgen (2025), J. Causal Inference | PROXIMAL |
| Doubly Robust Proximal Synthetic Controls | Qiu, Shi, Miao, Dobriban & Tchetgen Tchetgen (2024), Biometrics 80(2) | PROXIMAL |
Matrix completion / missing data
| Estimator | Reference | Class |
|---|---|---|
| Matrix Completion with Nuclear Norm Minimization | Athey, Bayati, Doudchenko, Imbens & Khosravi (2021), JASA 116(536) | MCNNM |
| Synthetic Nearest Neighbors / Causal Matrix Completion | Agarwal, Dahleh, Shah & Shen (2021), arXiv:2109.15154 | SNN |
| Robust Matrix estimation with Side Information | Agarwal, Choi & Yuan | RMSI |
Distributional / nonlinear / continuous / IV / micro
| Estimator | Reference | Class |
|---|---|---|
| Distributional Synthetic Controls | Gunsilius (2023), Econometrica 91(3):1105–1117 | DSC |
| SCM with Nonlinear Outcomes | Tian (2023), arXiv:2306.01967 | NSC |
| Continuous-Treatment Synthetic Control | Powell (2022), JBES 40(3):1302–1314 | CTSC |
| Synthetic IV Estimation in Panels | Gulek & Vives-i-Bastida (2024), Job Market Paper | SIV |
| Micro-level Balancing Synthetic Control | Robbins & Davenport (2021), J. Statistical Software 97(2) | MicroSynth |
| Synthetic Control with Disaggregated Data | Bottmer (2026), Stanford job-market paper | MLSC |
| Synthetic Historical Control | Chen, Yang & Yang (2024), SSRN 4995085 | SHC |
Experimental design & geo-testing
| Estimator | Reference | Class |
|---|---|---|
| Synthetic Controls for Experimental Design | Abadie & Zhao (2026) | MAREX |
| Synthetic Design (optimization approach) | Doudchenko, Khosravi, Pouget-Abadie, Lahaie, Lubin, Mirrokni, Spiess & Imbens (2021) | SYNDES |
| Lexicographic Synthetic Control (validity → power) | Abadie & Zhao (2026); Vives-i-Bastida (2022) | LEXSCM |
| Synthetic Principal Component Design | Lu, Li, Ying & Blanchet (2022), arXiv:2211.15241 | SPCD |
| Parallel-Trends Supergeo Design | mlsynth (PANGEO), extending Supergeo Design — Chen, Doudchenko, Jiang, Stein & Ying (2023) |
PANGEO |
| GeoLift Market Selection | mlsynth (GeoLift); conformal design — Ben-Michael, Feller & Rothstein (2021); Chernozhukov, Wüthrich & Zhu (2021) | GEOLIFT |
| Multi-cell GeoLift Analysis | mlsynth; multi-cell extension of GeoLift | MULTICELLGEOLIFT |
Contributing
Fixes are always welcome. For new estimators, inference tests, or larger changes, please email Jared first. Estimators currently on the development list include continuous-treatment synthetic controls, Bayesian factor SCMs, random-forest-based SCMs, simplex-weight inference, and prediction-interval refinements.
Whatever change is proposed, it must reproduce either a canonical benchmark application from the SCM literature (Basque Country, California Proposition 99, or West Germany reunification) or the empirical findings reported in the originating methodological paper. Continuous integration runs the unit test suite, a fresh-install smoke check, and coverage reporting on every pull request.
In addition to code, you can also develop tutorials, presentations, and
educational materials using mlsynth, promote it on LinkedIn or in the
classroom, and help with outreach and onboarding new contributors.
License
mlsynth is open source and distributed under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mlsynth-1.0.0.tar.gz.
File metadata
- Download URL: mlsynth-1.0.0.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7e7338851bf37191b9f02f475d7eec76b79e8bfee0a73329ac871175a8f6fcd
|
|
| MD5 |
6efbe333ee668c6332311ce8304fc60d
|
|
| BLAKE2b-256 |
2de8776aa9c247a309a795b72154afc552f2e31bcf64407bbb105d56c9ec9b03
|
Provenance
The following attestation bundles were made for mlsynth-1.0.0.tar.gz:
Publisher:
release.yml on jgreathouse9/mlsynth
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mlsynth-1.0.0.tar.gz -
Subject digest:
a7e7338851bf37191b9f02f475d7eec76b79e8bfee0a73329ac871175a8f6fcd - Sigstore transparency entry: 1884158200
- Sigstore integration time:
-
Permalink:
jgreathouse9/mlsynth@24f1b66c1b99e60c881dd84384bc459a4b3dab8e -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/jgreathouse9
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@24f1b66c1b99e60c881dd84384bc459a4b3dab8e -
Trigger Event:
release
-
Statement type:
File details
Details for the file mlsynth-1.0.0-py3-none-any.whl.
File metadata
- Download URL: mlsynth-1.0.0-py3-none-any.whl
- Upload date:
- Size: 1.4 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90bd26fee339aae0f37f98c790174caa77a6e1f4cde2e4979f1071be8c6bd3c8
|
|
| MD5 |
c8ec3876bcc6e49ef69a41aa48d94476
|
|
| BLAKE2b-256 |
de409b9cc04f4a6e36866471f2ef54a02ed024c0993f8f245c4f98971ead9748
|
Provenance
The following attestation bundles were made for mlsynth-1.0.0-py3-none-any.whl:
Publisher:
release.yml on jgreathouse9/mlsynth
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mlsynth-1.0.0-py3-none-any.whl -
Subject digest:
90bd26fee339aae0f37f98c790174caa77a6e1f4cde2e4979f1071be8c6bd3c8 - Sigstore transparency entry: 1884158379
- Sigstore integration time:
-
Permalink:
jgreathouse9/mlsynth@24f1b66c1b99e60c881dd84384bc459a4b3dab8e -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/jgreathouse9
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@24f1b66c1b99e60c881dd84384bc459a4b3dab8e -
Trigger Event:
release
-
Statement type: