Changelog
Changelog
v0.3.0 (2026-10-16)
scspill’s second model: method="cd", Cao & Dowd’s synthetic control under a known spillover structure, behind the same configuration and result type as sar, with new case studies and articles.
New model: cd (Cao & Dowd)
Contributed by Hayato Tagawa, and restructured to the package’s architecture.
method="cd"implements Cao and Dowd, “Estimation and Inference for Synthetic Control Methods with Spillover Effects” (arXiv:1902.07343v3). It fits an intercept-adjusted simplex SCM for every unit and takes a known exposure structure:affected_units(one effect per exposed control), or a labeledeffect_matrixwithspillover_structure="custom". It reports period-specific direct and spillover effects (effects_panel), pointwise intervals and P values (effect_ci,pointwise_p_values), Andrews-type P tests of anyC α_t = d(test_hypothesis), and theκ_Aspecification test of the structure (kappa_test).- The defaults are the paper’s Section 4 definitions. Critical values are the left inverse of the empirical CDF, intervals invert the squared P test, and the references come from the full-sample fit. This is the same rule
sarfollows: paper-correct defaults, with the reference implementation’s behaviour behind switches. The authors’ Proposition 99 application code differs in two places.quantile_method="linear"andci_method="published"reproduce it, andreference_mode="leave_one_period_out"refits every SCM equation without each pre-treatment period. - One configuration, one result type for every model.
cdis selected withSCSPILLConfig(method="cd"), and its fields carry a(method='cd')prefix assar’s carry(method='sar'). Setting a field of the model you did not select is an error that names the field. A fit returnsSCSPILLResultswith the model’s own fit in a typed slot (result.sarorresult.cd). The shared surface —att,counterfactual,gap,donor_weights,effects_detail,plot()— works for both. Model-only accessors raiseAttributeErroron the other model. - Exposure structures from the paper.
spillover_structuretakes:"per_unit": one effect per exposed control (Example 1);"homogeneous": one spillover shared by theaffected_units(Example 2);"distance_decay": a spillover ofb·exp(-d_i), with distances inunit_distances(Example 3);"custom": a labeledeffect_matrix.
- Several treated units with a common start date (v3 Section S.1.2). Each gets its own direct effect. The headline surface (
att,counterfactual, plots) follows the first treated unit in label order;att_by_unitandeffects_panelcover all of them. weighting="efficient"adds the lower-variance estimator of v3 Section S.1.1, which weights the residual equations by the inverse of their estimated covariance. It is reported alongside the headline fit asresult.cd.efficient. A warning flags when there are too few pre-treatment periods to estimate that covariance.- Pure-donor sensitivity (v3 Section 5.2.2).
result.cd.sensitivityholds the worst-case bias of the direct effect whenpassumed-clean controls in fact receive spillovers, for the spillover-aware estimator and for the pure-donor synthetic control. The newsensitivityplot kind draws it, and theweightsplot compares against the pure-donor weights. - Structure selection by
κ_A(v3 Sections 5.1.2 and S.1.3).result.select_structure_by_kappa(candidates)compares candidate structures on the same SCM fit. The default statistic is the paper’s multi-periodκ_A, the norm of the summed residual vectors over√T1.statistic="mean_norm"uses mlsynth’s mean of the per-period norms instead. - Cross-checked against mlsynth.
scspill/tests/test_cd_mlsynth_parity.pycompares every feature with mlsynth’sSPILLSYNTH(method="cd")on frozen fixtures (benchmarks/reference/mlsynth_cd/, written bytools/freeze_mlsynth_cd_reference.py). SCM weights agree to the difference between the two solvers, and P values agree exactly. - Each treated unit’s row of a custom
effect_matrixmust be its own direct-effect indicator, so a spillover column cannot leak into the headline effect.affected_unitsmay not list the treated unit. - Plots.
panel,full,effect,spill_topandweightswork forcd. Newcdkinds:spillover(every exposed unit’s effect with its interval) andkappa(the structure test by period). - Data.
load_california_cd(51)andload_california_cd(41)bundle the authors’ 50-states-plus-DC panel, treated from 1989, as aSpillPanelwith the paper’s exposed states.scspill/tests/test_cd.pychecks both fits against the authors’ frozen output (jcao0/synthetic-control-spillover, commit86004020), and their simulation code’s statistics, stored inbenchmarks/reference/cao_dowd/.
Documentation
- The website’s top bar is reorganized: Home, Get started, Models, Articles, Reference, For AI / LLMs, Changelog. The two case studies (Proposition 99 and the Sudan secession) are listed under Articles, and their URLs are unchanged.
- New article, “The
cdmodel in practice” (docs/articles/cd-results.qmd). It covers usage, the Proposition 99 results, and four disagreements:cdvssar, the paper vs the authors’ code, the structure test rejecting the paper’s exposure set, and the structure-selection statistic vs mlsynth’s. To separate model from sample, it fits both models on all three samples, using rook-contiguity weights for the 51-unit panel built from the same Census source as the bundled ones (docs/articles/data/cd_rook_W.csv,tools/build_cd_contiguity.py).
Changed
- Panel preparation is shared by every model (
scspill_helpers.setup.prepare_panel_inputs). It now also rejects, forsartoo, duplicate unit-period rows and a treatment indicator that is not 0/1. - The containers moved next to the code that fills them.
PanelInputsandSCSPILLEffectsare inscspill_helpers.containers.- The
sarinputs, posterior blocks and fit are inscspill_helpers.sar.structures(SARInputs,AlphaPosterior,SARPosterior,SARFit). - The old import paths still work, and
SCSPILLInputsandSCSPILLFitremain as aliases.
SCSPILLEffectsis keyword-only. Itssar-only fields (att_draws,att_plugin,propagate_alpha,n_draws_used) are optional, andinterval_kindrecords how the bands were built.SpillPanel.spatial_wandspatial_Wmay beNone.SpillPanel.config_kwargs(method=None)configuressarwhen the panel has spatial weights andcdotherwise.
v0.2.1 (2026-07-28)
Authorship metadata only; no functional change.
- Carlos Mendez is the sole author of the software, in
CITATION.cffand in the package metadata. Shosei Sakaguchi and Hayato Tagawa authored the method and the R/C++ implementation thesarmodel ports, and they are credited where that belongs: as the authors of the article and of the replication package, both listed underreferencesinCITATION.cff, and in the acknowledgements. Cite their article whenever you fitsar. LICENSEis unchanged and still carries their copyright notice. That is a condition of reusing their MIT-licensed code, and is independent of who authored this package.
v0.2.0 (2026-07-28)
scspill is now presented as what it is becoming: a package of synthetic control models with spillover effects, of which Sakaguchi & Tagawa’s is the first and, today, the only one. Estimation is unchanged — same samplers, same numbers, same public API.
SCSPILLConfiggainsmethod, aLiteral["sar"]defaulting to the only implemented model, so existing code is unaffected. Results carry the value asSCSPILLResults.method, andmethod_details.method_nameis now"SCSPILL/sar". Models added later become a new value plus a subpackage rather than a new class. The literal deliberately lists only what exists: the roadmap names are rejected, not silently accepted.- Internal layout separates the shared spillover layer from the model. The
sarmodel’s samplers, kernels, identification formulas, inference assembly and pipeline moved toscspill/utils/scspill_helpers/sar/; the MCMC diagnostics and classical-SCM baseline moved up toscspill/utils/, and the spatial-weight primitives to a newscspill/utils/spatial.py. The shared root now mirrors mlsynth’s equivalent level exactly. Public imports are unaffected; code importing private helper paths must follow the move. - Documentation gains a Models section — a catalogue with a clearly labelled roadmap (Cao & Dowd, the inclusive SCM of Di Stefano & Mellace, and Grossi et al.’s partial-interference SCG, none implemented) and a page per model. The method article is now the
sarmodel’s page; its published URL still resolves. - Fixes a latent crash: the default plot filename is derived from
method_name, so a name containing/madesave=Truewrite into a directory that does not exist. The slug is now sanitized.
v0.1.1 (2026-07-28)
Attribution and citation metadata only — no functional change to the estimator, and results are bit-for-bit identical to v0.1.0.
- The software’s citation title is now Synthetic Control Models with Spillovers in Python, and Shosei Sakaguchi and Hayato Tagawa are credited as co-authors alongside Carlos Mendez, in
CITATION.cffand in the package metadata. They authored the method and the R/C++ implementation this package ports; the Python implementation is Carlos Mendez’s. LICENSEnow retains the original implementation’s MIT copyright notice (Shosei Sakaguchi and Hayato Tagawa) alongside this package’s.- Citations throughout the README, documentation, and module docstrings now carry the article’s year (2026) and DOI (
10.1093/ectj/utag006), andCITATION.cffadditionally references the replication package (10.5281/zenodo.19066186). - The documentation site gained a “Citing” section, which it previously lacked entirely.
v0.1.0 (2026-07-28)
Initial release: a Python implementation of the Bayesian spatial-spillover synthetic control of Sakaguchi & Tagawa, architecturally aligned with mlsynth.
SCSPILL(config).fit() -> SCSPILLResults: two-step sampler (horseshoe synthetic weights via Makalic–Schmidt Gibbs; SAR block with AR(1) latent factors, horseshoe or ridge covariate priors, and adaptive random-walk Metropolis for the spillover intensity), effects via the identification formulas with an eigendecomposition + Sherman–Morrison fast path, credible bands, a time-by-donor spillover panel, MCMC diagnostics, and R-parity plot kinds.- Paper-correct defaults with R-compatibility escape hatches: proper covariate arrays (the R replication package’s fits received scrambled covariates), horseshoe on the covariate coefficients (
beta_prior), paired posterior draws in the effect intervals (propagate_alpha), and burn-in step adaptation for the Metropolis step (adapt_rho). - A coherent latent-factor block: the Geweke joint distribution test caught two mutually inconsistent conditionals in the reference implementation (the omega parametrization and the FFBS initialization); both follow the paper’s parametrization here, and every isolated sampler block passes the test.
scspill.validation: the Geweke joint distribution test (simplified and production kernels), prior-sensitivity grids, and prior predictive checks whose nine statistics are pinned to the R package’s frozen California table to three decimals.scspill.simulate: the paper’s Monte Carlo engine (rook-lattice SAR DGP, SCM/BSCM/SCSPILL comparison, the Tables 1-2 grid) with a loader for the frozen R results.scspill.data: the bundled California Proposition 99 and Sudan secession case studies with label-aligned spatial weights.- Optional numba backend compiling the same kernel source (~10x faster sampling); single-threaded BLAS inside the samplers for reproducibility.
- Cross-validation benchmarks against the frozen R results (
benchmarks/run_benchmarks.py).