diff-diff 3.1.3__tar.gz → 3.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. {diff_diff-3.1.3 → diff_diff-3.3.0}/PKG-INFO +41 -2
  2. {diff_diff-3.1.3 → diff_diff-3.3.0}/README.md +40 -1
  3. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/__init__.py +102 -9
  4. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/_backend.py +12 -4
  5. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/_guides_api.py +7 -3
  6. diff_diff-3.3.0/diff_diff/_nprobust_port.py +1412 -0
  7. diff_diff-3.3.0/diff_diff/_reporting_helpers.py +634 -0
  8. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/bootstrap_utils.py +85 -0
  9. diff_diff-3.3.0/diff_diff/business_report.py +2653 -0
  10. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/chaisemartin_dhaultfoeuille.py +1769 -344
  11. diff_diff-3.3.0/diff_diff/chaisemartin_dhaultfoeuille_bootstrap.py +1015 -0
  12. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/chaisemartin_dhaultfoeuille_results.py +289 -39
  13. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/continuous_did.py +59 -3
  14. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/continuous_did_bspline.py +29 -3
  15. diff_diff-3.3.0/diff_diff/diagnostic_report.py +3381 -0
  16. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/efficient_did.py +52 -29
  17. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/efficient_did_covariates.py +64 -1
  18. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/efficient_did_results.py +6 -0
  19. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/estimators.py +330 -52
  20. diff_diff-3.3.0/diff_diff/guides/llms-autonomous.txt +844 -0
  21. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/guides/llms-full.txt +145 -4
  22. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/guides/llms-practitioner.txt +44 -0
  23. diff_diff-3.3.0/diff_diff/had.py +4415 -0
  24. diff_diff-3.3.0/diff_diff/had_pretests.py +3019 -0
  25. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/imputation.py +1 -0
  26. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/imputation_results.py +1 -0
  27. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/linalg.py +779 -75
  28. diff_diff-3.3.0/diff_diff/local_linear.py +1332 -0
  29. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/power.py +42 -14
  30. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/practitioner.py +50 -8
  31. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/prep.py +96 -5
  32. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/pretrends.py +34 -6
  33. diff_diff-3.3.0/diff_diff/profile.py +714 -0
  34. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/results.py +224 -70
  35. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/stacked_did.py +1 -0
  36. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/stacked_did_results.py +1 -0
  37. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/staggered.py +78 -15
  38. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/staggered_results.py +8 -0
  39. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/staggered_triple_diff.py +80 -6
  40. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/staggered_triple_diff_results.py +5 -0
  41. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/sun_abraham.py +7 -0
  42. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/survey.py +353 -1
  43. diff_diff-3.3.0/diff_diff/synthetic_did.py +2688 -0
  44. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/trop_global.py +64 -48
  45. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/trop_local.py +91 -107
  46. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/twfe.py +121 -10
  47. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/two_stage.py +17 -0
  48. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/two_stage_bootstrap.py +15 -0
  49. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/two_stage_results.py +1 -0
  50. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/utils.py +603 -161
  51. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/wooldridge.py +23 -3
  52. {diff_diff-3.1.3 → diff_diff-3.3.0}/pyproject.toml +1 -1
  53. {diff_diff-3.1.3 → diff_diff-3.3.0}/rust/Cargo.lock +7 -7
  54. {diff_diff-3.1.3 → diff_diff-3.3.0}/rust/Cargo.toml +1 -1
  55. {diff_diff-3.1.3 → diff_diff-3.3.0}/rust/src/lib.rs +4 -2
  56. {diff_diff-3.1.3 → diff_diff-3.3.0}/rust/src/linalg.rs +1 -1
  57. {diff_diff-3.1.3 → diff_diff-3.3.0}/rust/src/trop.rs +289 -142
  58. {diff_diff-3.1.3 → diff_diff-3.3.0}/rust/src/weights.rs +582 -145
  59. diff_diff-3.1.3/diff_diff/chaisemartin_dhaultfoeuille_bootstrap.py +0 -590
  60. diff_diff-3.1.3/diff_diff/synthetic_did.py +0 -1482
  61. {diff_diff-3.1.3 → diff_diff-3.3.0}/LICENSE +0 -0
  62. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/bacon.py +0 -0
  63. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/continuous_did_results.py +0 -0
  64. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/datasets.py +0 -0
  65. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/diagnostics.py +0 -0
  66. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/efficient_did_bootstrap.py +0 -0
  67. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/efficient_did_weights.py +0 -0
  68. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/guides/__init__.py +0 -0
  69. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/guides/llms.txt +0 -0
  70. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/honest_did.py +0 -0
  71. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/imputation_bootstrap.py +0 -0
  72. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/prep_dgp.py +0 -0
  73. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/staggered_aggregation.py +0 -0
  74. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/staggered_bootstrap.py +0 -0
  75. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/triple_diff.py +0 -0
  76. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/trop.py +0 -0
  77. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/trop_results.py +0 -0
  78. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/visualization/__init__.py +0 -0
  79. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/visualization/_common.py +0 -0
  80. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/visualization/_continuous.py +0 -0
  81. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/visualization/_diagnostic.py +0 -0
  82. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/visualization/_event_study.py +0 -0
  83. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/visualization/_power.py +0 -0
  84. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/visualization/_staggered.py +0 -0
  85. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/visualization/_synthetic.py +0 -0
  86. {diff_diff-3.1.3 → diff_diff-3.3.0}/diff_diff/wooldridge_results.py +0 -0
  87. {diff_diff-3.1.3 → diff_diff-3.3.0}/rust/build.rs +0 -0
  88. {diff_diff-3.1.3 → diff_diff-3.3.0}/rust/src/bootstrap.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diff-diff
3
- Version: 3.1.3
3
+ Version: 3.3.0
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: Operating System :: OS Independent
@@ -57,6 +57,7 @@ Project-URL: Repository, https://github.com/igerber/diff-diff
57
57
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
58
58
  [![Downloads](https://img.shields.io/pypi/dm/diff-diff.svg)](https://pypi.org/project/diff-diff/)
59
59
  [![Documentation](https://readthedocs.org/projects/diff-diff/badge/?version=stable)](https://diff-diff.readthedocs.io/en/stable/)
60
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19646175.svg)](https://doi.org/10.5281/zenodo.19646175)
60
61
 
61
62
  A Python library for Difference-in-Differences (DiD) causal inference analysis with an sklearn-like API and statsmodels-style outputs.
62
63
 
@@ -144,6 +145,38 @@ Measuring campaign lift? Evaluating a product launch? diff-diff handles the caus
144
145
  - **[Brand awareness survey tutorial](docs/tutorials/17_brand_awareness_survey.ipynb)** - Full example with complex survey design, brand funnel analysis, and staggered rollouts
145
146
  - **Have BRFSS/ACS/CPS individual records?** Use [`aggregate_survey()`](docs/api/prep.rst) to roll respondent-level microdata into a geographic-period panel with inverse-variance precision weights. The returned second-stage design uses analytic weights (`aweight`), so it works directly with `DifferenceInDifferences`, `TwoWayFixedEffects`, `MultiPeriodDiD`, `SunAbraham`, `ContinuousDiD`, and `EfficientDiD` (estimators marked **Full** in the [survey support matrix](docs/choosing_estimator.rst))
146
147
 
148
+ ### Experimental preview: `BusinessReport` and `DiagnosticReport`
149
+
150
+ diff-diff ships two preview classes, `BusinessReport` and `DiagnosticReport`, that produce plain-English output and a structured `to_dict()` schema from any fitted result. **Both are experimental in this release** — wording, verdict thresholds, and schema shape will change as the library learns from real practitioner usage. Do not anchor downstream tooling on the schema yet; the experimental flag is noted in the CHANGELOG.
151
+
152
+ ```python
153
+ from diff_diff import CallawaySantAnna, BusinessReport
154
+
155
+ cs = CallawaySantAnna(base_period="universal").fit(
156
+ df, outcome="revenue", unit="store", time="month",
157
+ first_treat="first_treat", aggregate="event_study",
158
+ )
159
+ report = BusinessReport(
160
+ cs,
161
+ outcome_label="Revenue per store",
162
+ outcome_unit="$",
163
+ business_question="Did the loyalty program lift revenue?",
164
+ treatment_label="the loyalty program",
165
+ # Optional: pass the panel + column names so the auto-constructed
166
+ # DiagnosticReport can run data-dependent checks (2x2 pre-trends,
167
+ # Goodman-Bacon decomposition, EfficientDiD Hausman pretest).
168
+ # Without these the auto path still runs but skips those checks.
169
+ data=df,
170
+ outcome="revenue",
171
+ unit="store",
172
+ time="month",
173
+ first_treat="first_treat",
174
+ )
175
+ print(report.summary())
176
+ ```
177
+
178
+ `BusinessReport` auto-constructs a `DiagnosticReport` so the summary mentions pre-trends, sensitivity, and design-effect findings in one call. Methodology (phrasing rules, verdict thresholds, schema stability) is documented in [docs/methodology/REPORTING.md](docs/methodology/REPORTING.md). Feedback on wording, applicability, and missing diagnostics is welcome — this is the part of the library most likely to evolve in the next few releases.
179
+
147
180
  Already know DiD? The [academic quickstart](docs/quickstart.rst) and [estimator guide](docs/choosing_estimator.rst) cover the full technical details.
148
181
 
149
182
  ## Features
@@ -3119,14 +3152,20 @@ If you use diff-diff in your research, please cite it:
3119
3152
  ```bibtex
3120
3153
  @software{diff_diff,
3121
3154
  title = {diff-diff: Difference-in-Differences Causal Inference for Python},
3122
- author = {{diff-diff contributors}},
3155
+ author = {Gerber, Isaac},
3156
+ year = {2026},
3123
3157
  url = {https://github.com/igerber/diff-diff},
3158
+ doi = {10.5281/zenodo.19646175},
3124
3159
  license = {MIT},
3125
3160
  }
3126
3161
  ```
3127
3162
 
3163
+ The DOI above is the Zenodo concept DOI — it always resolves to the latest release. To cite a specific version, look up its versioned DOI on [the Zenodo project page](https://doi.org/10.5281/zenodo.19646175).
3164
+
3128
3165
  See [`CITATION.cff`](CITATION.cff) for the full citation metadata.
3129
3166
 
3167
+ **Note on authorship**: academic citation (`CITATION.cff`, the BibTeX above) lists individual authors with ORCIDs per scholarly convention. Package metadata surfaces (`pyproject.toml`, Sphinx docs) list "diff-diff contributors" to acknowledge the collective — see [`CONTRIBUTORS.md`](CONTRIBUTORS.md) for the full list.
3168
+
3130
3169
  ## License
3131
3170
 
3132
3171
  MIT License
@@ -5,6 +5,7 @@
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Downloads](https://img.shields.io/pypi/dm/diff-diff.svg)](https://pypi.org/project/diff-diff/)
7
7
  [![Documentation](https://readthedocs.org/projects/diff-diff/badge/?version=stable)](https://diff-diff.readthedocs.io/en/stable/)
8
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19646175.svg)](https://doi.org/10.5281/zenodo.19646175)
8
9
 
9
10
  A Python library for Difference-in-Differences (DiD) causal inference analysis with an sklearn-like API and statsmodels-style outputs.
10
11
 
@@ -92,6 +93,38 @@ Measuring campaign lift? Evaluating a product launch? diff-diff handles the caus
92
93
  - **[Brand awareness survey tutorial](docs/tutorials/17_brand_awareness_survey.ipynb)** - Full example with complex survey design, brand funnel analysis, and staggered rollouts
93
94
  - **Have BRFSS/ACS/CPS individual records?** Use [`aggregate_survey()`](docs/api/prep.rst) to roll respondent-level microdata into a geographic-period panel with inverse-variance precision weights. The returned second-stage design uses analytic weights (`aweight`), so it works directly with `DifferenceInDifferences`, `TwoWayFixedEffects`, `MultiPeriodDiD`, `SunAbraham`, `ContinuousDiD`, and `EfficientDiD` (estimators marked **Full** in the [survey support matrix](docs/choosing_estimator.rst))
94
95
 
96
+ ### Experimental preview: `BusinessReport` and `DiagnosticReport`
97
+
98
+ diff-diff ships two preview classes, `BusinessReport` and `DiagnosticReport`, that produce plain-English output and a structured `to_dict()` schema from any fitted result. **Both are experimental in this release** — wording, verdict thresholds, and schema shape will change as the library learns from real practitioner usage. Do not anchor downstream tooling on the schema yet; the experimental flag is noted in the CHANGELOG.
99
+
100
+ ```python
101
+ from diff_diff import CallawaySantAnna, BusinessReport
102
+
103
+ cs = CallawaySantAnna(base_period="universal").fit(
104
+ df, outcome="revenue", unit="store", time="month",
105
+ first_treat="first_treat", aggregate="event_study",
106
+ )
107
+ report = BusinessReport(
108
+ cs,
109
+ outcome_label="Revenue per store",
110
+ outcome_unit="$",
111
+ business_question="Did the loyalty program lift revenue?",
112
+ treatment_label="the loyalty program",
113
+ # Optional: pass the panel + column names so the auto-constructed
114
+ # DiagnosticReport can run data-dependent checks (2x2 pre-trends,
115
+ # Goodman-Bacon decomposition, EfficientDiD Hausman pretest).
116
+ # Without these the auto path still runs but skips those checks.
117
+ data=df,
118
+ outcome="revenue",
119
+ unit="store",
120
+ time="month",
121
+ first_treat="first_treat",
122
+ )
123
+ print(report.summary())
124
+ ```
125
+
126
+ `BusinessReport` auto-constructs a `DiagnosticReport` so the summary mentions pre-trends, sensitivity, and design-effect findings in one call. Methodology (phrasing rules, verdict thresholds, schema stability) is documented in [docs/methodology/REPORTING.md](docs/methodology/REPORTING.md). Feedback on wording, applicability, and missing diagnostics is welcome — this is the part of the library most likely to evolve in the next few releases.
127
+
95
128
  Already know DiD? The [academic quickstart](docs/quickstart.rst) and [estimator guide](docs/choosing_estimator.rst) cover the full technical details.
96
129
 
97
130
  ## Features
@@ -3067,14 +3100,20 @@ If you use diff-diff in your research, please cite it:
3067
3100
  ```bibtex
3068
3101
  @software{diff_diff,
3069
3102
  title = {diff-diff: Difference-in-Differences Causal Inference for Python},
3070
- author = {{diff-diff contributors}},
3103
+ author = {Gerber, Isaac},
3104
+ year = {2026},
3071
3105
  url = {https://github.com/igerber/diff-diff},
3106
+ doi = {10.5281/zenodo.19646175},
3072
3107
  license = {MIT},
3073
3108
  }
3074
3109
  ```
3075
3110
 
3111
+ The DOI above is the Zenodo concept DOI — it always resolves to the latest release. To cite a specific version, look up its versioned DOI on [the Zenodo project page](https://doi.org/10.5281/zenodo.19646175).
3112
+
3076
3113
  See [`CITATION.cff`](CITATION.cff) for the full citation metadata.
3077
3114
 
3115
+ **Note on authorship**: academic citation (`CITATION.cff`, the BibTeX above) lists individual authors with ORCIDs per scholarly convention. Package metadata surfaces (`pyproject.toml`, Sphinx docs) list "diff-diff contributors" to acknowledge the collective — see [`CONTRIBUTORS.md`](CONTRIBUTORS.md) for the full list.
3116
+
3078
3117
  ## License
3079
3118
 
3080
3119
  MIT License
@@ -4,14 +4,20 @@ diff-diff: A library for Difference-in-Differences analysis.
4
4
  This library provides sklearn-like estimators for causal inference
5
5
  using the difference-in-differences methodology.
6
6
 
7
- For rigorous analysis, follow the 8-step practitioner workflow based
8
- on Baker et al. (2025). After estimation, call
9
- ``practitioner_next_steps(results)`` for context-aware guidance on
10
- remaining diagnostic steps.
7
+ For AI agents:
11
8
 
12
- AI agents: call ``diff_diff.get_llm_guide()`` for a complete API reference.
13
- Use ``get_llm_guide("practitioner")`` for the 8-step workflow or
14
- ``get_llm_guide("full")`` for comprehensive documentation.
9
+ 1. Describe your data: ``diff_diff.profile_panel(df, unit=..., time=...,
10
+ treatment=..., outcome=...)``
11
+ 2. Consult the reference: ``diff_diff.get_llm_guide("autonomous")``
12
+ (estimator-support matrix + reasoning)
13
+ 3. Follow the workflow: ``diff_diff.get_llm_guide("practitioner")``
14
+ (Baker et al. (2025) 8-step recipe)
15
+ 4. Report results: ``diff_diff.BusinessReport(results)``
16
+ (structured agent-legible output)
17
+
18
+ For a comprehensive API reference call ``diff_diff.get_llm_guide("full")``;
19
+ ``practitioner_next_steps(results)`` returns context-aware guidance after
20
+ any estimator's ``fit()``.
15
21
  """
16
22
 
17
23
  # Import backend detection from dedicated module (avoids circular imports)
@@ -21,7 +27,6 @@ from diff_diff._backend import (
21
27
  _rust_compute_robust_vcov,
22
28
  _rust_project_simplex,
23
29
  _rust_solve_ols,
24
- _rust_synthetic_weights,
25
30
  )
26
31
 
27
32
  from diff_diff.bacon import (
@@ -43,6 +48,38 @@ from diff_diff.linalg import (
43
48
  InferenceResult,
44
49
  LinearRegression,
45
50
  )
51
+ from diff_diff.local_linear import (
52
+ KERNELS,
53
+ BandwidthResult,
54
+ BiasCorrectedFit,
55
+ LocalLinearFit,
56
+ bias_corrected_local_linear,
57
+ epanechnikov_kernel,
58
+ kernel_moments,
59
+ local_linear_fit,
60
+ mse_optimal_bandwidth,
61
+ triangular_kernel,
62
+ uniform_kernel,
63
+ )
64
+ from diff_diff.had import (
65
+ HeterogeneousAdoptionDiD,
66
+ HeterogeneousAdoptionDiDEventStudyResults,
67
+ HeterogeneousAdoptionDiDResults,
68
+ )
69
+ from diff_diff.had_pretests import (
70
+ HADPretestReport,
71
+ QUGTestResults,
72
+ StuteJointResult,
73
+ StuteTestResults,
74
+ YatchewTestResults,
75
+ did_had_pretest_workflow,
76
+ joint_homogeneity_test,
77
+ joint_pretrends_test,
78
+ qug_test,
79
+ stute_joint_pretest,
80
+ stute_test,
81
+ yatchew_hr_test,
82
+ )
46
83
  from diff_diff.estimators import (
47
84
  DifferenceInDifferences,
48
85
  MultiPeriodDiD,
@@ -202,7 +239,18 @@ from diff_diff.visualization import (
202
239
  plot_synth_weights,
203
240
  )
204
241
  from diff_diff.practitioner import practitioner_next_steps
242
+ from diff_diff.business_report import (
243
+ BUSINESS_REPORT_SCHEMA_VERSION,
244
+ BusinessContext,
245
+ BusinessReport,
246
+ )
247
+ from diff_diff.diagnostic_report import (
248
+ DIAGNOSTIC_REPORT_SCHEMA_VERSION,
249
+ DiagnosticReport,
250
+ DiagnosticReportResults,
251
+ )
205
252
  from diff_diff._guides_api import get_llm_guide
253
+ from diff_diff.profile import Alert, PanelProfile, profile_panel
206
254
  from diff_diff.datasets import (
207
255
  clear_cache,
208
256
  list_datasets,
@@ -230,8 +278,9 @@ Bacon = BaconDecomposition
230
278
  EDiD = EfficientDiD
231
279
  ETWFE = WooldridgeDiD
232
280
  DCDH = ChaisemartinDHaultfoeuille
281
+ HAD = HeterogeneousAdoptionDiD
233
282
 
234
- __version__ = "3.1.3"
283
+ __version__ = "3.3.0"
235
284
  __all__ = [
236
285
  # Estimators
237
286
  "DifferenceInDifferences",
@@ -395,6 +444,40 @@ __all__ = [
395
444
  # Linear algebra helpers
396
445
  "LinearRegression",
397
446
  "InferenceResult",
447
+ # Local-linear regression infrastructure (Phase 1a for HeterogeneousAdoptionDiD)
448
+ "KERNELS",
449
+ "LocalLinearFit",
450
+ "epanechnikov_kernel",
451
+ "kernel_moments",
452
+ "local_linear_fit",
453
+ "triangular_kernel",
454
+ "uniform_kernel",
455
+ # MSE-optimal bandwidth selector (Phase 1b for HeterogeneousAdoptionDiD)
456
+ "BandwidthResult",
457
+ "mse_optimal_bandwidth",
458
+ # Bias-corrected local-linear (Phase 1c for HeterogeneousAdoptionDiD)
459
+ "BiasCorrectedFit",
460
+ "bias_corrected_local_linear",
461
+ # HeterogeneousAdoptionDiD (Phase 2a single-period, Phase 2b event study)
462
+ "HeterogeneousAdoptionDiD",
463
+ "HeterogeneousAdoptionDiDResults",
464
+ "HeterogeneousAdoptionDiDEventStudyResults",
465
+ "HAD",
466
+ # HeterogeneousAdoptionDiD pre-test diagnostics (Phase 3)
467
+ "qug_test",
468
+ "stute_test",
469
+ "yatchew_hr_test",
470
+ "did_had_pretest_workflow",
471
+ "QUGTestResults",
472
+ "StuteTestResults",
473
+ "YatchewTestResults",
474
+ "HADPretestReport",
475
+ # HAD joint pre-tests (Phase 3 follow-up) — multi-period event-study
476
+ # workflow dispatch via did_had_pretest_workflow(aggregate="event_study")
477
+ "stute_joint_pretest",
478
+ "joint_pretrends_test",
479
+ "joint_homogeneity_test",
480
+ "StuteJointResult",
398
481
  # Datasets
399
482
  "load_card_krueger",
400
483
  "load_castle_doctrine",
@@ -405,6 +488,16 @@ __all__ = [
405
488
  "clear_cache",
406
489
  # Practitioner guidance
407
490
  "practitioner_next_steps",
491
+ "BusinessReport",
492
+ "BusinessContext",
493
+ "BUSINESS_REPORT_SCHEMA_VERSION",
494
+ "DiagnosticReport",
495
+ "DiagnosticReportResults",
496
+ "DIAGNOSTIC_REPORT_SCHEMA_VERSION",
497
+ # Panel profiling (agent-facing pre-fit describe utility)
498
+ "profile_panel",
499
+ "PanelProfile",
500
+ "Alert",
408
501
  # LLM guide accessor
409
502
  "get_llm_guide",
410
503
  ]
@@ -19,7 +19,6 @@ _backend_env = os.environ.get("DIFF_DIFF_BACKEND", "auto").lower()
19
19
  try:
20
20
  from diff_diff._rust_backend import (
21
21
  generate_bootstrap_weights_batch as _rust_bootstrap_weights,
22
- compute_synthetic_weights as _rust_synthetic_weights,
23
22
  project_simplex as _rust_project_simplex,
24
23
  solve_ols as _rust_solve_ols,
25
24
  compute_robust_vcov as _rust_compute_robust_vcov,
@@ -35,6 +34,9 @@ try:
35
34
  compute_time_weights as _rust_compute_time_weights,
36
35
  compute_noise_level as _rust_compute_noise_level,
37
36
  sc_weight_fw as _rust_sc_weight_fw,
37
+ sc_weight_fw_with_convergence as _rust_sc_weight_fw_with_convergence,
38
+ sc_weight_fw_weighted as _rust_sc_weight_fw_weighted,
39
+ sc_weight_fw_weighted_with_convergence as _rust_sc_weight_fw_weighted_with_convergence,
38
40
  # Diagnostics
39
41
  rust_backend_info as _rust_backend_info,
40
42
  )
@@ -43,7 +45,6 @@ try:
43
45
  except ImportError:
44
46
  _rust_available = False
45
47
  _rust_bootstrap_weights = None
46
- _rust_synthetic_weights = None
47
48
  _rust_project_simplex = None
48
49
  _rust_solve_ols = None
49
50
  _rust_compute_robust_vcov = None
@@ -59,6 +60,9 @@ except ImportError:
59
60
  _rust_compute_time_weights = None
60
61
  _rust_compute_noise_level = None
61
62
  _rust_sc_weight_fw = None
63
+ _rust_sc_weight_fw_with_convergence = None
64
+ _rust_sc_weight_fw_weighted = None
65
+ _rust_sc_weight_fw_weighted_with_convergence = None
62
66
  _rust_backend_info = None
63
67
 
64
68
  # Determine final backend based on environment variable and availability
@@ -66,7 +70,6 @@ if _backend_env == "python":
66
70
  # Force pure Python mode - disable Rust even if available
67
71
  HAS_RUST_BACKEND = False
68
72
  _rust_bootstrap_weights = None
69
- _rust_synthetic_weights = None
70
73
  _rust_project_simplex = None
71
74
  _rust_solve_ols = None
72
75
  _rust_compute_robust_vcov = None
@@ -82,6 +85,9 @@ if _backend_env == "python":
82
85
  _rust_compute_time_weights = None
83
86
  _rust_compute_noise_level = None
84
87
  _rust_sc_weight_fw = None
88
+ _rust_sc_weight_fw_with_convergence = None
89
+ _rust_sc_weight_fw_weighted = None
90
+ _rust_sc_weight_fw_weighted_with_convergence = None
85
91
  _rust_backend_info = None
86
92
  elif _backend_env == "rust":
87
93
  # Force Rust mode - fail if not available
@@ -115,7 +121,6 @@ __all__ = [
115
121
  "HAS_RUST_BACKEND",
116
122
  "rust_backend_info",
117
123
  "_rust_bootstrap_weights",
118
- "_rust_synthetic_weights",
119
124
  "_rust_project_simplex",
120
125
  "_rust_solve_ols",
121
126
  "_rust_compute_robust_vcov",
@@ -131,4 +136,7 @@ __all__ = [
131
136
  "_rust_compute_time_weights",
132
137
  "_rust_compute_noise_level",
133
138
  "_rust_sc_weight_fw",
139
+ "_rust_sc_weight_fw_with_convergence",
140
+ "_rust_sc_weight_fw_weighted",
141
+ "_rust_sc_weight_fw_weighted_with_convergence",
134
142
  ]
@@ -1,4 +1,5 @@
1
1
  """Runtime accessor for bundled LLM guide files."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from importlib.resources import files
@@ -7,6 +8,7 @@ _VARIANT_TO_FILE = {
7
8
  "concise": "llms.txt",
8
9
  "full": "llms-full.txt",
9
10
  "practitioner": "llms-practitioner.txt",
11
+ "autonomous": "llms-autonomous.txt",
10
12
  }
11
13
 
12
14
 
@@ -21,6 +23,10 @@ def get_llm_guide(variant: str = "concise") -> str:
21
23
  - ``"concise"`` -- compact API reference (llms.txt)
22
24
  - ``"full"`` -- complete API documentation (llms-full.txt)
23
25
  - ``"practitioner"`` -- 8-step practitioner workflow (llms-practitioner.txt)
26
+ - ``"autonomous"`` -- reference guide for AI-agent use: estimator-support
27
+ matrix, per-design-feature reasoning, post-fit validation index, and
28
+ BR/DR schema (llms-autonomous.txt). Pair with
29
+ :func:`diff_diff.profile_panel` for pre-fit data description.
24
30
 
25
31
  Returns
26
32
  -------
@@ -42,7 +48,5 @@ def get_llm_guide(variant: str = "concise") -> str:
42
48
  filename = _VARIANT_TO_FILE[variant]
43
49
  except (KeyError, TypeError):
44
50
  valid = ", ".join(repr(k) for k in _VARIANT_TO_FILE)
45
- raise ValueError(
46
- f"Unknown guide variant {variant!r}. Valid options: {valid}."
47
- ) from None
51
+ raise ValueError(f"Unknown guide variant {variant!r}. Valid options: {valid}.") from None
48
52
  return files("diff_diff.guides").joinpath(filename).read_text(encoding="utf-8")