Skip to main content

Synthetic control and experimental-design estimators for causal inference on panel data.

Project description

mlsynth

coverage

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 mlsynth and importing any estimator class always works on the base install. The extra is consulted only when you actually call the design optimiser (SYNDES / MAREX) or SPOTSYNTH's Bayesian path; without it those raise a clear error pointing you at the missing package, while everything else runs unchanged.
  • pyscipopt ships prebuilt wheels (bundling SCIP) for the common platforms, so mlsynth[design] is normally a plain pip install with 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mlsynth-1.0.0.tar.gz (1.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mlsynth-1.0.0-py3-none-any.whl (1.4 MB view details)

Uploaded Python 3

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

Hashes for mlsynth-1.0.0.tar.gz
Algorithm Hash digest
SHA256 a7e7338851bf37191b9f02f475d7eec76b79e8bfee0a73329ac871175a8f6fcd
MD5 6efbe333ee668c6332311ce8304fc60d
BLAKE2b-256 2de8776aa9c247a309a795b72154afc552f2e31bcf64407bbb105d56c9ec9b03

See more details on using hashes here.

Provenance

The following attestation bundles were made for mlsynth-1.0.0.tar.gz:

Publisher: release.yml on jgreathouse9/mlsynth

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for mlsynth-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 90bd26fee339aae0f37f98c790174caa77a6e1f4cde2e4979f1071be8c6bd3c8
MD5 c8ec3876bcc6e49ef69a41aa48d94476
BLAKE2b-256 de409b9cc04f4a6e36866471f2ef54a02ed024c0993f8f245c4f98971ead9748

See more details on using hashes here.

Provenance

The following attestation bundles were made for mlsynth-1.0.0-py3-none-any.whl:

Publisher: release.yml on jgreathouse9/mlsynth

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page