cleverly 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 (207) hide show
  1. cleverly-0.1.0/.codex/config.toml +2 -0
  2. cleverly-0.1.0/.gitattributes +15 -0
  3. cleverly-0.1.0/.github/actions/setup-env/action.yml +58 -0
  4. cleverly-0.1.0/.github/pull_request_template.md +23 -0
  5. cleverly-0.1.0/.github/workflows/ci.yml +143 -0
  6. cleverly-0.1.0/.github/workflows/pages.yml +45 -0
  7. cleverly-0.1.0/.github/workflows/publish.yml +81 -0
  8. cleverly-0.1.0/.github/workflows/validation.yml +39 -0
  9. cleverly-0.1.0/.gitignore +20 -0
  10. cleverly-0.1.0/AGENTS.md +3 -0
  11. cleverly-0.1.0/CLAUDE.md +113 -0
  12. cleverly-0.1.0/CONTRIBUTING.md +60 -0
  13. cleverly-0.1.0/LICENSE +21 -0
  14. cleverly-0.1.0/PKG-INFO +257 -0
  15. cleverly-0.1.0/README.md +195 -0
  16. cleverly-0.1.0/docs/README.md +46 -0
  17. cleverly-0.1.0/docs/_static/custom.css +382 -0
  18. cleverly-0.1.0/docs/_static/custom.js +514 -0
  19. cleverly-0.1.0/docs/_templates/autosummary/class.rst +29 -0
  20. cleverly-0.1.0/docs/api/datasets.md +31 -0
  21. cleverly-0.1.0/docs/api/estimands.md +28 -0
  22. cleverly-0.1.0/docs/api/exceptions.md +22 -0
  23. cleverly-0.1.0/docs/api/index.md +24 -0
  24. cleverly-0.1.0/docs/api/interventions.md +26 -0
  25. cleverly-0.1.0/docs/api/learners.md +22 -0
  26. cleverly-0.1.0/docs/api/methods.md +20 -0
  27. cleverly-0.1.0/docs/api/object-index.rst +160 -0
  28. cleverly-0.1.0/docs/api/results-assessment.md +34 -0
  29. cleverly-0.1.0/docs/api/sensitivity-validation.md +72 -0
  30. cleverly-0.1.0/docs/api/study-design.md +21 -0
  31. cleverly-0.1.0/docs/architecture-invariants.md +320 -0
  32. cleverly-0.1.0/docs/conf.py +117 -0
  33. cleverly-0.1.0/docs/development/contributing.md +176 -0
  34. cleverly-0.1.0/docs/development/index.md +23 -0
  35. cleverly-0.1.0/docs/development/method-benchmarking.md +250 -0
  36. cleverly-0.1.0/docs/development/pull-requests.md +123 -0
  37. cleverly-0.1.0/docs/development/releases.md +77 -0
  38. cleverly-0.1.0/docs/development/testing-strategy.md +163 -0
  39. cleverly-0.1.0/docs/examples/collaborative-tmle.md +278 -0
  40. cleverly-0.1.0/docs/examples/cross-fitting.md +346 -0
  41. cleverly-0.1.0/docs/examples/dr-tmle.md +241 -0
  42. cleverly-0.1.0/docs/examples/index.md +93 -0
  43. cleverly-0.1.0/docs/examples/interventions.md +311 -0
  44. cleverly-0.1.0/docs/examples/longitudinal-survival.md +312 -0
  45. cleverly-0.1.0/docs/examples/longitudinal-tmle.md +348 -0
  46. cleverly-0.1.0/docs/examples/msm-projections.md +276 -0
  47. cleverly-0.1.0/docs/examples/point-treatment-tmle.md +440 -0
  48. cleverly-0.1.0/docs/examples/survey-nonresponse.md +346 -0
  49. cleverly-0.1.0/docs/examples/twins-causal-inference.ipynb +1628 -0
  50. cleverly-0.1.0/docs/getting-started/index.md +16 -0
  51. cleverly-0.1.0/docs/getting-started/installation.md +68 -0
  52. cleverly-0.1.0/docs/getting-started/quickstart.md +73 -0
  53. cleverly-0.1.0/docs/index.md +142 -0
  54. cleverly-0.1.0/docs/references.md +346 -0
  55. cleverly-0.1.0/docs/roadmap.md +501 -0
  56. cleverly-0.1.0/docs/technical-reference/collaborative-tmle.md +132 -0
  57. cleverly-0.1.0/docs/technical-reference/cv-tmle.md +146 -0
  58. cleverly-0.1.0/docs/technical-reference/dr-tmle/diagnostics.md +80 -0
  59. cleverly-0.1.0/docs/technical-reference/dr-tmle/index.md +224 -0
  60. cleverly-0.1.0/docs/technical-reference/dr-tmle/nuisance-conditions.md +37 -0
  61. cleverly-0.1.0/docs/technical-reference/dr-tmle/supported-estimands.md +111 -0
  62. cleverly-0.1.0/docs/technical-reference/dr-tmle/targeting.md +280 -0
  63. cleverly-0.1.0/docs/technical-reference/dr-tmle/theorem.md +256 -0
  64. cleverly-0.1.0/docs/technical-reference/dr-tmle/validation-programme.md +45 -0
  65. cleverly-0.1.0/docs/technical-reference/evidence.md +315 -0
  66. cleverly-0.1.0/docs/technical-reference/index.md +63 -0
  67. cleverly-0.1.0/docs/technical-reference/inference.md +103 -0
  68. cleverly-0.1.0/docs/technical-reference/longitudinal-tmle.md +264 -0
  69. cleverly-0.1.0/docs/technical-reference/method-evidence/canonical-dr-tmle.md +247 -0
  70. cleverly-0.1.0/docs/technical-reference/method-evidence/canonical-point-treatment-tmle.md +234 -0
  71. cleverly-0.1.0/docs/technical-reference/method-evidence/clustered-point-treatment-cv-tmle.md +136 -0
  72. cleverly-0.1.0/docs/technical-reference/method-evidence/continuous-modified-treatment-policies.md +135 -0
  73. cleverly-0.1.0/docs/technical-reference/method-evidence/controlled-direct-effect-tmle.md +200 -0
  74. cleverly-0.1.0/docs/technical-reference/method-evidence/cross-fitted-categorical-longitudinal-tmle.md +189 -0
  75. cleverly-0.1.0/docs/technical-reference/method-evidence/cross-fitted-competing-risk-longitudinal-tmle.md +225 -0
  76. cleverly-0.1.0/docs/technical-reference/method-evidence/cross-fitted-end-of-study-longitudinal-tmle.md +213 -0
  77. cleverly-0.1.0/docs/technical-reference/method-evidence/cross-fitted-survival-curve-longitudinal-tmle.md +233 -0
  78. cleverly-0.1.0/docs/technical-reference/method-evidence/cross-fitted-weighted-end-of-study-longitudinal-tmle.md +198 -0
  79. cleverly-0.1.0/docs/technical-reference/method-evidence/deterministic-point-treatment-regimes.md +112 -0
  80. cleverly-0.1.0/docs/technical-reference/method-evidence/fold-evaluated-point-treatment-cv-tmle.md +157 -0
  81. cleverly-0.1.0/docs/technical-reference/method-evidence/fold-targeted-point-treatment-cv-tmle.md +156 -0
  82. cleverly-0.1.0/docs/technical-reference/method-evidence/how-to-read.md +97 -0
  83. cleverly-0.1.0/docs/technical-reference/method-evidence/incremental-propensity-interventions.md +132 -0
  84. cleverly-0.1.0/docs/technical-reference/method-evidence/index.md +59 -0
  85. cleverly-0.1.0/docs/technical-reference/method-evidence/learned-weighted-point-treatment-tmle.md +167 -0
  86. cleverly-0.1.0/docs/technical-reference/method-evidence/multi-arm-dr-tmle.md +150 -0
  87. cleverly-0.1.0/docs/technical-reference/method-evidence/ordinary-categorical-longitudinal-tmle.md +183 -0
  88. cleverly-0.1.0/docs/technical-reference/method-evidence/ordinary-competing-risk-longitudinal-tmle.md +215 -0
  89. cleverly-0.1.0/docs/technical-reference/method-evidence/ordinary-end-of-study-longitudinal-tmle.md +202 -0
  90. cleverly-0.1.0/docs/technical-reference/method-evidence/ordinary-longitudinal-msm-projection.md +140 -0
  91. cleverly-0.1.0/docs/technical-reference/method-evidence/ordinary-missing-outcome-tmle.md +133 -0
  92. cleverly-0.1.0/docs/technical-reference/method-evidence/ordinary-multi-arm-tmle.md +121 -0
  93. cleverly-0.1.0/docs/technical-reference/method-evidence/ordinary-survival-curve-longitudinal-tmle.md +254 -0
  94. cleverly-0.1.0/docs/technical-reference/method-evidence/ordinary-weighted-end-of-study-longitudinal-tmle.md +193 -0
  95. cleverly-0.1.0/docs/technical-reference/method-evidence/outcome-adaptive-multi-arm-c-tmle.md +124 -0
  96. cleverly-0.1.0/docs/technical-reference/method-evidence/outcome-adaptive-point-treatment-c-tmle.md +184 -0
  97. cleverly-0.1.0/docs/technical-reference/method-evidence/point-treatment-msm-projection.md +154 -0
  98. cleverly-0.1.0/docs/technical-reference/method-evidence/randomized-missing-outcome-dr-tmle.md +129 -0
  99. cleverly-0.1.0/docs/technical-reference/method-evidence/repeated-cross-fitting.md +164 -0
  100. cleverly-0.1.0/docs/technical-reference/method-evidence/selector-based-multi-arm-c-tmle.md +130 -0
  101. cleverly-0.1.0/docs/technical-reference/method-evidence/selector-based-point-treatment-c-tmle.md +159 -0
  102. cleverly-0.1.0/docs/technical-reference/method-evidence/stacked-point-treatment-cv-tmle.md +207 -0
  103. cleverly-0.1.0/docs/technical-reference/method-evidence/stochastic-point-treatment-regimes.md +123 -0
  104. cleverly-0.1.0/docs/technical-reference/method-evidence/validation-grid.md +66 -0
  105. cleverly-0.1.0/docs/technical-reference/method-evidence/weighted-point-treatment-tmle.md +175 -0
  106. cleverly-0.1.0/docs/technical-reference/msm-projections.md +181 -0
  107. cleverly-0.1.0/docs/technical-reference/point-treatment-tmle.md +308 -0
  108. cleverly-0.1.0/docs/technical-reference/scope-and-refusals.md +134 -0
  109. cleverly-0.1.0/docs/technical-reference/validation-methods.md +515 -0
  110. cleverly-0.1.0/docs/user-guide/capabilities.md +45 -0
  111. cleverly-0.1.0/docs/user-guide/data-design.md +78 -0
  112. cleverly-0.1.0/docs/user-guide/estimands.md +120 -0
  113. cleverly-0.1.0/docs/user-guide/index.md +29 -0
  114. cleverly-0.1.0/docs/user-guide/longitudinal.md +101 -0
  115. cleverly-0.1.0/docs/user-guide/methods-learners.md +129 -0
  116. cleverly-0.1.0/docs/user-guide/results-assessment.md +182 -0
  117. cleverly-0.1.0/docs/workflow.md +110 -0
  118. cleverly-0.1.0/noxfile.py +110 -0
  119. cleverly-0.1.0/pyproject.toml +167 -0
  120. cleverly-0.1.0/scripts/check_distribution.py +77 -0
  121. cleverly-0.1.0/scripts/check_release.py +41 -0
  122. cleverly-0.1.0/scripts/execute_notebook.py +48 -0
  123. cleverly-0.1.0/scripts/smoke_backend.py +103 -0
  124. cleverly-0.1.0/src/cleverly/__init__.py +162 -0
  125. cleverly-0.1.0/src/cleverly/_typing.py +121 -0
  126. cleverly-0.1.0/src/cleverly/_version.py +5 -0
  127. cleverly-0.1.0/src/cleverly/assessment.py +2324 -0
  128. cleverly-0.1.0/src/cleverly/data/__init__.py +8 -0
  129. cleverly-0.1.0/src/cleverly/data/causal_data.py +1249 -0
  130. cleverly-0.1.0/src/cleverly/data/validate.py +453 -0
  131. cleverly-0.1.0/src/cleverly/data/weighting.py +627 -0
  132. cleverly-0.1.0/src/cleverly/datasets/__init__.py +97 -0
  133. cleverly-0.1.0/src/cleverly/datasets/longitudinal.py +894 -0
  134. cleverly-0.1.0/src/cleverly/datasets/synthetic.py +1708 -0
  135. cleverly-0.1.0/src/cleverly/estimators/__init__.py +43 -0
  136. cleverly-0.1.0/src/cleverly/estimators/_nuisance.py +1320 -0
  137. cleverly-0.1.0/src/cleverly/estimators/base.py +1233 -0
  138. cleverly-0.1.0/src/cleverly/estimators/ctmle.py +1461 -0
  139. cleverly-0.1.0/src/cleverly/estimators/direct_effect.py +387 -0
  140. cleverly-0.1.0/src/cleverly/estimators/drtmle.py +996 -0
  141. cleverly-0.1.0/src/cleverly/estimators/reduced.py +964 -0
  142. cleverly-0.1.0/src/cleverly/estimators/serialize.py +99 -0
  143. cleverly-0.1.0/src/cleverly/estimators/targeting.py +2610 -0
  144. cleverly-0.1.0/src/cleverly/estimators/tmle.py +2809 -0
  145. cleverly-0.1.0/src/cleverly/exceptions.py +108 -0
  146. cleverly-0.1.0/src/cleverly/fluctuation/__init__.py +74 -0
  147. cleverly-0.1.0/src/cleverly/fluctuation/_score.py +72 -0
  148. cleverly-0.1.0/src/cleverly/fluctuation/iterative.py +820 -0
  149. cleverly-0.1.0/src/cleverly/fluctuation/mechanism.py +641 -0
  150. cleverly-0.1.0/src/cleverly/fluctuation/one_step.py +176 -0
  151. cleverly-0.1.0/src/cleverly/fluctuation/reduced.py +273 -0
  152. cleverly-0.1.0/src/cleverly/fluctuation/submodel.py +1362 -0
  153. cleverly-0.1.0/src/cleverly/inference/__init__.py +64 -0
  154. cleverly-0.1.0/src/cleverly/inference/bootstrap.py +305 -0
  155. cleverly-0.1.0/src/cleverly/inference/cluster.py +267 -0
  156. cleverly-0.1.0/src/cleverly/inference/delta.py +173 -0
  157. cleverly-0.1.0/src/cleverly/inference/influence.py +1449 -0
  158. cleverly-0.1.0/src/cleverly/inference/multiplier.py +471 -0
  159. cleverly-0.1.0/src/cleverly/inference/results.py +98 -0
  160. cleverly-0.1.0/src/cleverly/interventions/__init__.py +49 -0
  161. cleverly-0.1.0/src/cleverly/interventions/base.py +582 -0
  162. cleverly-0.1.0/src/cleverly/interventions/incremental.py +564 -0
  163. cleverly-0.1.0/src/cleverly/interventions/shift.py +558 -0
  164. cleverly-0.1.0/src/cleverly/interventions/support.py +238 -0
  165. cleverly-0.1.0/src/cleverly/learners/__init__.py +61 -0
  166. cleverly-0.1.0/src/cleverly/learners/_fitting.py +256 -0
  167. cleverly-0.1.0/src/cleverly/learners/_threads.py +322 -0
  168. cleverly-0.1.0/src/cleverly/learners/crossfit.py +520 -0
  169. cleverly-0.1.0/src/cleverly/learners/density.py +507 -0
  170. cleverly-0.1.0/src/cleverly/learners/library.py +153 -0
  171. cleverly-0.1.0/src/cleverly/learners/screeners.py +170 -0
  172. cleverly-0.1.0/src/cleverly/learners/super_learner.py +631 -0
  173. cleverly-0.1.0/src/cleverly/longitudinal/__init__.py +69 -0
  174. cleverly-0.1.0/src/cleverly/longitudinal/data.py +1378 -0
  175. cleverly-0.1.0/src/cleverly/longitudinal/estimator.py +1701 -0
  176. cleverly-0.1.0/src/cleverly/longitudinal/msm.py +1069 -0
  177. cleverly-0.1.0/src/cleverly/longitudinal/regimen.py +346 -0
  178. cleverly-0.1.0/src/cleverly/longitudinal/sequential.py +1261 -0
  179. cleverly-0.1.0/src/cleverly/methods.py +866 -0
  180. cleverly-0.1.0/src/cleverly/msm.py +1029 -0
  181. cleverly-0.1.0/src/cleverly/provenance.py +232 -0
  182. cleverly-0.1.0/src/cleverly/py.typed +0 -0
  183. cleverly-0.1.0/src/cleverly/sensitivity/__init__.py +39 -0
  184. cleverly-0.1.0/src/cleverly/sensitivity/_parameters.py +188 -0
  185. cleverly-0.1.0/src/cleverly/sensitivity/evalue.py +321 -0
  186. cleverly-0.1.0/src/cleverly/sensitivity/missingness.py +392 -0
  187. cleverly-0.1.0/src/cleverly/sensitivity/omitted_variable.py +883 -0
  188. cleverly-0.1.0/src/cleverly/sensitivity/positivity.py +839 -0
  189. cleverly-0.1.0/src/cleverly/study.py +2071 -0
  190. cleverly-0.1.0/src/cleverly/targets/__init__.py +240 -0
  191. cleverly-0.1.0/src/cleverly/targets/base.py +484 -0
  192. cleverly-0.1.0/src/cleverly/targets/builtin.py +637 -0
  193. cleverly-0.1.0/src/cleverly/utils/__init__.py +31 -0
  194. cleverly-0.1.0/src/cleverly/utils/bounds.py +272 -0
  195. cleverly-0.1.0/src/cleverly/utils/frames.py +195 -0
  196. cleverly-0.1.0/src/cleverly/utils/parallel.py +52 -0
  197. cleverly-0.1.0/src/cleverly/utils/phases.py +300 -0
  198. cleverly-0.1.0/src/cleverly/utils/records.py +101 -0
  199. cleverly-0.1.0/src/cleverly/utils/text.py +49 -0
  200. cleverly-0.1.0/src/cleverly/validation/__init__.py +70 -0
  201. cleverly-0.1.0/src/cleverly/validation/drtmle.py +840 -0
  202. cleverly-0.1.0/src/cleverly/validation/nuisance.py +555 -0
  203. cleverly-0.1.0/src/cleverly/validation/refute.py +1743 -0
  204. cleverly-0.1.0/src/cleverly/validation/score.py +745 -0
  205. cleverly-0.1.0/src/cleverly/validation/simulation.py +650 -0
  206. cleverly-0.1.0/src/cleverly/variable_importance.py +240 -0
  207. cleverly-0.1.0/uv.lock +3110 -0
@@ -0,0 +1,2 @@
1
+ # Reuse the repository's existing Claude instructions when no AGENTS.md is present.
2
+ project_doc_fallback_filenames = ["CLAUDE.md"]
@@ -0,0 +1,15 @@
1
+ # A checkout must produce the same bytes on every platform, because some of these
2
+ # bytes are hashed. `tests/studies/evidence/manifest.py` records a sha256 over each
3
+ # published study artifact and `tests/unit/test_method_evidence.py` checks it, so a
4
+ # line ending rewritten at checkout time turns a provenance claim into a statement
5
+ # about the machine that ran `git clone`. Without this file and with the common
6
+ # `core.autocrlf=true`, the canonical TMLE and CV-TMLE manifests verified on Windows
7
+ # and failed on Linux CI, which is the one thing a byte-exact claim must not do.
8
+ #
9
+ # `text=auto` still lets git detect binary content; `eol=lf` fixes the working-tree
10
+ # ending for everything it does classify as text. The compressed replicate archives
11
+ # are marked explicitly rather than left to detection, since they are the artifacts
12
+ # whose bytes matter most and a misdetection there would corrupt them silently.
13
+ * text=auto eol=lf
14
+
15
+ *.gz binary
@@ -0,0 +1,58 @@
1
+ name: Set up cleverly's environment
2
+ description: >-
3
+ uv, a pinned interpreter, a virtual environment on PATH, and the package installed
4
+ editable from the committed lock. Jobs that need a working cleverly do this identically.
5
+
6
+ inputs:
7
+ python-version:
8
+ description: The interpreter to install and build the virtual environment against.
9
+ required: true
10
+ extra:
11
+ description: Which optional-dependency group to install; "dev" for anything running pytest.
12
+ required: false
13
+ default: dev
14
+ packages:
15
+ description: >-
16
+ Further requirements to install after the locked project, space-separated.
17
+ required: false
18
+ default: ""
19
+
20
+ runs:
21
+ using: composite
22
+ steps:
23
+ - uses: astral-sh/setup-uv@v5
24
+
25
+ - run: uv python install "${PYTHON_VERSION}"
26
+ shell: bash
27
+ env:
28
+ PYTHON_VERSION: ${{ inputs.python-version }}
29
+
30
+ # A virtual environment rather than `--system`, and the reason is not only that
31
+ # `--system` stopped working. `ubuntu-latest`'s Debian Python is marked externally
32
+ # managed (PEP 668), so `uv pip install --system` is now refused outright -- which
33
+ # turned every job in this repository except `lint` red, with no commit to blame, and
34
+ # kept them red while the merges went on. The manually dispatched workflows failed
35
+ # the same way and more quietly still: a run that dies at its install step reports a
36
+ # failed tier rather than a missing one.
37
+ #
38
+ # But `--system` was the wrong instrument even while it worked: it installs into
39
+ # whichever interpreter `/usr` holds, *not* the one `uv python install` just placed, so
40
+ # the version named and the version tested were only ever the same by accident. Doing
41
+ # it here makes them the same by construction, which is what `ci.yml`'s matrix needs.
42
+ - run: |
43
+ uv venv --python "${PYTHON_VERSION}"
44
+ echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
45
+ shell: bash
46
+ env:
47
+ PYTHON_VERSION: ${{ inputs.python-version }}
48
+
49
+ - run: uv sync --locked --extra "${EXTRA}"
50
+ shell: bash
51
+ env:
52
+ EXTRA: ${{ inputs.extra }}
53
+
54
+ - if: ${{ inputs.packages != '' }}
55
+ run: uv pip install ${PACKAGES}
56
+ shell: bash
57
+ env:
58
+ PACKAGES: ${{ inputs.packages }}
@@ -0,0 +1,23 @@
1
+ <!-- Guides: docs/development/contributing.md and docs/development/pull-requests.md -->
2
+
3
+ ## What this changes
4
+
5
+ <!-- The claim this change makes, and the reason for it. Name the file to read first. -->
6
+
7
+ ## Evidence
8
+
9
+ <!-- What moved, what stayed identical, and the gate you ran. For example:
10
+ pytest -m "not slow": 5081 passed, 113 skipped. -->
11
+
12
+ ## Checks
13
+
14
+ - [ ] `ruff check .` and `ruff format --check .` pass
15
+ - [ ] `mypy` passes
16
+ - [ ] `pytest -m "not slow" -q` passes
17
+ - [ ] `python -m tests.prose` findings are fixed, or carry `accepted: <reason>` in `tests/prose-report.md`
18
+ - [ ] `nox -s docs` passes, if a document or a docstring changed
19
+ - [ ] `python -m build`, strict Twine validation, archive checks, and clean install checks pass
20
+ - [ ] regenerated artifacts carry matching manifest hashes and line-feed endings
21
+
22
+ <!-- CI runs all of these except the prose report. Running them locally first saves a round trip,
23
+ and `nox -s docs` is the slowest one to learn about from a runner. -->
@@ -0,0 +1,143 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ lint:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: ./.github/actions/setup-env
19
+ with:
20
+ python-version: "3.12"
21
+ extra: dev
22
+ - run: ruff check .
23
+ - run: ruff format --check .
24
+ # Prose is not linted here. `tests/prose.py` reports on it and the fast tier gates the
25
+ # *report*: a finding with no recorded judgment fails, the prose itself never does. The
26
+ # reason is on record -- when the dash rule was a build error, the sweep that followed
27
+ # optimized for green and broke six sentences to satisfy it.
28
+
29
+ typecheck:
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ # The install steps live in the composite action; it says why they are what they are.
34
+ # The interpreter matches `[tool.mypy]`'s `python_version`, which is `3.12` and says at
35
+ # length why it is not the `3.11` of `requires-python`: numpy's stubs use PEP 695 `type`
36
+ # statements, which a 3.11 target refuses to parse. Running the job on the interpreter it
37
+ # targets keeps the two from drifting apart silently. **3.11 is still supported and still
38
+ # tested**. The `tests` matrix below runs the fast tier on it.
39
+ - uses: ./.github/actions/setup-env
40
+ with:
41
+ python-version: "3.12"
42
+ extra: dev
43
+ - run: mypy
44
+
45
+ # The site that `pages.yml` deploys, built here before the merge rather than after it.
46
+ #
47
+ # This is not the removed documentation dispatch, and `docs/architecture-invariants.md` still
48
+ # rules that one out. That job re-ran what the fast tier already runs. This one covers what no
49
+ # fast test can see: numpydoc validation of every rendered docstring, a document that no
50
+ # toctree references, and a cross-reference Sphinx cannot resolve. `pages.yml` triggers on a
51
+ # push to `main`, so until now the first run of `-W` happened after the merge, and a broken
52
+ # docstring took the published site down rather than failing a pull request.
53
+ #
54
+ # 3.12 is the interpreter `pages.yml` deploys from, so this job answers the question that
55
+ # matters: does the site that will ship build clean. The `docs` extra is what that build needs,
56
+ # and this job is the one that installs it.
57
+ docs:
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - uses: actions/checkout@v4
61
+ - uses: ./.github/actions/setup-env
62
+ with:
63
+ python-version: "3.12"
64
+ extra: docs
65
+ - run: sphinx-build -W --keep-going -b html docs docs/_build/html
66
+
67
+ tests:
68
+ runs-on: ubuntu-latest
69
+ strategy:
70
+ fail-fast: false
71
+ matrix:
72
+ python-version: ["3.11", "3.12", "3.13"]
73
+ steps:
74
+ - uses: actions/checkout@v4
75
+ # The matrix version is threaded through to `uv venv --python`; without that, every
76
+ # row of the matrix would install into `/usr`'s interpreter and exercise the same one.
77
+ - uses: ./.github/actions/setup-env
78
+ with:
79
+ python-version: ${{ matrix.python-version }}
80
+ # `-n auto` resolves through psutil, else `os.cpu_count()` -- neither of which reads a
81
+ # container's CFS quota, so on a quota-limited runner it asks for the *host's* cores.
82
+ # `tests.parallel` goes through joblib/loky, which does read it.
83
+ - name: Size the worker pool from the cores this job may actually use
84
+ run: |
85
+ echo "PYTEST_XDIST_AUTO_NUM_WORKERS=$(python -m tests.parallel --workers)" >> "$GITHUB_ENV"
86
+ python -m tests.parallel
87
+ # Behavioral examples belong in this fast unit/integration/e2e tier, and so do the
88
+ # static documentation checks -- links resolve, every `python` fence parses. Neither
89
+ # executes an example, and both are cheap, so there is no documentation dispatch.
90
+ - run: pytest -m "not slow" -q -n auto
91
+
92
+ # One backend at a time, which the `tests` job above cannot check: it installs `.[dev]`,
93
+ # which pulls pandas *and* polars, so the single-backend and neither-installed branches of
94
+ # `cleverly.utils.frames` had never executed anywhere. That is how `make_longitudinal_weighted`
95
+ # came to hard-code `backend="pandas"` and call `.loc` -- unusable on a polars-only install,
96
+ # and invisible to a suite that always had both.
97
+ #
98
+ # The test suite itself imports pandas and polars freely, so it cannot be the check here.
99
+ # What runs instead is the import and a fit end to end through the one backend present.
100
+ minimal-install:
101
+ runs-on: ubuntu-latest
102
+ strategy:
103
+ fail-fast: false
104
+ matrix:
105
+ backend: [pandas, polars]
106
+ steps:
107
+ - uses: actions/checkout@v4
108
+ - uses: ./.github/actions/setup-env
109
+ with:
110
+ python-version: "3.11"
111
+ extra: ${{ matrix.backend }}
112
+ - name: A fit, its reports and the generators, on one backend
113
+ run: python scripts/smoke_backend.py ${{ matrix.backend }}
114
+
115
+ package:
116
+ runs-on: ubuntu-latest
117
+ steps:
118
+ - uses: actions/checkout@v4
119
+ - uses: ./.github/actions/setup-env
120
+ with:
121
+ python-version: "3.12"
122
+ extra: dev
123
+ - name: Build the wheel and source distribution
124
+ run: python -m build
125
+ - name: Validate package metadata and archive contents
126
+ run: |
127
+ python -m twine check --strict dist/*
128
+ python scripts/check_distribution.py dist
129
+ - name: Install and exercise the wheel in a clean environment
130
+ run: |
131
+ uv venv --python 3.12 /tmp/cleverly-wheel
132
+ uv pip install --python /tmp/cleverly-wheel/bin/python pandas "$(find dist -name '*.whl')"
133
+ /tmp/cleverly-wheel/bin/python scripts/smoke_backend.py pandas
134
+ - name: Install and exercise the source distribution in a clean environment
135
+ run: |
136
+ uv venv --python 3.12 /tmp/cleverly-source
137
+ uv pip install --python /tmp/cleverly-source/bin/python polars "$(find dist -name '*.tar.gz')"
138
+ /tmp/cleverly-source/bin/python scripts/smoke_backend.py polars
139
+ - uses: actions/upload-artifact@v5
140
+ with:
141
+ name: distributions
142
+ path: dist/
143
+ if-no-files-found: error
@@ -0,0 +1,45 @@
1
+ name: pages
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: pages
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: astral-sh/setup-uv@v5
23
+ with:
24
+ enable-cache: true
25
+ - name: Install Python
26
+ run: uv python install 3.12
27
+ - name: Install documentation dependencies
28
+ run: uv sync --locked --extra docs
29
+ - name: Build documentation
30
+ run: uv run --no-sync sphinx-build -W --keep-going -b html docs docs/_build/html
31
+ - uses: actions/configure-pages@v5
32
+ - uses: actions/upload-pages-artifact@v4
33
+ with:
34
+ path: docs/_build/html
35
+
36
+ deploy:
37
+ environment:
38
+ name: github-pages
39
+ url: ${{ steps.deployment.outputs.page_url }}
40
+ runs-on: ubuntu-latest
41
+ needs: build
42
+ steps:
43
+ - name: Deploy to GitHub Pages
44
+ id: deployment
45
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,81 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["v0.1.*"]
6
+
7
+ permissions: {}
8
+
9
+ jobs:
10
+ validate:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+ steps:
15
+ - uses: actions/checkout@v6
16
+ with:
17
+ fetch-depth: 0
18
+ persist-credentials: false
19
+ - name: Match the tag, package version, and reviewed main history
20
+ run: |
21
+ python scripts/check_release.py "$GITHUB_REF_NAME"
22
+ git fetch --no-tags origin main
23
+ git merge-base --is-ancestor "$GITHUB_SHA" origin/main
24
+
25
+ build:
26
+ needs: validate
27
+ runs-on: ubuntu-latest
28
+ permissions:
29
+ contents: read
30
+ steps:
31
+ - uses: actions/checkout@v6
32
+ with:
33
+ persist-credentials: false
34
+ - uses: ./.github/actions/setup-env
35
+ with:
36
+ python-version: "3.12"
37
+ extra: dev
38
+ - run: python -m build
39
+ - run: python -m twine check --strict dist/*
40
+ - run: python scripts/check_distribution.py dist
41
+ - uses: actions/upload-artifact@v5
42
+ with:
43
+ name: release-distributions
44
+ path: dist/
45
+ if-no-files-found: error
46
+
47
+ publish-testpypi:
48
+ needs: build
49
+ runs-on: ubuntu-latest
50
+ environment:
51
+ name: testpypi
52
+ url: https://test.pypi.org/p/cleverly
53
+ permissions:
54
+ actions: read
55
+ id-token: write
56
+ steps:
57
+ - uses: actions/download-artifact@v6
58
+ with:
59
+ name: release-distributions
60
+ path: dist/
61
+ # pypa/gh-action-pypi-publish v1.14.2 from the official upstream repository.
62
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33
63
+ with:
64
+ repository-url: https://test.pypi.org/legacy/
65
+
66
+ publish-pypi:
67
+ needs: publish-testpypi
68
+ runs-on: ubuntu-latest
69
+ environment:
70
+ name: pypi
71
+ url: https://pypi.org/p/cleverly
72
+ permissions:
73
+ actions: read
74
+ id-token: write
75
+ steps:
76
+ - uses: actions/download-artifact@v6
77
+ with:
78
+ name: release-distributions
79
+ path: dist/
80
+ # pypa/gh-action-pypi-publish v1.14.2 from the official upstream repository.
81
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33
@@ -0,0 +1,39 @@
1
+ name: manual-validation
2
+
3
+ # Statistical validation only. Documentation has no dispatch of its own on purpose: the
4
+ # links and example-syntax checks are ordinary fast-tier tests and the markdown formatter
5
+ # runs in `ci.yml`'s lint job, so a documentation suite here would re-run checks that
6
+ # already run unconditionally -- which reads as a separate gate while adding no coverage.
7
+ on:
8
+ workflow_dispatch:
9
+ inputs:
10
+ selection:
11
+ description: Optional pytest node id; the whole evidence re-execution tier when empty
12
+ type: string
13
+ default: ""
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ validation:
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 360
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: ./.github/actions/setup-env
25
+ with:
26
+ python-version: "3.12"
27
+ - name: Size the worker pool from the cores this job may actually use
28
+ run: |
29
+ echo "PYTEST_XDIST_AUTO_NUM_WORKERS=$(python -m tests.parallel --workers)" >> "$GITHUB_ENV"
30
+ python -m tests.parallel
31
+ - name: Statistical validation
32
+ env:
33
+ SELECTION: ${{ inputs.selection }}
34
+ run: |
35
+ if [ -n "$SELECTION" ]; then
36
+ pytest "$SELECTION" -q -n auto --durations=20
37
+ else
38
+ pytest -m slow -q -n auto --durations=20
39
+ fi
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ .venv/
7
+ venv/
8
+ .nox/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .coverage
13
+ .claude/
14
+ htmlcov/
15
+ target/
16
+ docs/_build/
17
+ docs/api/generated/
18
+
19
+ # Where every canonical study's README sends a disposable regeneration.
20
+ .tmp/
@@ -0,0 +1,3 @@
1
+ # Repository instructions
2
+
3
+ Read and follow [`CLAUDE.md`](CLAUDE.md) for all repository-specific working agreements.
@@ -0,0 +1,113 @@
1
+ # Working on cleverly
2
+
3
+ `cleverly` is alpha software under heavy development. Treat the current architecture as
4
+ provisional: inspect the code and tests before changing it, and do not preserve an implementation
5
+ shape solely because this file once described it.
6
+
7
+ This file holds only the working agreements that no other file states and no test enforces.
8
+ Everything else is routed:
9
+
10
+ | for | read |
11
+ | --- | --- |
12
+ | setup, development commands, the public overview | `README.md` |
13
+ | making a change, and the checks it needs | `docs/development/contributing.md` |
14
+ | opening a pull request, and the commit style | `docs/development/pull-requests.md` |
15
+ | where technical documentation lives | `docs/README.md` |
16
+ | cross-module constraints not derivable from one implementation | `docs/architecture-invariants.md` |
17
+ | test tiers, and which deprecated studies no longer run | `docs/development/testing-strategy.md` |
18
+ | designing and registering a validation study | `docs/development/method-benchmarking.md` |
19
+ | which instrument covers which estimand | `docs/technical-reference/evidence.md` |
20
+ | what each shipped method was validated against | `docs/technical-reference/index.md` |
21
+
22
+ Current behavior is determined by code and tests, not by historical plans or investigation notes.
23
+
24
+ ## Scientific changes
25
+
26
+ - Exact-law checks are blind to terms that vanish at the truth. When a sign, mask, guard, or
27
+ counterfactual block can disappear, add a nonzero witness or a deliberate-mutation control that
28
+ fails when that component is wrong.
29
+ - Refuse unsupported but well-posed compositions explicitly and explain what is missing. Do not
30
+ silently return a convenient approximation to a different estimand.
31
+
32
+ ## Tests and tooling
33
+
34
+ - Read `docs/development/testing-strategy.md` before choosing a tier. The fast tier is the default
35
+ handoff gate. The repeated-sampling studies that predate the registered rows are deprecated
36
+ and skipped; do not re-enable one to justify a change. A shipped method is validated by its rows
37
+ in the implementation validation grid, which the fast tier recomputes from committed artifacts.
38
+ Nothing is contingent on `pytest -m slow` running.
39
+ - A refactor is not a reason to regenerate a study. The Python module hashes in a study's
40
+ `manifest.json` record the run; no test gates them, so cleaning shared code under
41
+ `tests/studies/evidence/` is free. The container and R-runner hashes *are* gated. Declare a
42
+ result-neutral edit in `tests/canonical/provenance-revisions.md` rather than rewriting a
43
+ recorded hash, which would leave the manifest describing bytes that never ran.
44
+ `docs/development/method-benchmarking.md` says how to tell the two kinds of change apart.
45
+ - Ruff and mypy are pinned once in `pyproject.toml`'s `dev` extra, which resolves to
46
+ `cleverly[all]` plus tooling. An optional extra kept out of `dev` *and* out of a dedicated CI job
47
+ is installed by no session, so its tests can only skip, and a skipped correctness check reads
48
+ exactly like a passing one. Put a new extra in `dev`, or give it a job that installs and runs its
49
+ tests.
50
+ - Ruff *formats* the Python examples in Markdown, so run it over the whole tree. Its linter does
51
+ not read Markdown at all, and the formatter skips any block it cannot parse. Neither one sees a
52
+ syntax error in an example.
53
+ - Follow `docs/development/pull-requests.md` when you prepare a handoff. It gives the commit
54
+ subject and body style, the evidence line the body carries, and what each CI job checks. The
55
+ `docs` job builds the site with `-W` on every pull request, so a docstring that numpydoc rejects
56
+ now fails the request rather than the deploy. Run `nox -s docs` before you hand off.
57
+
58
+ ## Documentation writing
59
+
60
+ The root `README.md` and reader-facing documents under `docs/` align with Issue 9 of
61
+ ASD-STE100 Simplified Technical English. This project does not claim certified compliance.
62
+
63
+ - Write one idea per sentence. Keep sentences to 20 words in procedures and 25 in descriptions.
64
+ - Keep paragraphs to six sentences. Prefer three.
65
+ - Use the active voice, the present tense, and a named actor.
66
+ - Give the instruction first and the reason second.
67
+ - Use one word for one meaning, and do not use the same word as two parts of speech.
68
+ - Keep articles. Do not build a noun cluster longer than three words.
69
+ - Do not join clauses with an em dash or `--`. Use a full stop, or a table.
70
+ - When the content is parallel, write a table. In the technical reference the table is the primary
71
+ communication device and the prose exists to define its terms.
72
+ - Statistical terms of art are exempt from the vocabulary restriction: influence curve, nuisance,
73
+ targeting, remainder, estimand names, and any API identifier.
74
+ - Give evidence for each material claim. Cite the source, name the test or artifact, or state the
75
+ applicable condition. Remove adjectives and transitions that add no verifiable information.
76
+
77
+ When you change a reader-facing document, run `python -m tests.prose`, review every finding it
78
+ reports, and plan a fix that keeps the sentence whole. Where the standard should not apply, record
79
+ `accepted: <reason>` against that finding in `tests/prose-report.md`; that is a passing outcome and
80
+ the reason is the point. The fast tier fails on a finding nobody has judged, never on the prose
81
+ itself, because a mechanical edit that satisfies a rule and breaks a sentence is the failure this
82
+ report exists to prevent. No tool here certifies STE compliance or verifies a scientific claim.
83
+ Check those against the code, tests, artifacts, and sources.
84
+
85
+ Scope is `README.md` and every reader-facing Markdown, RST, or notebook source under `docs/`.
86
+ Generated API pages and `docs/_build/` are not source. Rewrite the text a change touches. Do not
87
+ sweep unrelated pages unless the user requests a broad documentation review.
88
+
89
+ ## Docstrings
90
+
91
+ Docstrings are numpydoc, and `sphinx.ext.napoleon` is not installed. The loose `name:` form that
92
+ napoleon accepted is now a build error, because `pages.yml` builds with `-W` and `docs/conf.py`
93
+ enables `GL06, GL07, PR01, PR02, PR04, PR10, RT01`. Write `name : type` always, one entry per
94
+ parameter. Two names on one line become one parameter with a comma in its name.
95
+
96
+ - Document a frozen dataclass's fields under `Parameters`. numpydoc reads the generated signature,
97
+ so a field described only under `Attributes` reads as undocumented. Reserve `Attributes` for
98
+ derived properties.
99
+ - Give a property-backed attribute its name and type and no description. numpydoc renders the
100
+ property's own docstring over anything written there.
101
+ - Where a synthetic signature produces a finding nobody can fix, use the inline
102
+ `# numpydoc ignore=PR01` form on the definition line. Do not use `numpydoc_validation_exclude`,
103
+ which drops the object from every check rather than one.
104
+ - `Examples` and `See Also` are required on the task spine only, which
105
+ `tests/unit/test_documentation_api.py:EXAMPLE_TARGETS` declares. The targets include core
106
+ methods so a direct method anchor explains its own call. Every See Also entry carries a
107
+ description.
108
+ - Show the smallest normal use of the documented object or method. Start with the common case.
109
+ Include every import, use compact data, and check deterministic output. Do not use an example
110
+ to assert a statistical guarantee from one sample.
111
+ - An example must run in the fast tier without `doctest.SKIP`. Pass explicit learners to fits;
112
+ the default learner library costs 30 to 120 seconds per fit. Put expensive studies and extended
113
+ comparisons in narrative documentation and test them separately.
@@ -0,0 +1,60 @@
1
+ # Contributing to cleverly
2
+
3
+ `cleverly` is alpha software for causal studies built on targeted maximum likelihood estimation.
4
+ One person maintains it, so a review can take several days. Open an issue before you start a large
5
+ change.
6
+
7
+ The full guides live in the documentation site.
8
+
9
+ | for | read |
10
+ | --- | --- |
11
+ | setup, branch names, the checks, documentation and docstring rules | [Contributing](https://esbraun.github.io/cleverly-tmle/development/contributing.html) |
12
+ | the commit style, the pull request body, what CI does and does not check | [Pull requests](https://esbraun.github.io/cleverly-tmle/development/pull-requests.html) |
13
+ | alpha versions, package checks, tags, and Trusted Publishing | [Releases](https://esbraun.github.io/cleverly-tmle/development/releases.html) |
14
+ | which tier a change has to satisfy | [Test tiers and gates](https://esbraun.github.io/cleverly-tmle/development/testing-strategy.html) |
15
+ | designing and registering a validation study | [Method benchmarking strategy](https://esbraun.github.io/cleverly-tmle/development/method-benchmarking.html) |
16
+
17
+ The sources are `docs/development/contributing.md` and `docs/development/pull-requests.md`.
18
+
19
+ ## Set up
20
+
21
+ ```bash
22
+ git clone https://github.com/esbraun/cleverly-tmle.git
23
+ cd cleverly-tmle
24
+ uv venv
25
+ uv pip install -e ".[dev,docs]"
26
+ ```
27
+
28
+ ## Run the checks
29
+
30
+ ```bash
31
+ ruff check .
32
+ ruff format --check .
33
+ python -m tests.prose
34
+ mypy
35
+ pytest -m "not slow" -q
36
+ sphinx-build -W --keep-going -b html docs docs/_build/html
37
+ ```
38
+
39
+ `nox` with no argument runs the `lint`, `typecheck`, `docs`, and `tests` sessions, which mirror the
40
+ CI jobs. `nox -s docs` runs the last command in an isolated environment, which is what CI uses. Run
41
+ one test tier at a time, because each tier expects the whole machine.
42
+
43
+ `python -m tests.prose` reports on the reader-facing prose. It changes nothing and it fails
44
+ nothing. Fix each finding, or record `accepted: <reason>` against it in `tests/prose-report.md`.
45
+ The fast tier fails only on a finding with no recorded judgment.
46
+
47
+ ## Working agreements
48
+
49
+ `CLAUDE.md` holds the working agreements that no other file states and no test enforces. Read it
50
+ before you change scientific code, documentation prose, or a docstring.
51
+
52
+ ## License
53
+
54
+ `cleverly` is under the [MIT License](LICENSE). Your contribution to the project is licensed
55
+ under the same terms.
56
+
57
+ One directory differs. `tests/canonical/` is under the
58
+ [GNU General Public License v3.0](tests/canonical/LICENSE), because its R runners call
59
+ reference packages in the same process. A contribution to that directory is licensed under
60
+ the GPL. No published distribution carries it.
cleverly-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Eric Braun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.