ergodic 0.3.0__tar.gz → 0.3.2__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 (415) hide show
  1. {ergodic-0.3.0 → ergodic-0.3.2}/CHANGELOG.md +66 -1
  2. {ergodic-0.3.0 → ergodic-0.3.2}/PKG-INFO +2 -2
  3. {ergodic-0.3.0 → ergodic-0.3.2}/README.md +1 -1
  4. {ergodic-0.3.0 → ergodic-0.3.2}/docs/case-studies/01-birthweight.py +8 -8
  5. {ergodic-0.3.0 → ergodic-0.3.2}/docs/case-studies/03-pension401k.py +1 -1
  6. {ergodic-0.3.0 → ergodic-0.3.2}/docs/case-studies/06-clv.py +5 -4
  7. {ergodic-0.3.0 → ergodic-0.3.2}/docs/case-studies/07-churn.py +7 -3
  8. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/09-process-intelligence.py +1 -1
  9. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/assumptions.md +3 -3
  10. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/choose-a-method.md +21 -2
  11. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/discover-a-graph.md +14 -0
  12. ergodic-0.3.2/docsite/pages/estimate-average-effect.md +168 -0
  13. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/evaluate-a-graph.md +9 -2
  14. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/heterogeneous-effects.md +14 -0
  15. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/install.md +1 -1
  16. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/refute-an-estimate.md +9 -9
  17. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/start.md +1 -1
  18. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/test-a-graph.md +20 -0
  19. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/time-series-discovery.md +6 -6
  20. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/site.toml +1 -1
  21. ergodic-0.3.2/scripts/audits/qualify_aipw.py +103 -0
  22. ergodic-0.3.2/scripts/audits/qualify_ci_discovery.py +49 -0
  23. ergodic-0.3.2/scripts/audits/qualify_gcm.py +51 -0
  24. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/__init__.py +1 -1
  25. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_posterior.py +41 -3
  26. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_stats.py +9 -0
  27. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_mmpc.py +13 -9
  28. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/ci/_gcm.py +55 -5
  29. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/ci/_linear.py +5 -3
  30. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_crossfit.py +2 -2
  31. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_estimate.py +6 -4
  32. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_estimator.py +42 -14
  33. ergodic-0.3.2/src/ergodic/inference/_overlap.py +90 -0
  34. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_perunit.py +4 -6
  35. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_registry.py +13 -9
  36. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_result.py +7 -1
  37. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_scores.py +18 -1
  38. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_select.py +6 -6
  39. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_target.py +4 -1
  40. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_dml.py +3 -0
  41. ergodic-0.3.2/src/ergodic/inference/estimators/_doubly_robust.py +292 -0
  42. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_iv.py +116 -7
  43. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_regression.py +2 -0
  44. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_weighting.py +56 -16
  45. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/_record.py +16 -8
  46. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_bayes.py +34 -0
  47. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_cate.py +4 -3
  48. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_ci.py +109 -0
  49. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_graphtest.py +10 -4
  50. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_mmpc.py +29 -0
  51. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_estimators_e2.py +2 -2
  52. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_graph_posterior.py +43 -0
  53. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_inference.py +1 -1
  54. ergodic-0.3.2/tests/test_inference_guardrails.py +205 -0
  55. ergodic-0.3.2/tests/test_release_cate_regressions.py +194 -0
  56. ergodic-0.3.0/docsite/pages/estimate-average-effect.md +0 -100
  57. ergodic-0.3.0/src/ergodic/inference/estimators/_doubly_robust.py +0 -152
  58. {ergodic-0.3.0 → ergodic-0.3.2}/.gitignore +0 -0
  59. {ergodic-0.3.0 → ergodic-0.3.2}/CONTRIBUTING.md +0 -0
  60. {ergodic-0.3.0 → ergodic-0.3.2}/LICENSE +0 -0
  61. {ergodic-0.3.0 → ergodic-0.3.2}/NOTICE +0 -0
  62. {ergodic-0.3.0 → ergodic-0.3.2}/SECURITY.md +0 -0
  63. {ergodic-0.3.0 → ergodic-0.3.2}/docs/case-studies/02-berkeley.py +0 -0
  64. {ergodic-0.3.0 → ergodic-0.3.2}/docs/case-studies/04-synthetic-control.py +0 -0
  65. {ergodic-0.3.0 → ergodic-0.3.2}/docs/case-studies/05-discovery.py +0 -0
  66. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/01-why-causality.py +0 -0
  67. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/02-good-and-bad-controls.py +0 -0
  68. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/03-estimating-effects.py +0 -0
  69. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/04-heterogeneous-effects.py +0 -0
  70. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/05-hidden-confounding.py +0 -0
  71. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/06-discovering-structure.py +0 -0
  72. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/07-quasi-experiments.py +0 -0
  73. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/08-process-mining.py +0 -0
  74. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/10-decision-points.py +0 -0
  75. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/11-forecasting.py +0 -0
  76. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/12-structural-causal-models.py +0 -0
  77. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/13-hidden-regimes.py +0 -0
  78. {ergodic-0.3.0 → ergodic-0.3.2}/docs/learn/14-causal-forests-and-policy-trees.py +0 -0
  79. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/docs.css +0 -0
  80. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/docs.js +0 -0
  81. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/ergodic-mark-dark.svg +0 -0
  82. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/ergodic-mark-light.svg +0 -0
  83. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/fonts/OFL-Geist-Mono.txt +0 -0
  84. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/fonts/OFL-Geist.txt +0 -0
  85. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/fonts/geist-latin-ext-wght-normal.woff2 +0 -0
  86. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/fonts/geist-latin-wght-normal.woff2 +0 -0
  87. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/fonts/geist-mono-latin-ext-wght-normal.woff2 +0 -0
  88. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/fonts/geist-mono-latin-wght-normal.woff2 +0 -0
  89. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/site.css +0 -0
  90. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/assets/tokens.css +0 -0
  91. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/adjustment-sets.md +0 -0
  92. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/backtest-a-forecast.md +0 -0
  93. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/bootstrap-stability.md +0 -0
  94. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/certify-and-calibrate.md +0 -0
  95. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/changelog.md +0 -0
  96. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/compatibility.md +0 -0
  97. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/conformance-and-decisions.md +0 -0
  98. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/decode-a-graph.md +0 -0
  99. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/difference-in-differences.md +0 -0
  100. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/domain-knowledge.md +0 -0
  101. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/edge-probabilities.md +0 -0
  102. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/fit-an-scm.md +0 -0
  103. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/forecast-with-drivers.md +0 -0
  104. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/forecast-with-welt.md +0 -0
  105. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/glossary.md +0 -0
  106. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/graphs-and-marks.md +0 -0
  107. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/identify-an-effect.md +0 -0
  108. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/interventions-and-counterfactuals.md +0 -0
  109. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/map-a-process.md +0 -0
  110. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/mediation.md +0 -0
  111. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/reconcile-a-hierarchy.md +0 -0
  112. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/recover-hidden-regimes.md +0 -0
  113. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/run-scenarios.md +0 -0
  114. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/synthetic-control.md +0 -0
  115. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/target-a-policy.md +0 -0
  116. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/pages/welt.md +0 -0
  117. {ergodic-0.3.0 → ergodic-0.3.2}/docsite/provenance.json +0 -0
  118. {ergodic-0.3.0 → ergodic-0.3.2}/examples/README.md +0 -0
  119. {ergodic-0.3.0 → ergodic-0.3.2}/examples/adjustment_birthweight.py +0 -0
  120. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/01_cate_basics.py +0 -0
  121. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/02_meta_learners_compared.py +0 -0
  122. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/03_is_there_heterogeneity.py +0 -0
  123. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/04_where_is_the_effect_higher.py +0 -0
  124. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/05_targeting_with_rate.py +0 -0
  125. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/06_per_unit_uncertainty.py +0 -0
  126. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/07_cate_vs_ite.py +0 -0
  127. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/08_identification_gating.py +0 -0
  128. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/09_case_study_uplift_end_to_end.py +0 -0
  129. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/10_causal_forest.py +0 -0
  130. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/11_policy_trees.py +0 -0
  131. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/12_model_selection.py +0 -0
  132. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/13_case_study_phase2.py +0 -0
  133. {ergodic-0.3.0 → ergodic-0.3.2}/examples/cate/README.md +0 -0
  134. {ergodic-0.3.0 → ergodic-0.3.2}/examples/confounding_berkeley.py +0 -0
  135. {ergodic-0.3.0 → ergodic-0.3.2}/examples/discovery_asia.py +0 -0
  136. {ergodic-0.3.0 → ergodic-0.3.2}/examples/double_ml_pension.py +0 -0
  137. {ergodic-0.3.0 → ergodic-0.3.2}/examples/hidden_regimes.py +0 -0
  138. {ergodic-0.3.0 → ergodic-0.3.2}/examples/latent_switch.py +0 -0
  139. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/01_three_rungs.py +0 -0
  140. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/02_honesty_and_tipping_point.py +0 -0
  141. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/03_causal_price_optimization.py +0 -0
  142. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/04_counterfactual_recourse.py +0 -0
  143. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/05_counterfactual_fairness.py +0 -0
  144. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/06_causal_attribution.py +0 -0
  145. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/07_but_for_accounting.py +0 -0
  146. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/08_off_policy_evaluation.py +0 -0
  147. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/09_root_cause_attribution.py +0 -0
  148. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/10_frontdoor_unmeasured_confounder.py +0 -0
  149. {ergodic-0.3.0 → ergodic-0.3.2}/examples/scm/README.md +0 -0
  150. {ergodic-0.3.0 → ergodic-0.3.2}/examples/synthetic_control_smoking.py +0 -0
  151. {ergodic-0.3.0 → ergodic-0.3.2}/public/ergodic_logomark_dark.svg +0 -0
  152. {ergodic-0.3.0 → ergodic-0.3.2}/public/ergodic_logomark_light.svg +0 -0
  153. {ergodic-0.3.0 → ergodic-0.3.2}/pyproject.toml +0 -0
  154. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/__init__.py +0 -0
  155. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/__main__.py +0 -0
  156. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/build.py +0 -0
  157. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/config.py +0 -0
  158. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/examples.py +0 -0
  159. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/graphite.py +0 -0
  160. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/links.py +0 -0
  161. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/notebooks.py +0 -0
  162. {ergodic-0.3.0 → ergodic-0.3.2}/scripts/docsite/reference.py +0 -0
  163. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/__init__.py +0 -0
  164. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_dataset.py +0 -0
  165. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_eventlog.py +0 -0
  166. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_frame.py +0 -0
  167. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_hierarchy.py +0 -0
  168. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_hts.py +0 -0
  169. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_panel.py +0 -0
  170. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_schema.py +0 -0
  171. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_tabular.py +0 -0
  172. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/data/_timeseries.py +0 -0
  173. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/__init__.py +0 -0
  174. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/_bayesnet.py +0 -0
  175. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/_bunch.py +0 -0
  176. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/_bundled.py +0 -0
  177. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/_tables.py +0 -0
  178. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/data/birthweight.csv +0 -0
  179. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/data/germany.csv +0 -0
  180. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/data/pension401k.csv +0 -0
  181. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/datasets/data/smoking.csv +0 -0
  182. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/__init__.py +0 -0
  183. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_average.py +0 -0
  184. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_bootstrap.py +0 -0
  185. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_ci.py +0 -0
  186. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_decode.py +0 -0
  187. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_discover.py +0 -0
  188. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_encode.py +0 -0
  189. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_evaluate.py +0 -0
  190. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_exact.py +0 -0
  191. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_granger.py +0 -0
  192. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_graphtest.py +0 -0
  193. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_lagged.py +0 -0
  194. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_mcmc.py +0 -0
  195. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_probabilities.py +0 -0
  196. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_registry.py +0 -0
  197. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_result.py +0 -0
  198. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_score.py +0 -0
  199. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_target.py +0 -0
  200. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/_tsresult.py +0 -0
  201. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/__init__.py +0 -0
  202. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_astar.py +0 -0
  203. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_boss.py +0 -0
  204. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_fci.py +0 -0
  205. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_ges.py +0 -0
  206. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_glasso.py +0 -0
  207. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_grasp.py +0 -0
  208. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_hc.py +0 -0
  209. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_lingam.py +0 -0
  210. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_pc.py +0 -0
  211. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_pcmci.py +0 -0
  212. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_rfci.py +0 -0
  213. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/algorithms/_skeleton.py +0 -0
  214. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/ci/__init__.py +0 -0
  215. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/ci/_discrete.py +0 -0
  216. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/ci/_mixed.py +0 -0
  217. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/scores/__init__.py +0 -0
  218. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/scores/_bdeu.py +0 -0
  219. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/scores/_bge.py +0 -0
  220. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/scores/_bic.py +0 -0
  221. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/scores/_discrete_bic.py +0 -0
  222. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/discovery/scores/_mixed_bic.py +0 -0
  223. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/__init__.py +0 -0
  224. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_attribution.py +0 -0
  225. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_backtest.py +0 -0
  226. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_baselines.py +0 -0
  227. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_facade.py +0 -0
  228. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_fit.py +0 -0
  229. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_mechanism.py +0 -0
  230. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_metrics.py +0 -0
  231. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_protocol.py +0 -0
  232. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_quantiles.py +0 -0
  233. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_reconcile.py +0 -0
  234. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_result.py +0 -0
  235. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_scalers.py +0 -0
  236. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_schedule.py +0 -0
  237. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_staged.py +0 -0
  238. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/_structure.py +0 -0
  239. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/mechanisms/__init__.py +0 -0
  240. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/mechanisms/_bass.py +0 -0
  241. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/mechanisms/_ou.py +0 -0
  242. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/forecast/mechanisms/_structural.py +0 -0
  243. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/__init__.py +0 -0
  244. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/_graph.py +0 -0
  245. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/_io.py +0 -0
  246. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/_marks.py +0 -0
  247. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/_meek.py +0 -0
  248. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/_pag.py +0 -0
  249. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/_primitives.py +0 -0
  250. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/_separation.py +0 -0
  251. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/graph/_surgery.py +0 -0
  252. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/__init__.py +0 -0
  253. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_criteria.py +0 -0
  254. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_distribution.py +0 -0
  255. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_evaluate.py +0 -0
  256. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_functional.py +0 -0
  257. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_id.py +0 -0
  258. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_ida.py +0 -0
  259. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_mediation.py +0 -0
  260. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_recognize.py +0 -0
  261. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/identification/_record.py +0 -0
  262. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/__init__.py +0 -0
  263. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_design.py +0 -0
  264. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_learner.py +0 -0
  265. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_refute.py +0 -0
  266. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/_stats.py +0 -0
  267. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/__init__.py +0 -0
  268. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_did.py +0 -0
  269. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_forest.py +0 -0
  270. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_frontdoor.py +0 -0
  271. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_its.py +0 -0
  272. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_mediation.py +0 -0
  273. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_meta.py +0 -0
  274. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_null.py +0 -0
  275. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_plugin.py +0 -0
  276. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/estimators/_synthetic_control.py +0 -0
  277. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/__init__.py +0 -0
  278. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/_blp.py +0 -0
  279. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/_calibration.py +0 -0
  280. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/_clan.py +0 -0
  281. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/_gates.py +0 -0
  282. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/_linalg.py +0 -0
  283. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/_policy.py +0 -0
  284. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/explain/_rate.py +0 -0
  285. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/learners/__init__.py +0 -0
  286. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/learners/_linear.py +0 -0
  287. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/learners/_neighbors.py +0 -0
  288. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/inference/learners/_pymc.py +0 -0
  289. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/knowledge.py +0 -0
  290. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/__init__.py +0 -0
  291. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/_engine.py +0 -0
  292. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/_facade.py +0 -0
  293. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/_identify.py +0 -0
  294. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/_likelihood.py +0 -0
  295. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/_marginalize.py +0 -0
  296. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/_result.py +0 -0
  297. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/likelihoods/__init__.py +0 -0
  298. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/likelihoods/_ar.py +0 -0
  299. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/latent/likelihoods/_spectral.py +0 -0
  300. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/__init__.py +0 -0
  301. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_case_table.py +0 -0
  302. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_cases.py +0 -0
  303. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_conformance.py +0 -0
  304. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_decisions.py +0 -0
  305. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_filters.py +0 -0
  306. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_map.py +0 -0
  307. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_mine.py +0 -0
  308. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_net.py +0 -0
  309. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_panel.py +0 -0
  310. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_performance.py +0 -0
  311. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_soundness.py +0 -0
  312. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_tree.py +0 -0
  313. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/process/_variants.py +0 -0
  314. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/py.typed +0 -0
  315. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/__init__.py +0 -0
  316. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/_bounds.py +0 -0
  317. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/_compose.py +0 -0
  318. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/_district.py +0 -0
  319. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/_mechanism.py +0 -0
  320. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/_noise.py +0 -0
  321. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/_result.py +0 -0
  322. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/_sampling.py +0 -0
  323. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/scm/_scm.py +0 -0
  324. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/welt/__init__.py +0 -0
  325. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/welt/_client.py +0 -0
  326. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/welt/_data.py +0 -0
  327. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/welt/_discovery.py +0 -0
  328. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/welt/_forecast.py +0 -0
  329. {ergodic-0.3.0 → ergodic-0.3.2}/src/ergodic/welt/_learner.py +0 -0
  330. {ergodic-0.3.0 → ergodic-0.3.2}/tests/conftest.py +0 -0
  331. {ergodic-0.3.0 → ergodic-0.3.2}/tests/data/exp0626_decoder_parity.json +0 -0
  332. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_average_effect.py +0 -0
  333. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_case_table.py +0 -0
  334. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_cate_forest.py +0 -0
  335. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_cate_phase2_stress.py +0 -0
  336. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_cate_policy.py +0 -0
  337. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_cate_select.py +0 -0
  338. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_cate_stress.py +0 -0
  339. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_conformance.py +0 -0
  340. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_data.py +0 -0
  341. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_datasets.py +0 -0
  342. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_decisions.py +0 -0
  343. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_decode.py +0 -0
  344. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_decode_knowledge.py +0 -0
  345. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_did.py +0 -0
  346. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery.py +0 -0
  347. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_bootstrap.py +0 -0
  348. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_boss.py +0 -0
  349. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_fci.py +0 -0
  350. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_ges.py +0 -0
  351. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_glasso.py +0 -0
  352. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_grasp.py +0 -0
  353. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_hc.py +0 -0
  354. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_lingam.py +0 -0
  355. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_pc.py +0 -0
  356. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_pcmci.py +0 -0
  357. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_rfci.py +0 -0
  358. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_discovery_score.py +0 -0
  359. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_docs_welt_forecast_page.py +0 -0
  360. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_docs_welt_page.py +0 -0
  361. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_docsite.py +0 -0
  362. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_evaluate_graph.py +0 -0
  363. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_backtest.py +0 -0
  364. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_backtest_mcmc.py +0 -0
  365. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_core.py +0 -0
  366. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_explain.py +0 -0
  367. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_facade.py +0 -0
  368. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_mechanisms.py +0 -0
  369. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_metrics.py +0 -0
  370. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_quantiles.py +0 -0
  371. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_reconcile.py +0 -0
  372. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_staged.py +0 -0
  373. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_structure.py +0 -0
  374. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_forecast_zoo.py +0 -0
  375. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_granger.py +0 -0
  376. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_graph.py +0 -0
  377. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_graph_format.py +0 -0
  378. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_graph_primitives.py +0 -0
  379. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_id_algorithm.py +0 -0
  380. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_id_integration.py +0 -0
  381. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_identification.py +0 -0
  382. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_identification_classes.py +0 -0
  383. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_inductive_miner.py +0 -0
  384. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_its.py +0 -0
  385. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_knowledge.py +0 -0
  386. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_knowledge_soft.py +0 -0
  387. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_kpi_panel.py +0 -0
  388. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_latent_calibrate.py +0 -0
  389. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_latent_certify.py +0 -0
  390. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_latent_facade.py +0 -0
  391. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_latent_golden.py +0 -0
  392. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_latent_identify.py +0 -0
  393. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_latent_marginalize.py +0 -0
  394. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_mediation.py +0 -0
  395. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_native_learners.py +0 -0
  396. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_pag.py +0 -0
  397. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_plugin.py +0 -0
  398. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_process_filters.py +0 -0
  399. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_process_map.py +0 -0
  400. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_refute.py +0 -0
  401. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_reprs.py +0 -0
  402. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_review_fixes.py +0 -0
  403. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_scm.py +0 -0
  404. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_scm_pymc.py +0 -0
  405. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_score_posterior.py +0 -0
  406. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_sharp_edges.py +0 -0
  407. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_sklearn_interop.py +0 -0
  408. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_smoke.py +0 -0
  409. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_synthetic_control.py +0 -0
  410. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_temporal_wiring.py +0 -0
  411. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_user_facing_wording.py +0 -0
  412. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_welt.py +0 -0
  413. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_welt_forecast.py +0 -0
  414. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_welt_isolation.py +0 -0
  415. {ergodic-0.3.0 → ergodic-0.3.2}/tests/test_workflow_net.py +0 -0
@@ -7,6 +7,69 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.2] - 2026-10-24
11
+
12
+ ### Fixed
13
+ - Linear and GCM use `erfc` for normal tails: e.g. z=9 now gives approximately
14
+ 2.26e-19 instead of zero. Values below binary64 range still underflow (z=40);
15
+ they are not floored or presented as accurately representable probabilities.
16
+ PCMCI can change its test count: the documented time-series fixture retains
17
+ all five links, while its unconstrained CI queries change from 49 to 47
18
+ (the knowledge-constrained run remains at 37).
19
+ - `GraphSamples.top(k)` groups numerical weight ties at relative tolerance 1e-12,
20
+ then log-target ties at absolute tolerance 1e-9, anchored at each group's largest
21
+ value, and sorts final ties by named directed-edge tuples. Weights are unchanged.
22
+ - MMPC forward associations within absolute p-value distance 1e-15 now tie by
23
+ variable name; alpha decisions keep unrounded p-values. This stabilizes greedy
24
+ admission when tiny tails split, without claiming invariance to variable renaming.
25
+ On 150 registered seven-node worlds, the tie change alone changed no skeletons
26
+ (137 total skeleton errors before and after); PC/FCI/RFCI were also unchanged.
27
+
28
+ ### Documentation
29
+ - GCM's default KNN remains uncalibrated. A registered direct-CI study (200 paired
30
+ seeds per family/sample size) found 7.5–12% null rejection at nominal 5%.
31
+ OLS failed on nonlinear conditional means, so neither qualified as a general
32
+ default. Discover and graph-testing guides now disclose the measured limits.
33
+
34
+ ## [0.3.1] - 2026-10-20
35
+
36
+ Correctness and inference safeguards from the pre-release audit (API-179 through
37
+ API-182). No new model families or hosted capabilities are added.
38
+
39
+ ### Fixed
40
+ - Fitted CATE predictors reuse their training category vocabulary. Reordering
41
+ categorical metadata no longer swaps predictions; unknown categories and
42
+ incompatible prediction schemas fail explicitly.
43
+ - `estimate_effect(strategy="t_learner")` selects separate treatment-arm models,
44
+ consistently with `cate(method="t_learner")`. Contradictory alias settings fail.
45
+ - GCM refuses constant endpoints, interpolating nuisance fits and degenerate
46
+ residual products rather than returning a misleading independence p-value.
47
+ - Failed cross-fitted heterogeneity rankings no longer fall back to in-sample
48
+ rankings for inferential summaries or CATE model selection.
49
+ DML has no honest out-of-fold ranking proxy, so its GATES, CLAN, RATE and
50
+ calibration summaries now refuse too; BLP and descriptive effects remain available.
51
+
52
+ ### Changed
53
+ - Frequentist AIPW uses cross-fitting by default. Explicit in-sample mode remains
54
+ available; Bayesian nuisance fits retain their documented in-sample posterior
55
+ path. Results report the resolved mode and propensity diagnostics.
56
+ - Poor estimated overlap triggers diagnostics and warnings; severely degenerate
57
+ propensity fits fail by default. An explicit override does not certify valid
58
+ inference, and no rows are silently trimmed.
59
+ - Generic instrumental-variable estimates are labeled `linear_iv`. A binary
60
+ complier `late` interpretation requires explicit assumptions and the supported
61
+ treatment/instrument configuration. Scalar results retain identification.
62
+
63
+ ### Migration
64
+
65
+ Point estimates and intervals can change because of corrected fitting and method
66
+ selection. Set seeds explicitly when comparing runs. To request the old parametric
67
+ AIPW recipe, use `mode="in_sample"`; this does not make arbitrary interpolating
68
+ learners valid. Inspect overlap diagnostics rather than suppressing failures.
69
+ Existing code that reads the IV target string must accept `linear_iv` for generic
70
+ IV coefficients. See the effect-estimation and heterogeneous-effect guides for
71
+ precise modes, assumptions and failure behavior.
72
+
10
73
  ## [0.3.0] - 2026-10-17
11
74
 
12
75
  To be released together with welt-client 0.14 (API-164): decoding with domain knowledge
@@ -943,7 +1006,9 @@ inference, discovery, process, SCM, and forecast pillars.
943
1006
  silently with single-graph semantics; it now dispatches on the face, and
944
1007
  mixed graphs raise instead of being misread.
945
1008
 
946
- [Unreleased]: https://github.com/ergodic-ai/ergodic/compare/v0.3.0...HEAD
1009
+ [Unreleased]: https://github.com/ergodic-ai/ergodic/compare/v0.3.2...HEAD
1010
+ [0.3.2]: https://github.com/ergodic-ai/ergodic/compare/v0.3.1...v0.3.2
1011
+ [0.3.1]: https://github.com/ergodic-ai/ergodic/compare/v0.3.0...v0.3.1
947
1012
  [0.3.0]: https://github.com/ergodic-ai/ergodic/compare/v0.2.1...v0.3.0
948
1013
  [0.2.1]: https://github.com/ergodic-ai/ergodic/compare/v0.2.0...v0.2.1
949
1014
  [0.2.0]: https://github.com/ergodic-ai/ergodic/compare/v0.1.1...v0.2.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ergodic
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Causal AI: causal discovery, causal inference, and process intelligence.
5
5
  Project-URL: Homepage, https://docs.ergodic.dev
6
6
  Project-URL: Documentation, https://docs.ergodic.dev
@@ -89,7 +89,7 @@ Alarm --> MaryCalls
89
89
  Alarm <-- Burglary
90
90
  Alarm <-- Earthquake
91
91
  Identification(adjustment: P(JohnCalls | do(Burglary)) = sum_z P(JohnCalls | Burglary, z) P(z), with z = {Earthquake})
92
- EffectEstimate(aipw, analytic: ate=0.7982, se=0.0333, 95% CI [0.733, 0.863], n=10000)
92
+ EffectEstimate(aipw, analytic: ate=0.7997, se=0.0337, 95% CI [0.734, 0.866], n=10000)
93
93
  ```
94
94
 
95
95
  The true effect, from the network's own tables, is 0.793: inside the interval. The
@@ -43,7 +43,7 @@ Alarm --> MaryCalls
43
43
  Alarm <-- Burglary
44
44
  Alarm <-- Earthquake
45
45
  Identification(adjustment: P(JohnCalls | do(Burglary)) = sum_z P(JohnCalls | Burglary, z) P(z), with z = {Earthquake})
46
- EffectEstimate(aipw, analytic: ate=0.7982, se=0.0333, 95% CI [0.733, 0.863], n=10000)
46
+ EffectEstimate(aipw, analytic: ate=0.7997, se=0.0337, 95% CI [0.734, 0.866], n=10000)
47
47
  ```
48
48
 
49
49
  The true effect, from the network's own tables, is 0.793: inside the interval. The
@@ -170,10 +170,10 @@ adjusted
170
170
  # [estimating effects](../learn/03-estimating-effects.py); this page trusts the default and
171
171
  # reads the number.
172
172
  #
173
- # The number is about -222 g, with a 95 percent interval that runs roughly -273 to -172. So
174
- # closing the back door moved the estimate by some 50 g, from -275 toward zero. That 50 g was
173
+ # The number is about -220 g, with a 95 percent interval that runs roughly -271 to -168. So
174
+ # closing the back door moved the estimate by some 56 g, from -275 toward zero. That 56 g was
175
175
  # the imbalance from the balance table: the part of the raw gap that was really the mothers
176
- # differing, not the smoking. What's left, -222 g, is the effect of smoking with those
176
+ # differing, not the smoking. What's left, -220 g, is the effect of smoking with those
177
177
  # differences held fixed.
178
178
 
179
179
  # %%
@@ -190,8 +190,8 @@ summary.round(1)
190
190
  # %% [markdown]
191
191
  # The published adjusted figure on this same extract is about -231 g: the doubly robust (AIPW)
192
192
  # estimate in the Stata treatment-effects manual, with a standard error of 26 g. Our
193
- # adjustment lands at -222 g, well inside that interval. We started 44 g away from the
194
- # published number and ended 9 g away, having moved in the right direction for the right
193
+ # adjustment lands at -220 g, well inside that interval. We started 44 g away from the
194
+ # published number and ended 12 g away, having moved in the right direction for the right
195
195
  # reason. The remaining gap is down to which confounders enter and how each model is fit (the
196
196
  # manual's own estimators range from about -210 to -240 g), and the published interval and
197
197
  # ours overlap heavily. This is what a closed back door looks like on real data: not the
@@ -200,7 +200,7 @@ summary.round(1)
200
200
  #
201
201
  # ## What we had to assume
202
202
  #
203
- # Be exact about the claim. The -222 g holds if the 17 measured confounders close every back
203
+ # Be exact about the claim. The -220 g holds if the 17 measured confounders close every back
204
204
  # door between smoking and birthweight. That is an assumption about the world, and no amount
205
205
  # of data inside this table can prove it. If something we didn't measure moves both smoking
206
206
  # and birthweight, say a genetic factor, or diet, or stress, then a path is still open and
@@ -234,7 +234,7 @@ print(f"placebo estimate: {ref.refuted_value:+.1f} g (mean over 20 scrambles)")
234
234
  print(f"verdict: {'passed' if ref.passed else 'FAILED'}")
235
235
 
236
236
  # %% [markdown]
237
- # The real estimate is -222 g; the placebo, averaged over 20 random shuffles, is about +3 g,
237
+ # The real estimate is -220 g; the placebo, averaged over 20 random shuffles, is about +2 g,
238
238
  # a rounding error next to the real effect. The pipeline finds nothing when there's nothing
239
239
  # to find. That's a green light, and it's worth being clear about what color of green. It
240
240
  # rules out one failure mode, a bug or a quirk manufacturing an effect from noise. It says
@@ -243,7 +243,7 @@ print(f"verdict: {'passed' if ref.passed else 'FAILED'}")
243
243
  # fire inspection.
244
244
  #
245
245
  # That's the case, end to end. A balance table that showed the bias before any model ran, a
246
- # naive -275 g that the imbalance inflated, an adjusted -222 g that landed on the published
246
+ # naive -275 g that the imbalance inflated, an adjusted -220 g that landed on the published
247
247
  # neighborhood, and a placebo check that came back clean. The one input the data couldn't
248
248
  # supply was the graph: the arrows that said which 17 variables to adjust for and which arrow
249
249
  # to measure. Here we knew them from how the records are collected. When you don't, you can
@@ -121,7 +121,7 @@ naive = estimate_effect(dag([f"{ds.treatment} -> {ds.outcome}"]), ds.data, ds.tr
121
121
  print(f"naive gap: ${naive.value:,.0f}")
122
122
 
123
123
  # %% [markdown]
124
- # About $19,559, the headline. The balance table already told us why it's too big. Higher
124
+ # About $19,560, the headline. The balance table already told us why it's too big. Higher
125
125
  # earners are both more often eligible and wealthier, so the contrast sweeps the income gap
126
126
  # into the effect and reports the sum. The single arrow is a claim, that nothing else moves
127
127
  # both eligibility and wealth, and the table is nine counts against it. Income alone, an SMD
@@ -168,9 +168,10 @@ lo, hi = ate.interval()
168
168
  print(f"adjusted effect: ${ate.value:.2f} CI [{lo:.1f}, {hi:.1f}] truth ${tau.mean():.2f}")
169
169
 
170
170
  # %% [markdown]
171
- # The adjusted effect lands at 23.65 dollars, interval 22.4 to 24.9, right on the planted 23.39. The
172
- # confounding is gone, and the honest read of the average perk is now in front of us: it costs 25
173
- # and returns 23, so rolled out to everyone it loses a little money on every customer. A team that
171
+ # The adjusted effect lands at 23.87 dollars, interval 22.4 to 25.3, close to the planted 23.39.
172
+ # It costs 25 and returns about 24: the point estimate suggests a small average loss, but the
173
+ # interval includes break-even. Under the graph assumptions, adjustment removes the measured
174
+ # confounding; it does not prove the assumptions. A team that
174
175
  # stopped here would kill the program. That would be a mistake, and the reason is the second error.
175
176
  #
176
177
  # ## Predicted value is not movable value
@@ -345,7 +346,7 @@ print(f"real effect : ${ref.estimand_value:.2f}")
345
346
  print(f"placebo (mean) : ${ref.refuted_value:.2f} p = {ref.p_value:.3f} passed: {ref.passed}")
346
347
 
347
348
  # %% [markdown]
348
- # Scrambled, the effect collapses to roughly zero, far from the real 23.65, and the placebo runs
349
+ # Scrambled, the effect collapses to roughly zero, far from the real 23.87, and the placebo runs
349
350
  # almost never reach it. That rules out a leak firing on noise. It says nothing about an unmeasured
350
351
  # trait, some loyalty the columns don't capture, that pushes a customer toward both the perk and a
351
352
  # fat balance.
@@ -186,11 +186,15 @@ print(f"adjusted effect : {est.value:+.3f} CI [{lo:.3f}, {hi:.3f}]")
186
186
  print(f"truth : {true_effect:+.3f}")
187
187
 
188
188
  # %% [markdown]
189
- # The sign flips. The naive plus 22 points becomes minus 11, interval minus 12.4 to minus 10.2,
189
+ # The sign flips. The naive plus 22 points becomes minus 13.1, interval minus 15.6 to minus 10.6,
190
190
  # straddling the planted minus 12.2. Once the comparison holds risk fixed, the offer cuts churn by
191
- # about 11 points, close to the truth. The program the quarterly numbers condemned is preventing
191
+ # about 13 points, close to the truth. The program the quarterly numbers condemned is preventing
192
192
  # real departures, not causing them. The naive table had it exactly backwards.
193
193
  #
194
+ # The fit also warns about limited overlap: 28.4% of propensity estimates are in the
195
+ # tails outside [0.01, 0.99], and 1.9% require clipping. Treat the estimate cautiously;
196
+ # adjustment does not create comparisons where the data provides little support.
197
+ #
194
198
  # ## Where the data runs out
195
199
  #
196
200
  # Adjustment worked, but it leans on having low-risk and high-risk customers in both groups to
@@ -361,7 +365,7 @@ print(f"placebo (mean) : {ref.refuted_value:+.3f} p = {ref.p_value:.3f} pass
361
365
  # tests the pipeline, not the assumption.
362
366
  #
363
367
  # Two readings of the same quarter. The naive table said the save program triples churn and should
364
- # die. Holding risk fixed, the program cuts churn by about 11 points and should grow, aimed at the
368
+ # die. Holding risk fixed, the program cuts churn by about 13 points and should grow, aimed at the
365
369
  # customers it can move rather than the ones most likely to leave. The companion
366
370
  # [lifetime-value study](06-clv.py) tells the mirror-image story on a continuous outcome, where the
367
371
  # trap is targeting the valuable instead of the movable. For the methods, see
@@ -193,7 +193,7 @@ lo_d, hi_d = disc.interval()
193
193
  print(f"discovered graph: {disc.value:+.2f} h 95% CI [{lo_d:+.2f}, {hi_d:+.2f}]")
194
194
 
195
195
  # %% [markdown]
196
- # Both routes print +4.89 with an interval covering the planted five, a long way from the
196
+ # Both routes print +4.87 with an interval covering the planted five, a long way from the
197
197
  # variant table's 10.2. They agree to the printed digit because both graphs close the same
198
198
  # back door, adjusting for complexity; the hand graph adjusts for workload too, which is
199
199
  # unrelated to the review decision here and moves nothing visible. Discovery found the
@@ -46,15 +46,15 @@ print(identify_effect(hidden, smoking, weight))
46
46
  ```
47
47
 
48
48
  ```output from="assumptions_demo.py"
49
- 17 confounders: EffectEstimate(aipw, analytic: ate=-222.3, se=25.8, 95% CI [-273, -172], n=4642)
49
+ 17 confounders: EffectEstimate(aipw, analytic: ate=-219.5, se=26.3, 95% CI [-271, -168], n=4642)
50
50
  no confounders: EffectEstimate(aipw, analytic: ate=-275.3, se=21.2, 95% CI [-317, -234], n=4642)
51
51
  Identification(not identifiable, ['mbsmoke'] -> ['bweight'])
52
52
  ```
53
53
 
54
54
  - **Step 2.** With all 17 confounders in the graph, smoking lowers birthweight by
55
- about 222 g.
55
+ about 220 g.
56
56
  - **Step 3.** With none, the estimate is the raw difference, about 275 g. Nothing in
57
- the data says which graph is right; the 53 g gap is your assumption at work.
57
+ the data says which graph is right; the 56 g gap is your assumption at work.
58
58
  - **Step 4.** With an unmeasured cause `U` of both smoking and birthweight, and no
59
59
  mediator or instrument to route around it, the effect is not identifiable. ergodic
60
60
  says so instead of returning a number.
@@ -124,10 +124,15 @@ heuristic except A*, and GES returns the mode's equivalence class as a CPDAG.
124
124
 
125
125
  ## Give GCM a learner
126
126
 
127
- `GCM` turns any regression model into a test. `learner=` takes an alias (`"ols"`,
127
+ `GCM` uses a regression model to form a residual covariance test. `learner=` takes an alias (`"ols"`,
128
128
  `"knn"`) or any scikit-learn regressor, such as
129
129
  `RandomForestRegressor(min_samples_leaf=20)`. The default is the built-in
130
- nearest-neighbours regressor.
130
+ nearest-neighbours regressor. Fits and residuals use the same rows. A learner
131
+ that interpolates its training targets, such as an unrestricted decision tree,
132
+ leaves no residual variation and raises `ValueError`. Constant endpoints and
133
+ constant residual products also raise: GCM cannot form a valid p-value in these
134
+ cases. Use a less flexible learner when a fit removes all residual variation.
135
+ Nonzero residual variation alone does not establish a learner's calibration.
131
136
 
132
137
  Give the data in one place. Inside `discover`, build the test without data, as in
133
138
  step 5:
@@ -166,3 +171,17 @@ and restrict the search to the screen (see
166
171
  - [Python reference](reference-discovery.md): every method, test and score.
167
172
 
168
173
  </div>
174
+
175
+ !!! caution "GCM learner calibration"
176
+ The default GCM learner remains KNN; it is not a generally calibrated CI test.
177
+ A paired 200-seed direct-CI diagnostic at alpha 0.05 found null rejection rates
178
+ of 9.5%/7.5% (linear), 12%/10% (quadratic), and 10%/9% (sine), at n=200/1000.
179
+ OLS gave 7.5%/5%, 100%/100%, and 99%/100%, respectively: replacing KNN with
180
+ OLS would fail badly for these nonlinear conditional means. Neither qualified
181
+ across all cells of the registered 2–8% investigation band, so the default
182
+ is unchanged. All cells used 200 trials with zero refusals; 95% Wilson intervals
183
+ for the KNN rates span roughly 4.6–17.2%. These are direct-CI results, distinct
184
+ from the earlier ~30% rejection of true graphs by Fisher's combined test.
185
+ Learner choice must match the conditional mean; these diagnostics do not
186
+ establish calibration for arbitrary data or guarantee detection of dependence.
187
+ The reproducible protocol is in `scripts/audits/qualify_gcm.py`.
@@ -150,3 +150,17 @@ decodes the model's edge probabilities locally. See
150
150
  - [Python reference](reference-discovery.md): every method and its options.
151
151
 
152
152
  </div>
153
+
154
+ !!! caution "GCM learner calibration"
155
+ The default GCM learner remains KNN; it is not a generally calibrated CI test.
156
+ A paired 200-seed direct-CI diagnostic at alpha 0.05 found null rejection rates
157
+ of 9.5%/7.5% (linear), 12%/10% (quadratic), and 10%/9% (sine), at n=200/1000.
158
+ OLS gave 7.5%/5%, 100%/100%, and 99%/100%, respectively: replacing KNN with
159
+ OLS would fail badly for these nonlinear conditional means. Neither qualified
160
+ across all cells of the registered 2–8% investigation band, so the default
161
+ is unchanged. All cells used 200 trials with zero refusals; 95% Wilson intervals
162
+ for the KNN rates span roughly 4.6–17.2%. These are direct-CI results, distinct
163
+ from the earlier ~30% rejection of true graphs by Fisher's combined test.
164
+ Learner choice must match the conditional mean; these diagnostics do not
165
+ establish calibration for arbitrary data or guarantee detection of dependence.
166
+ The reproducible protocol is in `scripts/audits/qualify_gcm.py`.
@@ -0,0 +1,168 @@
1
+ # Estimate an average effect
2
+
3
+ Write down which variables confound a treatment and an outcome, then estimate the
4
+ average effect with an interval. The example measures what smoking during pregnancy
5
+ does to birthweight.
6
+
7
+ !!! need "You'll need"
8
+ - ergodic, installed with `pip install "ergodic"`.
9
+ - A list of confounders: variables that affect both the treatment and the outcome.
10
+
11
+ ## Run the program
12
+
13
+ The bundled birthweight data is the extract of US natality records used by Cattaneo
14
+ (2010): 4,642 singleton births in Pennsylvania, with the mother's smoking, the baby's
15
+ weight in grams and 17 background variables.
16
+
17
+ ```python file="average_effect.py"
18
+ from ergodic import dag, estimate_effect
19
+ from ergodic.datasets import load_birthweight
20
+
21
+ # 1. Load 4,642 births: the mother's smoking, the birthweight and 17 confounders.
22
+ births = load_birthweight()
23
+ smoking, weight = births.treatment, births.outcome
24
+ print(smoking, "->", weight, "with", len(births.confounders), "confounders")
25
+
26
+ # 2. Draw the graph: each confounder affects both smoking and birthweight.
27
+ edges = [f"{c} -> {smoking}" for c in births.confounders]
28
+ edges += [f"{c} -> {weight}" for c in births.confounders]
29
+ graph = dag([*edges, f"{smoking} -> {weight}"])
30
+
31
+ # 3. Compare the raw difference in mean birthweight.
32
+ rows = births.data.to_pandas()
33
+ means = rows.groupby(smoking, observed=True)[weight].mean()
34
+ print(f"raw difference: {means['smoker'] - means['nonsmoker']:.1f} g")
35
+
36
+ # 4. Estimate the average effect, adjusting for the confounders.
37
+ estimate = estimate_effect(
38
+ graph, births.data, smoking, weight, treated=births.treated, random_state=0
39
+ )
40
+ print(estimate)
41
+ low, high = estimate.interval()
42
+ print(f"95% interval: {low:.0f} g to {high:.0f} g")
43
+ ```
44
+
45
+ ```output from="average_effect.py"
46
+ mbsmoke -> bweight with 17 confounders
47
+ raw difference: -275.3 g
48
+ EffectEstimate(aipw, analytic: ate=-219.5, se=26.3, 95% CI [-271, -168], n=4642)
49
+ 95% interval: -271 g to -168 g
50
+ ```
51
+
52
+ - Babies of smokers weigh 275 g less on average. Part of that gap comes from the
53
+ confounders, not from smoking.
54
+ - Adjusted for the confounders, smoking lowers birthweight by about 220 g. The 95%
55
+ interval runs from 271 g to 168 g lower. The Stata treatment-effects manual's
56
+ doubly robust estimate on the same extract is −231 g (standard error 26 g), well
57
+ inside that interval; `births.target` records it with its source.
58
+ - `aipw` is the augmented inverse-probability-weighted estimator. It combines an
59
+ outcome model and a propensity model, and stays consistent if either one is right.
60
+
61
+ ## Draw the graph
62
+
63
+ The graph is your assumption about the world. Each confounder points at both the
64
+ treatment and the outcome:
65
+
66
+ ```python
67
+ edges = [f"{c} -> {smoking}" for c in births.confounders]
68
+ edges += [f"{c} -> {weight}" for c in births.confounders]
69
+ graph = dag([*edges, f"{smoking} -> {weight}"])
70
+ ```
71
+
72
+ `estimate_effect` reads the adjustment set from this graph. You never pass the
73
+ confounders to it directly.
74
+
75
+ ## Choose the treated level
76
+
77
+ `treated=` names the level that counts as treatment. Here the `mbsmoke` column holds
78
+ `"smoker"` and `"nonsmoker"`, and `births.treated` is `"smoker"`. The estimate is
79
+ the mean outcome if everyone were treated minus the mean if no one were.
80
+
81
+ !!! caution "The estimate is only as good as the graph"
82
+ It assumes no confounder is missing from the list. A variable that affects both
83
+ smoking and birthweight, but is not in the data, biases the result. No
84
+ check on this page can detect such a variable.
85
+
86
+ By default the propensity model is logistic regression. The outcome model is
87
+ ordinary least squares for a continuous outcome, as here, and logistic regression
88
+ for a binary one. Pass `learner=` (an alias, a learner, or a mapping from slot to
89
+ learner) to use other models.
90
+
91
+ ## Cross-fitting and overlap
92
+
93
+ AIPW now defaults to `mode="auto"`: frequentist nuisances use held-out predictions
94
+ from five shuffled, treatment-stratified folds. Set `n_folds=` and `random_state=`
95
+ explicitly for a recorded protocol. If no seed is supplied, AIPW uses seed 0 for
96
+ the split. Diagnostics report `n_folds_requested`, the number of nonempty folds
97
+ actually used as `n_folds`, and `crossfit_seed`. Both arms need at least two rows;
98
+ fold counts must be integers from 2 through the number of rows.
99
+
100
+ Cross-fitting prevents evaluating nuisance residuals on their own fitting rows.
101
+ It does not make every learner safe: valid ATE intervals also need overlap,
102
+ suitable nuisance convergence rates and independent observations. An unrestricted
103
+ decision-tree propensity model can predict only 0 and 1 even on held-out rows;
104
+ the default guard refuses that fit. This remains AIPW, with its counterfactual
105
+ outcome regressions and propensity residual corrections, rather than a partially
106
+ linear DML model. See [Chernozhukov et al.](https://arxiv.org/abs/1608.00060).
107
+
108
+ `mode="in_sample"` selects the old parametric fitting path explicitly. Its
109
+ influence-score interval needs appropriate parametric or empirical-process
110
+ conditions; unrestricted interpolating learners are not qualified. With any
111
+ Bayesian nuisance, `auto` preserves the in-sample posterior or posterior-mean
112
+ fallback without repeatedly fitting a sampler. `cross_fitted=0` and
113
+ `bayesian_in_sample=1` report that choice. Explicit `mode="cross_fitted"` refuses
114
+ Bayesian slots because cross-fitted posterior inference is not implemented.
115
+
116
+ Inspect `estimate.diagnostics`: `propensity_min`, `propensity_max`, and the
117
+ `propensity_q01`, `q05`, `median`, `q95` and `q99` entries use raw predictions,
118
+ before clipping. `propensity_clipped_fraction` records clipping;
119
+ `treated_effective_n`, `control_effective_n`, and each arm's `weight_max` and
120
+ `weight_q99` describe the clipped ATE inverse weights. `att_control_effective_n`
121
+ and `att_control_weight_max` describe ATT odds weights. For a Bayesian posterior,
122
+ these summaries use the posterior mean propensity; `posterior_draw_clipped_fraction`
123
+ and `posterior_draw_tail_fraction` separately summarize all propensity draw cells.
124
+
125
+ AIPW, IPW and stratification accept `overlap="warn"` (default), `"raise"`, or
126
+ `"ignore"`, plus `overlap_threshold=0.01` and `clip=0.001`. The default warns when
127
+ at least 10% of raw predictions lie outside `[0.01, 0.99]`, or an arm's effective
128
+ size falls below 10% of its observed count. It refuses when at least 50% require
129
+ clipping. `raise` also refuses moderate warnings; `ignore` explicitly overrides
130
+ these diagnostic guards, but invalid probabilities always fail. These thresholds
131
+ are diagnostic choices, not a test of population positivity. A result below the
132
+ thresholds is not a certificate of valid inference. Clipping cannot restore
133
+ missing support. No rows are trimmed; stratification refuses an occupied stratum
134
+ with a missing arm instead of dropping it and changing the estimand.
135
+
136
+ ## Instrumental-variable targets
137
+
138
+ Instrument estimators now default to `target="linear_iv"`, including continuous
139
+ treatment and PLIV. This replaces the old automatic `late` label. The numerical
140
+ coefficient is unchanged; consumers checking `EffectTarget.LATE` should migrate to
141
+ `EffectTarget.LINEAR_IV` when they mean the generic linear IV coefficient.
142
+
143
+ A complier LATE requires additional assumptions. For one unconditional binary
144
+ instrument and a binary treatment, 2SLS or Wald accepts `target="late"` only with
145
+ `late_assumptions=True`. This asserts exogeneity, exclusion, relevance,
146
+ monotonicity, consistency and no interference; it does not verify them. Adjusted
147
+ or multivariate instruments and PLIV retain the generic linear IV target.
148
+ See [Angrist, Imbens and Rubin](https://doi.org/10.1080/01621459.1996.10476902).
149
+
150
+ Scalar results retain `identification`; IV results also state `assumptions`.
151
+ Their `first_stage_partial_r2`, `first_stage_f` and degrees of freedom describe
152
+ first-stage strength. PLIV computes them on its cross-fitted residuals, marked by
153
+ `first_stage_residualized=1`. The classical F statistic is descriptive, assumes a
154
+ homoskedastic linear first stage for its usual interpretation, and does not test
155
+ exclusion or exogeneity. Values below 10 warn; larger values do not establish
156
+ instrument validity. Ordinary normal IV intervals can be unreliable with weak
157
+ instruments; weak-IV robust intervals are not implemented here.
158
+
159
+ ## Next steps
160
+
161
+ <div class="cards" markdown="1">
162
+
163
+ - [Refute an estimate](refute-an-estimate.md): stress-test this estimate with placebo and subset checks.
164
+ - [Average an effect over the graphs](evaluate-a-graph.md#average-an-effect-over-the-graphs): estimate across a posterior over DAGs.
165
+ - [Estimate heterogeneous effects](heterogeneous-effects.md): find who gains more or less from the treatment.
166
+ - [Python reference](reference-inference.md): every option of `estimate_effect`.
167
+
168
+ </div>
@@ -440,10 +440,10 @@ print(round(result.zero_effect_mass, 3), result.unidentified_mass, result.trunca
440
440
  0.811
441
441
  0.404
442
442
  adjustment_set mass n_dags estimate std_error
443
- 0 c 0.757 3 1.969 0.066
443
+ 0 c 0.757 3 1.970 0.066
444
444
  1 (empty) 0.156 6 3.563 0.072
445
445
  2 (no directed path) 0.087 16 0.000 0.000
446
- 2.046 0.857
446
+ 2.047 0.857
447
447
  0.087 0.0 0.0
448
448
  ```
449
449
 
@@ -525,3 +525,10 @@ Nothing touches numpy's global random state, and the same `seed` gives the same
525
525
  - [Python reference](reference-discovery.md): `graph_posterior`, `evaluate_graph`, `GraphSamples`.
526
526
 
527
527
  </div>
528
+
529
+ `GraphSamples.top(k)` lists the heaviest DAGs first. Weight ties within 1e-12
530
+ relative to the group's maximum are ordered by log target; log-target ties
531
+ within absolute 1e-9 are ordered by sorted named directed edges. Groups are
532
+ anchored at their maximum, not chained through approximate pairwise equality.
533
+ This removes floating-point noise from equivalent-graph ordering without
534
+ rounding returned masses or changing the posterior.
@@ -101,6 +101,9 @@ Demirer, Duflo and Fernández-Val, 2025; Yadlowsky and others, 2024).
101
101
  `method=` picks the learner. The default `"dr_learner"` is doubly robust. Other
102
102
  names: `"r_learner"`, `"x_learner"`, `"t_learner"`, `"s_learner"`, `"dml"` and
103
103
  `"causal_forest"`. `learner=` fills the nuisance models, as in `estimate_effect`.
104
+ The same S/T names work with `estimate_effect(strategy=...)`: `t_learner` fits
105
+ separate outcome models for each arm, and `s_learner` fits one pooled model.
106
+ Passing a contradictory `per_arm=` flag raises an error in either facade.
104
107
 
105
108
  - The uncertainty depends on the method. The DR-learner with its default linear
106
109
  final model has analytic standard errors. `"causal_forest"` (Athey, Tibshirani
@@ -126,6 +129,17 @@ names: `"r_learner"`, `"x_learner"`, `"t_learner"`, `"s_learner"`, `"dml"` and
126
129
  - `explain()` describes the units the model was fit on. To score new rows, fit an
127
130
  estimator such as `DRLearner` with `fit(identification, data)`, then call its
128
131
  `cate(new_rows)`. That result has no scores, so its `explain()` raises.
132
+ - New rows use the covariate types and category vocabulary saved during `fit`.
133
+ Column order and categorical metadata order can change, and a batch can omit
134
+ known category levels. Unknown values, missing required columns, changed
135
+ covariate types and missing covariate values raise an error. Treatment and
136
+ outcome columns are unnecessary when scoring new rows.
137
+ - If a fold fails while building the out-of-fold effect proxy, `explain().blp()`
138
+ still projects the cross-fit scores on covariates. GATES, CLAN, RATE and calibration
139
+ refuse to return intervals or p-values and report the ranking failure. They
140
+ never substitute the fitted in-sample effects. `select_cate` also refuses a
141
+ candidate without an out-of-fold proxy. DML supplies constant effects with no
142
+ out-of-fold CATE ranking, so it also refuses these summaries while retaining BLP.
129
143
  - `effects.ate()` returns the average effect. With the DR-learner it is the mean of
130
144
  the doubly robust scores, whose standard error is valid (the per-unit standard
131
145
  errors share one fitted model, so they are never combined as if independent); for
@@ -72,7 +72,7 @@ for module, use in optional.items():
72
72
  ```
73
73
 
74
74
  ```output from="install_check.py" lines="1"
75
- ergodic 0.3.0 writes ergodic.graph/1
75
+ ergodic 0.3.2 writes ergodic.graph/1
76
76
  ```
77
77
 
78
78
  - **Step 1.** The version, and `ergodic.graph/1`: the format `MixedGraph.to_dict()`
@@ -54,18 +54,18 @@ print(check)
54
54
  ```
55
55
 
56
56
  ```output from="refute_effect.py"
57
- EffectEstimate(aipw, analytic: ate=-222.3, se=25.8, 95% CI [-273, -172], n=4642)
58
- Refutation(placebo_treatment: estimate=-222.3, under perturbation=4.981, p=0.0099, passed, n=100)
59
- spread of the perturbed estimates: 21.7 g
60
- Refutation(random_common_cause: estimate=-222.3, under perturbation=-222.1, passed, n=100)
61
- spread of the perturbed estimates: 0.8 g
62
- Refutation(data_subset: estimate=-222.3, under perturbation=-219, passed, n=100)
63
- spread of the perturbed estimates: 13.8 g
64
- Refutation(placebo_treatment: estimate=-275.3, under perturbation=4.529, p=0.0099, passed, n=100)
57
+ EffectEstimate(aipw, analytic: ate=-219.5, se=26.3, 95% CI [-271, -168], n=4642)
58
+ Refutation(placebo_treatment: estimate=-219.5, under perturbation=4.747, p=0.0099, passed, n=100)
59
+ spread of the perturbed estimates: 21.5 g
60
+ Refutation(random_common_cause: estimate=-219.5, under perturbation=-219.3, passed, n=100)
61
+ spread of the perturbed estimates: 1.0 g
62
+ Refutation(data_subset: estimate=-219.5, under perturbation=-218.7, passed, n=100)
63
+ spread of the perturbed estimates: 14.2 g
64
+ Refutation(placebo_treatment: estimate=-275.3, under perturbation=4.526, p=0.0099, passed, n=100)
65
65
  ```
66
66
 
67
67
  - **Placebo treatment.** With the smoking column shuffled, the effect drops from
68
- −222 g to a few grams. In none of the 100 shuffles was the placebo effect as large
68
+ −220 g to a few grams. In none of the 100 shuffles was the placebo effect as large
69
69
  in size as the real one, so `p` is the smallest value 100 runs can give: 1/101.
70
70
  - **Random common cause.** Adding an independent random confounder to the graph
71
71
  leaves the estimate where it was, within a gram.
@@ -50,7 +50,7 @@ Alarm --> MaryCalls
50
50
  Alarm <-- Burglary
51
51
  Alarm <-- Earthquake
52
52
  Identification(adjustment: P(JohnCalls | do(Burglary)) = sum_z P(JohnCalls | Burglary, z) P(z), with z = {Earthquake})
53
- EffectEstimate(aipw, analytic: ate=0.7982, se=0.0333, 95% CI [0.733, 0.863], n=10000)
53
+ EffectEstimate(aipw, analytic: ate=0.7997, se=0.0337, 95% CI [0.734, 0.866], n=10000)
54
54
  ```
55
55
 
56
56
  - **Step 1.** Five yes/no columns. Burglaries are rare: about 1 row in 100.
@@ -210,3 +210,23 @@ own variables, and they hold in the margin over those variables.
210
210
  - [Python reference](reference-discovery.md): `test_graph` and `GraphTest` in full.
211
211
 
212
212
  </div>
213
+
214
+ !!! caution "GCM learner calibration"
215
+ The default GCM learner remains KNN; it is not a generally calibrated CI test.
216
+ A paired 200-seed direct-CI diagnostic at alpha 0.05 found null rejection rates
217
+ of 9.5%/7.5% (linear), 12%/10% (quadratic), and 10%/9% (sine), at n=200/1000.
218
+ OLS gave 7.5%/5%, 100%/100%, and 99%/100%, respectively: replacing KNN with
219
+ OLS would fail badly for these nonlinear conditional means. Neither qualified
220
+ across all cells of the registered 2–8% investigation band, so the default
221
+ is unchanged. All cells used 200 trials with zero refusals; 95% Wilson intervals
222
+ for the KNN rates span roughly 4.6–17.2%. These are direct-CI results, distinct
223
+ from the earlier ~30% rejection of true graphs by Fisher's combined test.
224
+ Learner choice must match the conditional mean; these diagnostics do not
225
+ establish calibration for arbitrary data or guarantee detection of dependence.
226
+ The reproducible protocol is in `scripts/audits/qualify_gcm.py`.
227
+
228
+ Linear and GCM compute two-sided normal tails with `erfc`, so strong statistics
229
+ such as z=9 retain their nonzero p-values (about 2.26e-19). Tails below binary64's
230
+ representable range still become zero, for example z=40. Graph-test aggregation
231
+ retains its existing safeguards for zero tails; no finite float can represent
232
+ those smaller probabilities accurately.
@@ -54,14 +54,14 @@ print(f"{pruned} links pruned; {tests[0]} tests instead of {tests[1]}")
54
54
 
55
55
  ```output from="time_series.py"
56
56
  TimeSeriesDiscoveryResult(pcmci/linear: dag on 9 window nodes, max_lag=2, 5 lagged links, 0 contemporaneous, alpha=0.05, n=496)
57
- ads[t-1] -> ads[t] p=0
58
- ads[t-1] -> visits[t] p=0
59
- sales[t-1] -> sales[t] p=0
60
- visits[t-2] -> sales[t] p=4.4e-15
61
- visits[t-1] -> visits[t] p=0
57
+ ads[t-1] -> ads[t] p=1.9e-38
58
+ ads[t-1] -> visits[t] p=6.1e-35
59
+ sales[t-1] -> sales[t] p=6.1e-34
60
+ visits[t-2] -> sales[t] p=4.5e-15
61
+ visits[t-1] -> visits[t] p=1.5e-26
62
62
  granger: [('ads', 'visits'), ('visits', 'sales')]
63
63
  True
64
- 9 links pruned; 37 tests instead of 49
64
+ 9 links pruned; 37 tests instead of 47
65
65
  ```
66
66
 
67
67
  - **Step 2.** The result holds a window graph over nodes such as `ads[t-1]`. With
@@ -31,7 +31,7 @@ commands = [
31
31
 
32
32
  [versions]
33
33
  # Published versions older than this build that stay in the picker (v<version>/), oldest first.
34
- previous = ["0.2.0", "0.2.1"]
34
+ previous = ["0.2.0", "0.2.1", "0.3.0", "0.3.1"]
35
35
 
36
36
  [[products]]
37
37
  name = "ergodic"