ergodic 0.1.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 (308) hide show
  1. ergodic-0.1.0/.gitignore +50 -0
  2. ergodic-0.1.0/CHANGELOG.md +394 -0
  3. ergodic-0.1.0/LICENSE +202 -0
  4. ergodic-0.1.0/NOTICE +42 -0
  5. ergodic-0.1.0/PKG-INFO +141 -0
  6. ergodic-0.1.0/README.md +101 -0
  7. ergodic-0.1.0/docs/assets/ergodic_logomark_dark.svg +9252 -0
  8. ergodic-0.1.0/docs/assets/ergodic_logomark_light.svg +9241 -0
  9. ergodic-0.1.0/docs/assets/extra.css +128 -0
  10. ergodic-0.1.0/docs/case-studies/01-birthweight.py +251 -0
  11. ergodic-0.1.0/docs/case-studies/02-berkeley.py +258 -0
  12. ergodic-0.1.0/docs/case-studies/03-pension401k.py +479 -0
  13. ergodic-0.1.0/docs/case-studies/04-synthetic-control.py +282 -0
  14. ergodic-0.1.0/docs/case-studies/05-discovery.py +289 -0
  15. ergodic-0.1.0/docs/case-studies/06-clv.py +362 -0
  16. ergodic-0.1.0/docs/case-studies/07-churn.py +369 -0
  17. ergodic-0.1.0/docs/case-studies/index.md +21 -0
  18. ergodic-0.1.0/docs/concepts.md +54 -0
  19. ergodic-0.1.0/docs/data.md +209 -0
  20. ergodic-0.1.0/docs/discovery.md +390 -0
  21. ergodic-0.1.0/docs/forecast.md +395 -0
  22. ergodic-0.1.0/docs/foundations.md +49 -0
  23. ergodic-0.1.0/docs/glossary.md +187 -0
  24. ergodic-0.1.0/docs/graph.md +163 -0
  25. ergodic-0.1.0/docs/identification.md +291 -0
  26. ergodic-0.1.0/docs/includes/abbreviations.md +50 -0
  27. ergodic-0.1.0/docs/index.md +121 -0
  28. ergodic-0.1.0/docs/inference.md +276 -0
  29. ergodic-0.1.0/docs/knowledge.md +126 -0
  30. ergodic-0.1.0/docs/learn/01-why-causality.py +226 -0
  31. ergodic-0.1.0/docs/learn/02-good-and-bad-controls.py +280 -0
  32. ergodic-0.1.0/docs/learn/03-estimating-effects.py +234 -0
  33. ergodic-0.1.0/docs/learn/04-heterogeneous-effects.py +181 -0
  34. ergodic-0.1.0/docs/learn/05-hidden-confounding.py +237 -0
  35. ergodic-0.1.0/docs/learn/06-discovering-structure.py +310 -0
  36. ergodic-0.1.0/docs/learn/07-quasi-experiments.py +352 -0
  37. ergodic-0.1.0/docs/learn/08-process-mining.py +390 -0
  38. ergodic-0.1.0/docs/learn/09-process-intelligence.py +368 -0
  39. ergodic-0.1.0/docs/learn/10-decision-points.py +361 -0
  40. ergodic-0.1.0/docs/learn/11-forecasting.py +415 -0
  41. ergodic-0.1.0/docs/learn/index.md +51 -0
  42. ergodic-0.1.0/docs/panel.md +150 -0
  43. ergodic-0.1.0/docs/pillars.md +44 -0
  44. ergodic-0.1.0/docs/process.md +398 -0
  45. ergodic-0.1.0/docs/reference.md +116 -0
  46. ergodic-0.1.0/docs/scm.md +228 -0
  47. ergodic-0.1.0/docs/tutorial.md +150 -0
  48. ergodic-0.1.0/examples/README.md +23 -0
  49. ergodic-0.1.0/examples/adjustment_birthweight.py +44 -0
  50. ergodic-0.1.0/examples/cate/01_cate_basics.py +93 -0
  51. ergodic-0.1.0/examples/cate/02_meta_learners_compared.py +71 -0
  52. ergodic-0.1.0/examples/cate/03_is_there_heterogeneity.py +79 -0
  53. ergodic-0.1.0/examples/cate/04_where_is_the_effect_higher.py +98 -0
  54. ergodic-0.1.0/examples/cate/05_targeting_with_rate.py +92 -0
  55. ergodic-0.1.0/examples/cate/06_per_unit_uncertainty.py +157 -0
  56. ergodic-0.1.0/examples/cate/07_cate_vs_ite.py +113 -0
  57. ergodic-0.1.0/examples/cate/08_identification_gating.py +100 -0
  58. ergodic-0.1.0/examples/cate/09_case_study_uplift_end_to_end.py +152 -0
  59. ergodic-0.1.0/examples/cate/10_causal_forest.py +115 -0
  60. ergodic-0.1.0/examples/cate/11_policy_trees.py +101 -0
  61. ergodic-0.1.0/examples/cate/12_model_selection.py +93 -0
  62. ergodic-0.1.0/examples/cate/13_case_study_phase2.py +191 -0
  63. ergodic-0.1.0/examples/cate/README.md +79 -0
  64. ergodic-0.1.0/examples/confounding_berkeley.py +41 -0
  65. ergodic-0.1.0/examples/discovery_asia.py +40 -0
  66. ergodic-0.1.0/examples/double_ml_pension.py +48 -0
  67. ergodic-0.1.0/examples/scm/01_three_rungs.py +101 -0
  68. ergodic-0.1.0/examples/scm/02_honesty_and_tipping_point.py +112 -0
  69. ergodic-0.1.0/examples/scm/03_causal_price_optimization.py +105 -0
  70. ergodic-0.1.0/examples/scm/04_counterfactual_recourse.py +113 -0
  71. ergodic-0.1.0/examples/scm/05_counterfactual_fairness.py +113 -0
  72. ergodic-0.1.0/examples/scm/06_causal_attribution.py +89 -0
  73. ergodic-0.1.0/examples/scm/07_but_for_accounting.py +94 -0
  74. ergodic-0.1.0/examples/scm/08_off_policy_evaluation.py +114 -0
  75. ergodic-0.1.0/examples/scm/09_root_cause_attribution.py +101 -0
  76. ergodic-0.1.0/examples/scm/10_frontdoor_unmeasured_confounder.py +87 -0
  77. ergodic-0.1.0/examples/scm/README.md +57 -0
  78. ergodic-0.1.0/examples/synthetic_control_smoking.py +29 -0
  79. ergodic-0.1.0/public/ergodic_logomark_dark.svg +9252 -0
  80. ergodic-0.1.0/public/ergodic_logomark_light.svg +9241 -0
  81. ergodic-0.1.0/pyproject.toml +170 -0
  82. ergodic-0.1.0/src/ergodic/__init__.py +216 -0
  83. ergodic-0.1.0/src/ergodic/data/__init__.py +51 -0
  84. ergodic-0.1.0/src/ergodic/data/_dataset.py +223 -0
  85. ergodic-0.1.0/src/ergodic/data/_eventlog.py +126 -0
  86. ergodic-0.1.0/src/ergodic/data/_frame.py +118 -0
  87. ergodic-0.1.0/src/ergodic/data/_hierarchy.py +126 -0
  88. ergodic-0.1.0/src/ergodic/data/_hts.py +118 -0
  89. ergodic-0.1.0/src/ergodic/data/_panel.py +113 -0
  90. ergodic-0.1.0/src/ergodic/data/_schema.py +311 -0
  91. ergodic-0.1.0/src/ergodic/data/_tabular.py +60 -0
  92. ergodic-0.1.0/src/ergodic/data/_timeseries.py +79 -0
  93. ergodic-0.1.0/src/ergodic/datasets/__init__.py +35 -0
  94. ergodic-0.1.0/src/ergodic/datasets/_bayesnet.py +210 -0
  95. ergodic-0.1.0/src/ergodic/datasets/_bunch.py +43 -0
  96. ergodic-0.1.0/src/ergodic/datasets/_bundled.py +263 -0
  97. ergodic-0.1.0/src/ergodic/datasets/_tables.py +75 -0
  98. ergodic-0.1.0/src/ergodic/datasets/data/birthweight.csv +4643 -0
  99. ergodic-0.1.0/src/ergodic/datasets/data/germany.csv +749 -0
  100. ergodic-0.1.0/src/ergodic/datasets/data/pension401k.csv +9916 -0
  101. ergodic-0.1.0/src/ergodic/datasets/data/smoking.csv +1210 -0
  102. ergodic-0.1.0/src/ergodic/discovery/__init__.py +94 -0
  103. ergodic-0.1.0/src/ergodic/discovery/_bootstrap.py +336 -0
  104. ergodic-0.1.0/src/ergodic/discovery/_ci.py +243 -0
  105. ergodic-0.1.0/src/ergodic/discovery/_discover.py +70 -0
  106. ergodic-0.1.0/src/ergodic/discovery/_encode.py +56 -0
  107. ergodic-0.1.0/src/ergodic/discovery/_granger.py +108 -0
  108. ergodic-0.1.0/src/ergodic/discovery/_lagged.py +172 -0
  109. ergodic-0.1.0/src/ergodic/discovery/_registry.py +199 -0
  110. ergodic-0.1.0/src/ergodic/discovery/_result.py +48 -0
  111. ergodic-0.1.0/src/ergodic/discovery/_score.py +214 -0
  112. ergodic-0.1.0/src/ergodic/discovery/_stats.py +71 -0
  113. ergodic-0.1.0/src/ergodic/discovery/_tsresult.py +68 -0
  114. ergodic-0.1.0/src/ergodic/discovery/algorithms/__init__.py +32 -0
  115. ergodic-0.1.0/src/ergodic/discovery/algorithms/_astar.py +269 -0
  116. ergodic-0.1.0/src/ergodic/discovery/algorithms/_boss.py +339 -0
  117. ergodic-0.1.0/src/ergodic/discovery/algorithms/_fci.py +275 -0
  118. ergodic-0.1.0/src/ergodic/discovery/algorithms/_ges.py +353 -0
  119. ergodic-0.1.0/src/ergodic/discovery/algorithms/_glasso.py +521 -0
  120. ergodic-0.1.0/src/ergodic/discovery/algorithms/_grasp.py +222 -0
  121. ergodic-0.1.0/src/ergodic/discovery/algorithms/_hc.py +284 -0
  122. ergodic-0.1.0/src/ergodic/discovery/algorithms/_lingam.py +361 -0
  123. ergodic-0.1.0/src/ergodic/discovery/algorithms/_mmpc.py +271 -0
  124. ergodic-0.1.0/src/ergodic/discovery/algorithms/_pc.py +287 -0
  125. ergodic-0.1.0/src/ergodic/discovery/algorithms/_pcmci.py +546 -0
  126. ergodic-0.1.0/src/ergodic/discovery/algorithms/_rfci.py +461 -0
  127. ergodic-0.1.0/src/ergodic/discovery/algorithms/_skeleton.py +86 -0
  128. ergodic-0.1.0/src/ergodic/discovery/ci/__init__.py +11 -0
  129. ergodic-0.1.0/src/ergodic/discovery/ci/_discrete.py +68 -0
  130. ergodic-0.1.0/src/ergodic/discovery/ci/_gcm.py +110 -0
  131. ergodic-0.1.0/src/ergodic/discovery/ci/_linear.py +73 -0
  132. ergodic-0.1.0/src/ergodic/discovery/ci/_mixed.py +64 -0
  133. ergodic-0.1.0/src/ergodic/discovery/scores/__init__.py +11 -0
  134. ergodic-0.1.0/src/ergodic/discovery/scores/_bdeu.py +95 -0
  135. ergodic-0.1.0/src/ergodic/discovery/scores/_bic.py +146 -0
  136. ergodic-0.1.0/src/ergodic/discovery/scores/_discrete_bic.py +85 -0
  137. ergodic-0.1.0/src/ergodic/discovery/scores/_mixed_bic.py +111 -0
  138. ergodic-0.1.0/src/ergodic/forecast/__init__.py +220 -0
  139. ergodic-0.1.0/src/ergodic/forecast/_attribution.py +678 -0
  140. ergodic-0.1.0/src/ergodic/forecast/_backtest.py +432 -0
  141. ergodic-0.1.0/src/ergodic/forecast/_baselines.py +237 -0
  142. ergodic-0.1.0/src/ergodic/forecast/_facade.py +734 -0
  143. ergodic-0.1.0/src/ergodic/forecast/_fit.py +1057 -0
  144. ergodic-0.1.0/src/ergodic/forecast/_mechanism.py +411 -0
  145. ergodic-0.1.0/src/ergodic/forecast/_metrics.py +335 -0
  146. ergodic-0.1.0/src/ergodic/forecast/_protocol.py +31 -0
  147. ergodic-0.1.0/src/ergodic/forecast/_reconcile.py +634 -0
  148. ergodic-0.1.0/src/ergodic/forecast/_result.py +347 -0
  149. ergodic-0.1.0/src/ergodic/forecast/_scalers.py +140 -0
  150. ergodic-0.1.0/src/ergodic/forecast/_schedule.py +449 -0
  151. ergodic-0.1.0/src/ergodic/forecast/_staged.py +774 -0
  152. ergodic-0.1.0/src/ergodic/forecast/_structure.py +129 -0
  153. ergodic-0.1.0/src/ergodic/forecast/mechanisms/__init__.py +26 -0
  154. ergodic-0.1.0/src/ergodic/forecast/mechanisms/_bass.py +253 -0
  155. ergodic-0.1.0/src/ergodic/forecast/mechanisms/_ou.py +346 -0
  156. ergodic-0.1.0/src/ergodic/forecast/mechanisms/_structural.py +360 -0
  157. ergodic-0.1.0/src/ergodic/graph/__init__.py +77 -0
  158. ergodic-0.1.0/src/ergodic/graph/_graph.py +598 -0
  159. ergodic-0.1.0/src/ergodic/graph/_io.py +141 -0
  160. ergodic-0.1.0/src/ergodic/graph/_marks.py +28 -0
  161. ergodic-0.1.0/src/ergodic/graph/_meek.py +231 -0
  162. ergodic-0.1.0/src/ergodic/graph/_pag.py +728 -0
  163. ergodic-0.1.0/src/ergodic/graph/_primitives.py +184 -0
  164. ergodic-0.1.0/src/ergodic/graph/_separation.py +231 -0
  165. ergodic-0.1.0/src/ergodic/graph/_surgery.py +80 -0
  166. ergodic-0.1.0/src/ergodic/identification/__init__.py +61 -0
  167. ergodic-0.1.0/src/ergodic/identification/_criteria.py +573 -0
  168. ergodic-0.1.0/src/ergodic/identification/_distribution.py +133 -0
  169. ergodic-0.1.0/src/ergodic/identification/_evaluate.py +172 -0
  170. ergodic-0.1.0/src/ergodic/identification/_functional.py +185 -0
  171. ergodic-0.1.0/src/ergodic/identification/_id.py +276 -0
  172. ergodic-0.1.0/src/ergodic/identification/_ida.py +163 -0
  173. ergodic-0.1.0/src/ergodic/identification/_mediation.py +351 -0
  174. ergodic-0.1.0/src/ergodic/identification/_recognize.py +110 -0
  175. ergodic-0.1.0/src/ergodic/identification/_record.py +47 -0
  176. ergodic-0.1.0/src/ergodic/inference/__init__.py +136 -0
  177. ergodic-0.1.0/src/ergodic/inference/_crossfit.py +63 -0
  178. ergodic-0.1.0/src/ergodic/inference/_design.py +32 -0
  179. ergodic-0.1.0/src/ergodic/inference/_estimate.py +163 -0
  180. ergodic-0.1.0/src/ergodic/inference/_estimator.py +817 -0
  181. ergodic-0.1.0/src/ergodic/inference/_learner.py +140 -0
  182. ergodic-0.1.0/src/ergodic/inference/_perunit.py +313 -0
  183. ergodic-0.1.0/src/ergodic/inference/_refute.py +212 -0
  184. ergodic-0.1.0/src/ergodic/inference/_registry.py +213 -0
  185. ergodic-0.1.0/src/ergodic/inference/_result.py +111 -0
  186. ergodic-0.1.0/src/ergodic/inference/_scores.py +227 -0
  187. ergodic-0.1.0/src/ergodic/inference/_select.py +278 -0
  188. ergodic-0.1.0/src/ergodic/inference/_stats.py +77 -0
  189. ergodic-0.1.0/src/ergodic/inference/_target.py +36 -0
  190. ergodic-0.1.0/src/ergodic/inference/estimators/__init__.py +49 -0
  191. ergodic-0.1.0/src/ergodic/inference/estimators/_did.py +868 -0
  192. ergodic-0.1.0/src/ergodic/inference/estimators/_dml.py +354 -0
  193. ergodic-0.1.0/src/ergodic/inference/estimators/_doubly_robust.py +152 -0
  194. ergodic-0.1.0/src/ergodic/inference/estimators/_forest.py +633 -0
  195. ergodic-0.1.0/src/ergodic/inference/estimators/_frontdoor.py +66 -0
  196. ergodic-0.1.0/src/ergodic/inference/estimators/_its.py +195 -0
  197. ergodic-0.1.0/src/ergodic/inference/estimators/_iv.py +188 -0
  198. ergodic-0.1.0/src/ergodic/inference/estimators/_mediation.py +599 -0
  199. ergodic-0.1.0/src/ergodic/inference/estimators/_meta.py +126 -0
  200. ergodic-0.1.0/src/ergodic/inference/estimators/_null.py +60 -0
  201. ergodic-0.1.0/src/ergodic/inference/estimators/_plugin.py +423 -0
  202. ergodic-0.1.0/src/ergodic/inference/estimators/_regression.py +169 -0
  203. ergodic-0.1.0/src/ergodic/inference/estimators/_synthetic_control.py +361 -0
  204. ergodic-0.1.0/src/ergodic/inference/estimators/_weighting.py +130 -0
  205. ergodic-0.1.0/src/ergodic/inference/explain/__init__.py +34 -0
  206. ergodic-0.1.0/src/ergodic/inference/explain/_blp.py +82 -0
  207. ergodic-0.1.0/src/ergodic/inference/explain/_calibration.py +103 -0
  208. ergodic-0.1.0/src/ergodic/inference/explain/_clan.py +100 -0
  209. ergodic-0.1.0/src/ergodic/inference/explain/_gates.py +103 -0
  210. ergodic-0.1.0/src/ergodic/inference/explain/_linalg.py +53 -0
  211. ergodic-0.1.0/src/ergodic/inference/explain/_policy.py +459 -0
  212. ergodic-0.1.0/src/ergodic/inference/explain/_rate.py +118 -0
  213. ergodic-0.1.0/src/ergodic/inference/explain/_record.py +110 -0
  214. ergodic-0.1.0/src/ergodic/inference/learners/__init__.py +17 -0
  215. ergodic-0.1.0/src/ergodic/inference/learners/_linear.py +152 -0
  216. ergodic-0.1.0/src/ergodic/inference/learners/_neighbors.py +107 -0
  217. ergodic-0.1.0/src/ergodic/inference/learners/_pymc.py +149 -0
  218. ergodic-0.1.0/src/ergodic/knowledge.py +444 -0
  219. ergodic-0.1.0/src/ergodic/process/__init__.py +106 -0
  220. ergodic-0.1.0/src/ergodic/process/_case_table.py +495 -0
  221. ergodic-0.1.0/src/ergodic/process/_cases.py +108 -0
  222. ergodic-0.1.0/src/ergodic/process/_conformance.py +461 -0
  223. ergodic-0.1.0/src/ergodic/process/_decisions.py +527 -0
  224. ergodic-0.1.0/src/ergodic/process/_filters.py +254 -0
  225. ergodic-0.1.0/src/ergodic/process/_map.py +212 -0
  226. ergodic-0.1.0/src/ergodic/process/_mine.py +489 -0
  227. ergodic-0.1.0/src/ergodic/process/_net.py +261 -0
  228. ergodic-0.1.0/src/ergodic/process/_panel.py +190 -0
  229. ergodic-0.1.0/src/ergodic/process/_performance.py +169 -0
  230. ergodic-0.1.0/src/ergodic/process/_soundness.py +103 -0
  231. ergodic-0.1.0/src/ergodic/process/_tree.py +146 -0
  232. ergodic-0.1.0/src/ergodic/process/_variants.py +64 -0
  233. ergodic-0.1.0/src/ergodic/py.typed +0 -0
  234. ergodic-0.1.0/src/ergodic/scm/__init__.py +51 -0
  235. ergodic-0.1.0/src/ergodic/scm/_bounds.py +225 -0
  236. ergodic-0.1.0/src/ergodic/scm/_compose.py +739 -0
  237. ergodic-0.1.0/src/ergodic/scm/_district.py +111 -0
  238. ergodic-0.1.0/src/ergodic/scm/_mechanism.py +762 -0
  239. ergodic-0.1.0/src/ergodic/scm/_noise.py +99 -0
  240. ergodic-0.1.0/src/ergodic/scm/_result.py +128 -0
  241. ergodic-0.1.0/src/ergodic/scm/_scm.py +1038 -0
  242. ergodic-0.1.0/tests/conftest.py +15 -0
  243. ergodic-0.1.0/tests/test_bayes.py +326 -0
  244. ergodic-0.1.0/tests/test_case_table.py +415 -0
  245. ergodic-0.1.0/tests/test_cate.py +463 -0
  246. ergodic-0.1.0/tests/test_cate_forest.py +332 -0
  247. ergodic-0.1.0/tests/test_cate_phase2_stress.py +619 -0
  248. ergodic-0.1.0/tests/test_cate_policy.py +243 -0
  249. ergodic-0.1.0/tests/test_cate_select.py +173 -0
  250. ergodic-0.1.0/tests/test_cate_stress.py +643 -0
  251. ergodic-0.1.0/tests/test_conformance.py +294 -0
  252. ergodic-0.1.0/tests/test_data.py +580 -0
  253. ergodic-0.1.0/tests/test_datasets.py +185 -0
  254. ergodic-0.1.0/tests/test_decisions.py +398 -0
  255. ergodic-0.1.0/tests/test_did.py +303 -0
  256. ergodic-0.1.0/tests/test_discovery.py +282 -0
  257. ergodic-0.1.0/tests/test_discovery_bootstrap.py +357 -0
  258. ergodic-0.1.0/tests/test_discovery_boss.py +376 -0
  259. ergodic-0.1.0/tests/test_discovery_ci.py +385 -0
  260. ergodic-0.1.0/tests/test_discovery_fci.py +336 -0
  261. ergodic-0.1.0/tests/test_discovery_ges.py +262 -0
  262. ergodic-0.1.0/tests/test_discovery_glasso.py +274 -0
  263. ergodic-0.1.0/tests/test_discovery_grasp.py +207 -0
  264. ergodic-0.1.0/tests/test_discovery_hc.py +226 -0
  265. ergodic-0.1.0/tests/test_discovery_lingam.py +246 -0
  266. ergodic-0.1.0/tests/test_discovery_mmpc.py +181 -0
  267. ergodic-0.1.0/tests/test_discovery_pc.py +613 -0
  268. ergodic-0.1.0/tests/test_discovery_pcmci.py +408 -0
  269. ergodic-0.1.0/tests/test_discovery_rfci.py +244 -0
  270. ergodic-0.1.0/tests/test_discovery_score.py +467 -0
  271. ergodic-0.1.0/tests/test_estimators_e2.py +208 -0
  272. ergodic-0.1.0/tests/test_forecast_backtest.py +412 -0
  273. ergodic-0.1.0/tests/test_forecast_backtest_mcmc.py +275 -0
  274. ergodic-0.1.0/tests/test_forecast_core.py +366 -0
  275. ergodic-0.1.0/tests/test_forecast_explain.py +697 -0
  276. ergodic-0.1.0/tests/test_forecast_facade.py +381 -0
  277. ergodic-0.1.0/tests/test_forecast_mechanisms.py +534 -0
  278. ergodic-0.1.0/tests/test_forecast_metrics.py +253 -0
  279. ergodic-0.1.0/tests/test_forecast_reconcile.py +1097 -0
  280. ergodic-0.1.0/tests/test_forecast_staged.py +836 -0
  281. ergodic-0.1.0/tests/test_forecast_structure.py +471 -0
  282. ergodic-0.1.0/tests/test_forecast_zoo.py +349 -0
  283. ergodic-0.1.0/tests/test_granger.py +72 -0
  284. ergodic-0.1.0/tests/test_graph.py +342 -0
  285. ergodic-0.1.0/tests/test_graph_primitives.py +266 -0
  286. ergodic-0.1.0/tests/test_id_algorithm.py +548 -0
  287. ergodic-0.1.0/tests/test_id_integration.py +454 -0
  288. ergodic-0.1.0/tests/test_identification.py +239 -0
  289. ergodic-0.1.0/tests/test_identification_classes.py +284 -0
  290. ergodic-0.1.0/tests/test_inductive_miner.py +224 -0
  291. ergodic-0.1.0/tests/test_inference.py +483 -0
  292. ergodic-0.1.0/tests/test_its.py +145 -0
  293. ergodic-0.1.0/tests/test_knowledge.py +323 -0
  294. ergodic-0.1.0/tests/test_kpi_panel.py +276 -0
  295. ergodic-0.1.0/tests/test_mediation.py +456 -0
  296. ergodic-0.1.0/tests/test_pag.py +664 -0
  297. ergodic-0.1.0/tests/test_plugin.py +479 -0
  298. ergodic-0.1.0/tests/test_process_filters.py +249 -0
  299. ergodic-0.1.0/tests/test_process_map.py +224 -0
  300. ergodic-0.1.0/tests/test_refute.py +94 -0
  301. ergodic-0.1.0/tests/test_review_fixes.py +64 -0
  302. ergodic-0.1.0/tests/test_scm.py +937 -0
  303. ergodic-0.1.0/tests/test_scm_pymc.py +1184 -0
  304. ergodic-0.1.0/tests/test_sklearn_interop.py +92 -0
  305. ergodic-0.1.0/tests/test_smoke.py +103 -0
  306. ergodic-0.1.0/tests/test_synthetic_control.py +216 -0
  307. ergodic-0.1.0/tests/test_temporal_wiring.py +93 -0
  308. ergodic-0.1.0/tests/test_workflow_net.py +342 -0
@@ -0,0 +1,50 @@
1
+ plans/
2
+ .devcontainer/
3
+
4
+ # ---- Python ----
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ dist/
14
+ wheels/
15
+ *.egg-info/
16
+ .eggs/
17
+
18
+ # Virtual environments
19
+ .venv/
20
+ venv/
21
+ env/
22
+
23
+ # Test / type / lint caches
24
+ .pytest_cache/
25
+ .mypy_cache/
26
+ .dmypy.json
27
+ .ruff_cache/
28
+ .cache/
29
+ htmlcov/
30
+ .coverage
31
+ .coverage.*
32
+ coverage.xml
33
+
34
+ # Docs build
35
+ site/
36
+
37
+ # Jupyter
38
+ .ipynb_checkpoints/
39
+
40
+ # Environments / secrets
41
+ .env
42
+
43
+ # OS / editor
44
+ .DS_Store
45
+ *.swp
46
+ .idea/
47
+ .vscode/
48
+
49
+ # Claude Code local config
50
+ .claude/
@@ -0,0 +1,394 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - Initial project scaffolding: the `ergodic` package with `discovery`, `inference`,
12
+ and `process` subpackages (placeholders).
13
+ - Tooling: uv project layout, ruff (lint + format), mypy (strict), pytest.
14
+ - MkDocs Material documentation skeleton, pre-commit hooks, and a Makefile.
15
+ - Causal graph objects (`ergodic.graph`): a mixed-graph core with typed endpoint
16
+ marks covering DAG, ADMG, MAG, CPDAG, and PAG; immutable value semantics; d- and
17
+ m-separation; the do-operator and subgraphs; networkx, adjacency, and dict interop.
18
+ - Domain knowledge (`ergodic.knowledge`): hard prior constraints (required and
19
+ forbidden edges, tiers, roots, sinks, confounded pairs) with composition, conflict
20
+ detection, and projections (`permits_edge`, `is_consistent`, `apply_to`, `edge_mask`).
21
+ - Identification (`ergodic.identification`): `identify_effect` plus the complete
22
+ adjustment criterion, the front-door criterion, and conditional instruments, turning a
23
+ graph and a treatment/outcome into an estimand.
24
+ - Typed data objects (`ergodic.data`): a `Dataset` base with a column `Schema`
25
+ (continuous, categorical, ordinal, datetime, excluded) across five shapes,
26
+ `tabular`, `timeseries`, `panel`, `hts` (with a `Hierarchy` aggregation tree),
27
+ and `event_log`. Immutable, with type inference and overrides, codes and a float
28
+ matrix, missing-value handling, and `aligns_with(graph)`. A dataframe builds it
29
+ and stays at the boundary; pandas native, polars and arrow through `to_pandas`.
30
+ - Effect estimation (`ergodic.inference`): `estimate_effect` identifies an effect
31
+ and estimates it over `TabularData`, returning a distribution-shaped
32
+ `EffectEstimate` (a point with an analytic or bootstrap standard error, or
33
+ draws). A `CausalEstimator` is the strategy and a `Learner` is the model in each
34
+ nuisance slot; the learner is structurally a sklearn estimator. Adjustment:
35
+ `OutcomeRegression` (S- and T-learner), `IPW`, `Stratification`, `AIPW`, and the
36
+ cross-fit `DML`, `DRLearner`, `RLearner`, and `XLearner`; front-door:
37
+ `TwoStageRegression`; instruments: `TwoStageLeastSquares`, `Wald`, and `PLIV`.
38
+ Heterogeneous effects (CATE) through `cate`, binary or continuous treatment for
39
+ `DML` and `PLIV`, and native `OLS`, `LogisticRegression`, and `KNN` learners.
40
+ - Bayesian learners (`ergodic.inference`): a `BayesianLearner` carries a posterior
41
+ over its mean function (detected by `predict_draws`), and a `PyMCLearner` /
42
+ `PyMCClassifier` wraps a user-written PyMC model (pymc optional, `ergodic[bayes]`).
43
+ `OutcomeRegression` (the Bayesian g-formula) and `AIPW` (doubly robust, with a
44
+ Dirichlet Bayesian bootstrap) return a posterior `EffectEstimate` through the same
45
+ accessors, and the T-learner reports per-unit CATE credible intervals via
46
+ `cate_draws` and `cate_interval`.
47
+ - Explicit treatment coding: `estimate_effect(..., treated=...)` picks which level
48
+ counts as treated, and an implicit non-numeric coding now warns with the chosen
49
+ levels (a string treatment used to be coded by alphabetical order, silently).
50
+ - The `"null"` identification strategy and `NullEffect` estimator: a graph with no
51
+ directed path from treatment to outcome identifies trivially as a zero effect.
52
+ - `as_kind` accepts the kind name as a string (`g.as_kind("admg")`).
53
+ - Stratified cross-fitting folds for binary treatments, bootstrap shortfall
54
+ diagnostics, and a clear error when a treatment arm is empty.
55
+ - Causal discovery (`ergodic.discovery`), the first build: a conditional
56
+ independence test is a class constructed with data (the stateful oracle:
57
+ encoding once, sufficient statistics, cached alpha-independent queries),
58
+ with `Linear` (partial correlation), `Discrete` (G squared, natively
59
+ categorical), `Mixed` (degenerate Gaussian for mixed columns), and a
60
+ `DSeparationOracle` ground truth; a decomposable `Score` answers
61
+ `local(node, parents)` with `BIC` (Gaussian); PC-stable orients through
62
+ v-structures and the Meek rules and returns a CPDAG with separating sets;
63
+ `AStar` is exact search over the order lattice; `discover(data, method=...)`
64
+ is the facade and `discover` re-exports at the top level. Background
65
+ knowledge enters in four modes (prune, protect, orient, validate), and
66
+ `DomainKnowledge.restrict_to(skeleton)` confines any search to an adjacency
67
+ support. The Meek rules, `cpdag_of_dag`, and the Dor-Tarsi extension check
68
+ live in `ergodic.graph`, which makes `is_cpdag` a real validator.
69
+ - Causal discovery, the score stack: `GES` (Chickering's greedy equivalence
70
+ search, held to the A* optimum in the tests) and `HillClimb` (steepest
71
+ ascent with a tabu list) consume the same `Score` contract as `AStar`;
72
+ `DiscreteBIC` (multinomial), `BDeu`, and `MixedBIC` (degenerate Gaussian,
73
+ no edge restriction) join the Gaussian `BIC`, with executable
74
+ score-equivalence tests; the `Score` base gains a native-categorical codes
75
+ form (`local_from_codes`); `score="auto"` picks the score from the column
76
+ schema the way `test="auto"` picks the test; and `GCM` (Shah and Peters)
77
+ turns any regression `Learner`, native or sklearn, into a conditional
78
+ independence test that catches nonlinear dependence the partial correlation
79
+ misses. `DSeparationOracle` now answers about the observed margin (latent
80
+ nodes are not query-able variables).
81
+ - Causal discovery with latent confounders: `FCI` returns a PAG (stable
82
+ skeleton, the possible-d-sep phase, Zhang's complete rule closure, no
83
+ selection bias), held in the tests to the canonical PAG of the truth under
84
+ a perfect oracle. The graph layer grew the toolkit underneath:
85
+ m-separation now reads circle marks through definite-status paths (the
86
+ class-invariant answer on a complete PAG), `mag_of_dag` is the latent
87
+ projection, `pag_of_mag`/`mag_of_pag` convert between a MAG and its
88
+ equivalence class, `fci_closure` joins `meek_closure`, `set_mark` edits one
89
+ endpoint, and `is_pag` became a real complete-PAG validator by round trip,
90
+ exactly as `is_cpdag` did. Domain knowledge translates to PAGs through
91
+ ancestry: tiers pin an arrowhead at the later end, `confound(a, b)` orients
92
+ a bidirected edge, directional forbids and roots/sinks validate only.
93
+ - Skeleton screening and stability: `MMPC` (max-min parents-children, AND
94
+ rule) returns an undirected support for `restrict_to`, recovering the MMHC
95
+ hybrid as a composition; `bootstrap` reruns any method on row resamples and
96
+ returns adjacency and orientation frequencies with a thresholded
97
+ `summary()` graph, the first soft output. `Dataset.take(rows)` resamples
98
+ rows, and `discover` dispatches on each algorithm's declared ingredient.
99
+ - Identification on equivalence classes: `identify_effect`, `adjustment_set`,
100
+ `minimal_adjustment_set`, and `is_valid_adjustment_set` accept a CPDAG,
101
+ MAG, or PAG and answer through the generalized adjustment criterion
102
+ (Perkovic et al., 2018), so a success holds in every graph the class
103
+ represents and `estimate_effect` runs straight off a discovery result.
104
+ Held executably to the all-members definition: exhaustive over candidate
105
+ sets against the enumerated members of small random classes. The graph
106
+ layer grew the toolkit underneath: `possible_descendants` and
107
+ `possible_ancestors` (possibly directed reachability), `visible_edges`
108
+ (Zhang's no-hidden-confounding certificate), and the member enumerators
109
+ `dags_of_cpdag` and `mags_of_pag` (deterministic, capped without silent
110
+ truncation). The front-door and instruments raise on a class and point at
111
+ the enumerators (the per-member IDA pattern); mixed graphs raise.
112
+ - Quasi-experimental designs (`ergodic.inference`): estimators that read the
113
+ panel structure instead of a graph, each stating its identification in a
114
+ `Design` record and returning the same `EffectEstimate` surface. `did` over
115
+ a `PanelData` runs the canonical two-period collapse for a single adoption
116
+ date and Callaway-Sant'Anna group-time ATTs for staggered adoption (with
117
+ `comparison="never_treated"` or `"not_yet_treated"` and unit-bootstrap
118
+ inference); static two-way fixed effects ships as an explicit strategy with
119
+ its staggered-adoption caveat and is never auto-picked. `event_study`
120
+ reports ATT by event time with leads as the visible pre-trend check.
121
+ `synthetic_control` fits simplex weights on the pre-period by projected
122
+ gradient and reports per-period gaps with Abadie's in-space placebo
123
+ inference (the RMSPE-ratio rank p-value; intervals come from the placebo
124
+ band on purpose). `interrupted_time_series` over a `TimeSeriesData` is
125
+ segmented regression with level and slope changes, optional seasonal terms,
126
+ and Newey-West standard errors. All four re-export at the top level.
127
+ - Temporal causal discovery (`ergodic.discovery`): `discover(data,
128
+ method="pcmci", max_lag=...)` accepts a `TimeSeriesData` or `PanelData`
129
+ (panels pool windows within entities) and returns a
130
+ `TimeSeriesDiscoveryResult` holding a window graph over lagged nodes like
131
+ `X[t-1]`, per-link p-values, and a variable-level `summary_edges()`. The
132
+ algorithm is PCMCI-shaped: a PC1 lagged-parent screen, momentary
133
+ conditional independence tests for lagged links (conditioning sets drawn
134
+ from a deeper lagged frame), a PC-stable contemporaneous phase conditioned
135
+ on lagged parents, and collider plus within-slice Meek orientation. It
136
+ reuses the existing `CITest` stack through `lagged_tabular`, so
137
+ `test="auto"` and the cache come free. `granger` is the predictive
138
+ baseline and returns a pair-level table rather than a graph (feedback
139
+ across lags is normal; the docstring says what Granger does not claim).
140
+ Shape guards keep the methods honest: iid methods reject temporal data
141
+ (rows are not exchangeable), `pcmci` rejects tabular data, and `bootstrap`
142
+ refuses temporal methods until a moving-block variant exists.
143
+ - The learn series: six beginner notebook guides (`docs/learn/`) that teach
144
+ causal inference from zero on commercial examples, confounding and Simpson's
145
+ paradox, good and bad controls, estimation with doubly robust and cross-fit
146
+ estimators, heterogeneous effects and uplift targeting, instruments and the
147
+ front door, and structure discovery ending at the discover-then-estimate
148
+ bridge. Each guide is a py:percent script that mkdocs-jupyter executes at
149
+ build time with fixed seeds, so the outputs and figures on the site are real
150
+ and a broken example fails `make docs`. Docs review alongside: the API
151
+ reference gains the discovery section (guide pages no longer inline `:::`
152
+ blocks), stale forward references brought current, and the index routes
153
+ beginners to the series.
154
+ - Learn guide 7, quasi-experiments: a staggered promo rollout recovered with
155
+ difference-in-differences and an event study, and a flagship market's price
156
+ change measured against its synthetic control, with the naive comparisons
157
+ failing first. A new "Panel and time-series designs" guide page documents
158
+ the design family, and the discovery guide gains its time-series section.
159
+ - Process intelligence (`ergodic.process`), the first build. The descriptive
160
+ layer recovers the daily workflow over an `EventLog`: `process_map` (the
161
+ directly-follows map as a value object with frequency and gap statistics
162
+ and a `simplify` that records exactly what it dropped), `performance`
163
+ (cycle times, and a waiting/service split when a lifecycle column carries
164
+ start/complete pairs; with one timestamp per activity the split is honestly
165
+ impossible and the result says so), `variant_table`, and six composable
166
+ filters (time, variants, endpoints, activities, follows, performance), each
167
+ declaring whether it drops events or whole cases and refusing to return an
168
+ empty log. The causal layer is the point: `case_table` encodes a log into
169
+ one row per case with timestamp-gated features (case-start features in
170
+ `when="anytime"`; landmark modes `("after_events", k)` and
171
+ `("after_elapsed", x)` measure prefix features at the landmark, drop
172
+ already-treated cases with counts, and state their population), the named
173
+ process confounders (open-case workload, calendar features), and an
174
+ auto-built `DomainKnowledge` whose tiers forbid post-treatment adjustment
175
+ mechanically, feeding `discover`, `identify_effect`, and `estimate_effect`
176
+ unchanged; `kpi_panel` aggregates a log into a `PanelData` (or a single
177
+ `TimeSeriesData`) so a process change becomes a natural experiment for the
178
+ quasi-experimental designs. Learn guide 8 walks the whole arc: the process
179
+ map convicts a review step of double its true cost, the case table acquits
180
+ it, and a routing rollout is measured with `kpi_panel` into `did`.
181
+ - The process model layer (`ergodic.process`, second build). `discover_model`
182
+ is the Inductive Miner implemented from the papers (pm4py is AGPL and is not
183
+ used): recursive cuts of the directly-follows graph (exclusive choice,
184
+ sequence, parallel, loop) into a block-structured `ProcessTree` (build one
185
+ by hand with `seq`, `xor`, `par`, `loop`, and `TAU`; structural equality,
186
+ canonical child order), with the infrequent variant behind `noise=` and
187
+ provenance on the returned `ProcessModel` (flower fall-through paths,
188
+ filtered arcs, dropped events). `ProcessTree.to_net()` converts to a
189
+ `WorkflowNet` that is sound by construction, and `is_sound` verifies any
190
+ net for real by reachability (option to complete, proper completion, no
191
+ dead transitions; capped, raising past the cap rather than guessing).
192
+ `conformance` checks a log against a model by token replay (the standard
193
+ missing/remaining/consumed/produced fitness) and by optimal alignments
194
+ (Dijkstra over the synchronous product with deterministic tie-breaking;
195
+ per-variant costs and sync/log/model moves). `decision_points` and
196
+ `decision_table` read an exclusive split causally: the branch taken is the
197
+ treatment, the landmark is the moment the case reveals its branch, every
198
+ covariate is measured strictly before it (workload, calendar, prefix
199
+ features, case attributes), the auto-built `DomainKnowledge` tiers order
200
+ covariates before branch before outcome, the default outcome is the
201
+ remaining time from the decision, and a first-class `PositivityReport`
202
+ (per-branch counts and shares, propensity overlap for two-way splits)
203
+ warns when the branches barely overlap. Splits with a silent branch are
204
+ refused with the reason. Learn guide 9 walks the arc: the miner finds the
205
+ routing split, conformance separates deviation from structure, the naive
206
+ lane comparison overstates the cost of review by a factor of three, and
207
+ the decision table recovers the planted effect.
208
+ - Learn guide 8, process mining: the descriptive workflow on its own, in
209
+ three steps on a simulated fulfillment flow, with the process maps drawn
210
+ as real graphs (traffic-scaled arrows, counts and median gaps on every
211
+ arc): see the process, measure it (the waiting-versus-service split finds
212
+ the planted pick queue, eight hours of waiting in front of 26 minutes of
213
+ work), and drill into the slice that hurts, with `simplify` declaring what
214
+ it dropped. The process guides renumber to make room: process intelligence
215
+ is guide 9 and decision points is guide 10.
216
+ - Selection bias end to end in the graph layer: `is_mag` reads the full
217
+ Richardson-Spirtes ancestral conditions (undirected endpoints carry no
218
+ arrowheads), `mag_of_dag` projects latent and selection nodes together
219
+ through the one-witness-set theorem, `fci_closure` runs Zhang's complete
220
+ ten rules (R5 to R7 are the selection rules), `mag_of_pag` resolves
221
+ circle-tail edges by arrowhead augmentation, and `mags_of_pag` enumerates
222
+ the selection members behind an explicit `selection=` switch. The
223
+ d-separation oracle conditions every query on `graph.selection`, so FCI
224
+ needs no algorithmic change and lands exactly on the canonical PAG of
225
+ random latent-plus-selection worlds, held executably (Richardson-Spirtes
226
+ Theorem 4.18, round trips, and the chordless-cycle world only R5 can
227
+ orient). Identification raises on selection marks instead of silently
228
+ misreading them.
229
+ - Discovery D4, the iid completion. PC gains `colliders="conservative"`
230
+ and `"majority"` (Ramsey 2006; Colombo-Maathuis 2014): unshielded triples
231
+ are voted across both neighborhoods' subsets, ambiguous triples are never
232
+ oriented and gate Meek R1/R3 through `meek_closure`'s new `ambiguous=`
233
+ parameter, and the face stays honest. RFCI (`method="rfci"`) skips FCI's
234
+ possible-d-sep phase, re-tests triples and discriminating paths instead,
235
+ and returns an RFCI-PAG whose marks stay sound (held executably against
236
+ random latent, confounded, and selection worlds, with exact FCI agreement
237
+ wherever possible-d-sep has nothing to do). BOSS and GRaSP (`method=
238
+ "boss"`, `"grasp"`) search permutations through a grow-shrink projection,
239
+ BOSS with a BES pass reusing GES's backward phase, both held to the A*
240
+ optimum. DirectLiNGAM (`method="lingam"`) fully orients a DAG from
241
+ non-Gaussian data with the Hyvarinen-Smith pairwise likelihood ratio and
242
+ native backward-BIC pruning. A graphical-lasso screen (`method="glasso"`)
243
+ estimates a sparse precision by block coordinate descent (matching
244
+ sklearn entry for entry), picks the penalty by EBIC at the
245
+ support-restricted refit MLE, and feeds `restrict_to` like MMPC. The
246
+ facade learns methods that consume neither a test nor a score.
247
+ - Temporal unblockers: `bootstrap` accepts `method="pcmci"` through a
248
+ moving-block design (a series resamples blocks that become entities of a
249
+ panel, so no lag window ever crosses a block seam; a panel resamples
250
+ whole entities; `block_length` overrides the default), and PCMCI reads
251
+ `DomainKnowledge` with variables as processes: forbids, tiers, roots, and
252
+ sinks prune at every lag, tiers orient contemporaneous edges, self-memory
253
+ survives, `require` validates after the run, and `confound` raises.
254
+ - Real-world datasets (`ergodic.datasets`), the first set: sklearn-style loaders
255
+ that return a `Bunch` carrying the typed dataset under `data` plus its causal
256
+ question (treatment, outcome, confounders or instrument, and the `target` the
257
+ literature already measured). `load_birthweight` (maternal smoking, backdoor
258
+ adjustment), `load_berkeley` (the 1973 admissions table, Simpson's paradox),
259
+ `load_pension401k` (401(k) eligibility, double machine learning), `load_smoking`
260
+ (Proposition 99, synthetic control) and `load_germany` (reunification), and
261
+ `load_asia` / `load_earthquake` (small networks written from their papers and
262
+ sampled, for discovery). The bundled data ships in the wheel; provenance and
263
+ citations are in the NOTICE. Example scripts in `examples/` run each analysis
264
+ next to its known answer, and the tests check the recovery (smoking lands within
265
+ the placebo band at -21 packs, double ML moves the pension gap from $19.5k to
266
+ about $9k, the Berkeley gap reverses under department).
267
+ - Refutation (`ergodic.inference`): `refute_effect(graph, data, treatment, outcome,
268
+ method=...)` stress-tests an estimate by re-running it under a perturbation and
269
+ returns a `Refutation`. The model-agnostic checks: `"placebo_treatment"` scrambles
270
+ the treatment and the effect should vanish, `"random_common_cause"` adds an
271
+ irrelevant covariate and the effect should hold, `"data_subset"` re-estimates on a
272
+ random slice. None proves an estimate right; a failure is a real warning. The
273
+ sharper confounder-specific sensitivity (partial R-squared, the E-value) comes
274
+ later. `refute_effect` and `Refutation` re-export at the top level.
275
+ - Case studies (`docs/case-studies/`): five narrative pages that apply the tools to
276
+ a famous dataset and recover the number the original study published. Smoking and
277
+ birthweight (backdoor adjustment), the 1973 Berkeley admissions paradox (Simpson's
278
+ paradox), 401(k) eligibility (double machine learning), Proposition 99 (synthetic
279
+ control), and structure discovery on a known network. Each opens with a causal
280
+ balance table, shows the naive answer failing, recovers the published figure, and
281
+ ends with a refutation check. They execute on every docs build, so the prose and
282
+ the code stay in step.
283
+ - The general do-calculus ID algorithm (`ergodic.identification`):
284
+ `identify_distribution(graph, outcome, do, given=...)` runs Shpitser-Pearl ID
285
+ (and IDC when `given` is set) on a single DAG or ADMG, deciding whether
286
+ `P(outcome | do(do), given)` is identifiable and returning the estimand as a
287
+ typed `Functional` (the `Prob` / `Product` / `Marginal` / `Ratio` algebra, with
288
+ `render`, `free_vars`, and `simplify`; public but marked provisional) or the
289
+ `Hedge` witness when it is not. An empty `do` is the observational query.
290
+ `identify_effect` now falls through to general ID after its named ladder misses
291
+ on a single graph, so a graph that is do-calculus identifiable but outside
292
+ adjustment, the front-door, and instruments (the Verma graph and its kin)
293
+ returns `strategy="id"` with the functional set; a non-identifiable graph
294
+ returns `strategy="none"` with the hedge. The `Identification` record gained
295
+ `functional` and `hedge` fields. `ida(graph, treatment, outcome)` identifies
296
+ across an equivalence class: it enumerates the members of a CPDAG and runs
297
+ `identify_effect` per member, returning an `IDA` record (`members`,
298
+ `n_identifiable`, `all_identifiable`, `agree`, `strategies`, `estimands`); PAG
299
+ and MAG members need a DAG realization and raise a clear "not built" for the
300
+ general case. Held executably to the soundness-against-truth anchor (the
301
+ functional evaluated on random discrete worlds matches the truth) and the
302
+ identifiability catalog.
303
+ - The discrete plug-in estimator (`ergodic.inference`): `estimate_effect` on a
304
+ discrete `strategy="id"` graph estimates the general functional directly,
305
+ evaluating the empirical sum-product with a bootstrap standard error. Discrete-
306
+ only and binary-treatment guarded, with a positivity check that raises on an
307
+ empty stratum the formula divides by (the discrete analog of the overlap check);
308
+ a continuous variable raises and points at the SCM path (not built).
309
+ - Mediation (`ergodic.identification`, `ergodic.inference`):
310
+ `identify_mediation(graph, treatment, outcome, mediators)` decides the natural
311
+ direct and indirect effects (NDE, NIE) via a covariate set that blocks the X-Y,
312
+ X-M, and M-Y back-doors (the three adjustment conditions), stating the cross-
313
+ world assumption (no mediator-outcome confounder affected by treatment) in words
314
+ since no data can test it, and returns a `MediationID`. The controlled direct
315
+ effect (CDE) routes through the general ID engine as a plain interventional
316
+ contrast and can identify even when NDE and NIE do not.
317
+ `estimate_mediation(graph, data, treatment, outcome, mediators, ...)` is the
318
+ g-computation estimator: NDE, NIE, and the total effect (each a distribution-
319
+ shaped `EffectEstimate`), the per-level CDE, a `Design` record naming the
320
+ assumptions, and `proportion_mediated` (flagged when the total effect is near
321
+ zero). Binary treatment and a discrete mediator; held to a planted linear world
322
+ where NDE and NIE recover the structural coefficients.
323
+ - Forecasting (`ergodic.forecast`), v1: `forecast(data, horizon)` fits a Bayesian
324
+ `TemporalMechanism` per series and returns a distribution-shaped `ForecastResult`
325
+ (per-node draws, `median`, `interval`, `to_frame`, provenance, and per-node
326
+ sampler diagnostics). The mechanism zoo is `StructuralMechanism` (a damped local
327
+ linear trend times a season plus a regression on graph parents, composed from
328
+ those pieces rather than fused), `CausalOU` (drivers move the long-run attractor,
329
+ so the transient answers "when" and not just "how much"), `Bass` (the diffusion
330
+ S-curve), and `ScenarioMechanism`; mechanisms are auto-assigned from the schema
331
+ with the seasonal period read off the time index, and count or categorical
332
+ columns raise at a named seam. Hierarchies reconcile without refitting through
333
+ `reconcile(result, hierarchy, method=...)`: `bottom_up`, `top_down` (historical or
334
+ per-draw forecast proportions), `middle_out`, and the `mint` projection (diagonal
335
+ posterior weights or `mint_shrink`, with `trust={node: w}`), each coherent to
336
+ machine epsilon read relative to the size of each parent. Drivers ride a window
337
+ graph over lagged names, exactly what `discover(..., method="pcmci")` returns:
338
+ staged fitting walks the `TrainingSchedule` and hands each node's whole forecast
339
+ posterior down to its children by injecting it into the child's trace, joint
340
+ fitting puts every node in one model and keeps the cross-node correlation, and
341
+ `fit="auto"` picks between them. `scenario=` pins a known future (an array, a
342
+ scalar, or `"hold"`) and states that it makes no causal claim; pinning an
343
+ endogenous node is refused by name and points at the `do=` that waits for
344
+ identification on window graphs. `backtest(model, data, ...)` scores anything that
345
+ fits a series and forecasts a horizon at rolling origins, with `Naive`,
346
+ `SeasonalNaive`, and `Drift` baselines, fifteen metrics including MASE and CRPS,
347
+ per-fold seeds, and the sampler's own report aggregated per fold;
348
+ `forecast_model(**options)` wraps the facade as a backtestable model.
349
+ `ForecastResult.explain()` returns the attribution waterfall: a baseline bar, a
350
+ seasonality bar, and one bar per driver over any `Window` of the horizon, each
351
+ carrying posterior draws and a credible interval, summing to the total with a
352
+ reported `closure_error`. The structural engine recomposes the mechanism's own
353
+ recorded pieces and costs no sampling; the replay engine (for a mechanism that
354
+ does not expose them) zeroes the factor's sites in a copy of the trace and
355
+ forecasts again on the stored seed. pymc is optional, `pip install
356
+ ergodic[forecast]`, and the bare install never imports it.
357
+
358
+ ### Fixed
359
+ - Instruments: descendants of the treatment are no longer certified as instruments
360
+ (the exclusion test was unsound); latent nodes are excluded from adjustment sets,
361
+ front-door sets, instruments, and conditioning sets; identification validates its
362
+ inputs (unknown names and overlapping treatment/outcome raise); multi-treatment
363
+ adjustment validity follows proper causal paths exactly.
364
+ - Graph construction: conflicting duplicate edges on one node pair raise instead of
365
+ silently keeping the last one (also through `add_edge`, `from_networkx`, and
366
+ `from_dict`); write a bow as an explicit latent confounder with `latent=`.
367
+ Separation queries reject unknown node names instead of returning a wrong answer.
368
+ - Domain knowledge: `forbid_adjacency` covers bidirected and undirected edges;
369
+ `conflicts` catches cycles spanning required edges and tier order together;
370
+ precedence is transitively closed; `confound(a, a)` raises; the cycle checks no
371
+ longer recurse (deep graphs build fine).
372
+ - Data: datasets copy their columns at the boundary, so mutating the source
373
+ dataframe or array no longer mutates the dataset; tz-aware datetimes infer as
374
+ datetime; missing categoricals and NaT reach `matrix()` as NaN instead of
375
+ sentinel numbers; values outside declared categories raise; HTS mean coherence
376
+ and `aggregate` share one definition.
377
+ - Inference: learner cloning works for sklearn pipelines and meta-estimators; the
378
+ KNN zero-feature fallback averages the whole training set; the AIPW ATT posterior
379
+ reweights the treated share consistently with the Bayesian bootstrap; the
380
+ R-learner names its final slot when a learner lacks `sample_weight`.
381
+ - Docs: repaired the broken examples (`as_kind("admg")`, a single learner alias on
382
+ AIPW, the phantom `"gbm"` alias) and refreshed stale passages.
383
+
384
+ ### Changed
385
+ - `effect()` lost its unused `level` parameter; pick the interval level on the
386
+ result (`estimate.interval(0.99)`).
387
+ - IV estimands now state the linear (parametric) modeling assumption.
388
+ - `m_separated` on a CPDAG-kind graph now answers for the equivalence class:
389
+ undirected edges read as circle marks and paths need definite status,
390
+ matching the PAG reading (a literal tail-tail walk could claim connection
391
+ where every member of the class is separated). Spell marks with `mixed(...)`
392
+ to walk them literally. `identify_effect` on a CPDAG or MAG used to run
393
+ silently with single-graph semantics; it now dispatches on the face, and
394
+ mixed graphs raise instead of being misread.
ergodic-0.1.0/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.