Constrained synthetic control, FE inference conventions, and untreated-only imputation
Published
September 21, 2026
Scope
The initial lecture audit identified a confirmed standalone synthetic-control optimization defect, different fixed-effect inference conventions, and missing causal-panel interfaces. This implementation addresses the solver defect and adds the core FE prediction, imputation, and covariance interfaces. It does not implement the entire new-estimator roadmap.
The standalone estimator now solves the squared-error objective directly on the simplex with an active-set quadratic solver. Zero donor weights are permitted. The old softmax parameterization could yield nearly flat gradients at a suboptimal solution. Returning a finite parameter vector no longer suffices: convergence requires a KKT optimality check, and exhausted iteration budgets raise an error.
For Proposition 99 outcome-only SCM:
Quantity
Before
Corrected
Independent reference
Pre-treatment RMSE
2.399913
1.656400
1.656400
Mean post-treatment gap
−18.998496
−19.513630
−19.513630
The corrected solve takes 34 active-set iterations in the recorded run. summary() exposes the objective, iterations, feasibility error, normalized KKT residual and stopping reason. Tests check boundary optima, redundant donors, donor permutations, common-level shifts, single-donor inputs, failed-refit state, and insufficient iteration budgets. This is outcome-only SCM, not predictor-V SCM.
Fixed-effect regression
FixedEffectsOLS now exposes absorption tolerance and iteration limits, supports FE-only fits, compacts caller-supplied FE IDs, retains level effects, and predicts identified one/two-way FE sums. Unseen levels and combinations crossing disconnected components are rejected. Three-or-more-way level prediction remains unsupported, while slope estimation still supports it.
The default full-FE degrees-of-freedom correction is preserved. New explicit options control FE counting, observation-count correction and cluster-count correction in both summaries and Wald tests. The fatalities example verifies:
Convention
Explicit settings beyond cluster IDs
SE
Existing CrabbyMetrics default
defaults
0.385787
fixest lecture default
fe_df="non_nested"
0.357078
plm HC1 lecture calculation
fe_df="none", ssc="hc1", cluster_correction=False
0.350150
All three retain the coefficient −0.639980. These are deliberate statistical conventions, not three competing numerical answers to one covariance formula.
Native FE counterfactuals
FEImputation uses the same sparse absorption machinery. It fits Y_{it}(0)=\alpha_i+\lambda_t+X_{it}'\beta only on observed positive-weight untreated cells. It accepts missing observations and treatment reversals, returns exact training/evaluation masks, and aggregates weighted observed-treated cell gaps by unit and time. It rejects unsupported FE combinations and nuisance covariates unidentified in the untreated sample.
The five available empirical surfaces are compared with the already-saved R FEct outputs below. No treated outcome enters estimation. This is point estimation, without confidence intervals or causal validation of treatment reversals.
Code
from pathlib import Pathimport osimport jsonimport pandas as pdimport numpy as npimport crabbymetrics as cmroot = Path(os.environ['YQX_REPLICATION_ROOT']) if'YQX_REPLICATION_ROOT'in os.environ elsenext( p /'replications/yqx_panel'for p in [Path.cwd(), *Path.cwd().parents]if (p /'replications/yqx_panel/results/fixes').exists())verification = json.loads((root /'results/fixes/verification.json').read_text())panel_results = pd.read_csv(root /'results/fixes/panels.csv')display(panel_results.loc[panel_results.model !='prop99_scm', ['model', 'att', 'reference_att', 'max_surface_error', 'n_train', 'n_treated']])
model
att
reference_att
max_surface_error
n_train
n_treated
1
gs2020_fe
0.127134
0.127134
2.397409e-10
6729.0
118.0
2
turnout_fe
1.425566
1.425566
2.129233e-08
1078.0
50.0
3
hh2019_fe
1.506416
1.506416
7.074693e-08
15135.0
2459.0
4
modern_Bischof_Wagner_2019_AJPS_full
0.082334
0.082334
7.262205e-07
374.0
160.0
5
modern_Grumbach_Sahn_2020_APSR_full
0.127749
0.127749
2.527977e-10
6729.0
118.0
Bischof’s surface agreement uses a 10^{-6} tolerance because its saved R fit has looser convergence. The other examples agree much more closely. These comparisons do not erase the initial audit’s sample/provenance differences.
Standard tests use original deterministic fixtures generated independently in base R. lm supplies coefficient/counterfactual references; full-design cluster score calculations supply covariance references; exhaustive simplex-face optimization supplies an independent small SCM optimum. The generation script is checked in; routine tests do not require R or a network connection.
Optional empirical tests replay Proposition 99, the five FEct examples, and the three fatalities covariance conventions using the ignored acquired data. They skip when the cache is unavailable rather than downloading data during CI.
replications/yqx_panel/verify_fixes.py reruns 59 lecture specifications and 47 archive regressions, plus SCM and imputation checks, against frozen R outputs. Post-fix results live in results/fixes/; original audit outputs are preserved. Schäfer uses the tightly converged R reference established by the initial audit.
Tests compare the appropriate reference specifications. Forty-seven archive regressions do not mean 47 complete replications of every estimator/inference pipeline. Two are explicitly baseline-only, and the two huge datasets remain outside the current memory budget.
Recorded implementation validation
The September 21 implementation run recorded 343 passing tests and one intermittent failure in the existing benchmark process-group cleanup test (os.killpg raises a macOS permission error after the parent exits). Both cleanup cases pass when rerun in isolation. All 24 new tests pass. No skip or change to that process runner was made, and the full suite is not labeled green. Details are in test-status.json.
The current release has separate CI and package artifacts. The historical local result above is preserved rather than presented as a current CI failure.
Remaining work
The implementation does not add causal IFEct/MCct, Sun–Abraham or group-time DID, varying-slope absorption, multiway clustering, joint event-study inference, bootstrap imputation inference, augmented/predictor-V SCM, or factorial-DID nuisance orchestration. They remain explicitly proposed in the specification. The existing absorbing-only estimators have not been silently broadened to missing or reversing treatment panels. These implementations were merged in PR #23 and are included in v0.9.1.