Skip to content

Commit 4d6a2e8

Browse files
Merge v1.4.0 — Phase 1 continuous causal core
2 parents 7a3a6a6 + 479444f commit 4d6a2e8

25 files changed

Lines changed: 2493 additions & 2 deletions

CHANGELOG.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,46 @@ All notable changes to this project are documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.4.0] - 2026-07-10
9+
10+
Phase 1 — the continuous causal core: identification-aware estimation, partial identification for
11+
continuous / heavy-tailed / transported targets, finite-sample conformal wrappers, and continuous
12+
mechanisms with posterior abduction. Fully additive on top of 1.3.0; every new inferential routine
13+
returns a unified `Certificate`. No shipped signature or behaviour changed; the `certify_decision`
14+
byte-for-byte regression pin and the `bench_causal_core` fast-path guards are preserved.
15+
16+
### Added
17+
- **`estimate/` — identification-aware DR/DML estimation** (§7.2): `certify_effect` compiles a graph
18+
query into a back-door plan (`identify_effect``backdoor_adjustment_set`) and estimates the ATE
19+
with plug-in, self-normalised IPW, AIPW, or cross-fitted DML (influence-function CIs), returning an
20+
`IDENTIFIED` certificate. Non-identifiable or front-door/general-ID queries hedge (never a silent
21+
point); destroyed positivity downgrades to a hedge (I3). Pure-NumPy default nuisances
22+
(ridge/logistic) + sklearn-style pluggable — no scipy/sklearn hard dependency. `estimate_ate` /
23+
`EffectEstimate` expose the estimators directly. `Certificate` gains an additive optional `ci`.
24+
- **`bounds/` — estimated-propensity MSM + heavy-tail/quantile targets** (§7.3):
25+
`msm_sensitivity_bounds` bounds `E[Y(1)]` under Tan's MSM with *estimated* propensities (reduces
26+
exactly to `ipw_sensitivity_bounds` on the known-propensity path); `moment_diagnostic` (Hill
27+
tail-index + finite-variance) with `certify_mean` downgrading a mean request to a median on
28+
infinite-variance data (I3); `certify_quantile` / `weighted_quantile` with percentile-bootstrap
29+
CIs. The package re-exports the shipped nominal-propensity kernels.
30+
- **`conformal/` — finite-sample coverage** (§7.4): `conformal_quantile` (weighted, covariate-shift
31+
aware), `split_conformal_interval`, `cqr_interval` (conformalized quantile regression), and
32+
`certify_conformal_interval` (`EMPIRICAL`, exchangeability recorded). Marginal coverage ≥ 1 − α.
33+
- **`transport/` — data-plane transport estimation** (§7.5): `certify_transported_effect` decides
34+
transportability via the shipped `transport_formula`, then estimates the transported mean from
35+
source/target observational data (direct = the source interventional mean transfers; adjustment =
36+
reweight source conditionals by the target covariate marginal); non-transportable → hedge.
37+
- **`scm/continuous/` — neural mechanisms + posterior abduction** (§7.1, torch): `MLPMechanism`,
38+
the invertible `LocationScaleMechanism`, exact `abduct_location_scale`, the amortized-VI
39+
`AmortizedGaussianAbduction`, and `certify_counterfactual` (`IDENTIFIED` for exact inversion,
40+
`EMPIRICAL` for VI; a posterior-predictive check recorded as a checkable assumption).
41+
42+
### Notes
43+
- No new dependencies: the estimation / bounds / conformal / transport layers are NumPy-only; the
44+
continuous mechanisms use the existing `[torch]` extra.
45+
- Deferred to a later 1.x minor: conditional normalizing-flow mechanisms and NUTS/NumPyro abduction;
46+
sequential DR / LTMLE and sequential-transport estimation; the 2.0 default-flip `FutureWarning`.
47+
848
## [1.3.0] - 2026-07-10
949

1050
The 1.x consolidation: one unified certificate protocol and a columnar data plane that every later

docs/architecture-map.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,31 @@ version is recorded, never a specific string.
116116
`experimental/ope.py``confounding_sensitivity_bounds(point, gamma)`, explicitly documented as a
117117
*qualitative, non-validated* sensitivity interval (not the published MSM bound). Phase 5's
118118
`experimental/cyclic/` lands here first.
119+
120+
## 9. Phase 1 — continuous causal core (v1.4.0)
121+
122+
Additive on 1.3.0; every new inferential routine returns a unified `Certificate`. NumPy-only except
123+
`scm/continuous/` (torch, `[torch]` extra). New leaf packages (re-export shims where they wrap
124+
shipped code, per plan §4):
125+
126+
- `estimate/` (§7.2) — `certify_effect` (graph query → back-door plan via `identify_effect` +
127+
`backdoor_adjustment_set` → DR/DML), `estimate_ate` / `EffectEstimate` (plug-in / IPW / AIPW /
128+
cross-fit DML with influence-function CIs), pure-numpy nuisances (`RidgeRegressor` /
129+
`LogisticRegressor`, sklearn-style pluggable). Non-identified / front-door / overlap-destroyed →
130+
hedge (I3).
131+
- `bounds/` (§7.3) — shim re-exporting `identification.bounds` + `bounds/continuous.py`:
132+
`msm_sensitivity_bounds` (estimated-`e` MSM; reduces to `ipw_sensitivity_bounds`),
133+
`moment_diagnostic` / `tail_index_hill`, `certify_mean` (heavy-tail → median downgrade),
134+
`certify_quantile` / `weighted_quantile` (percentile-bootstrap CIs).
135+
- `conformal/` (§7.4) — `conformal_quantile` (weighted), `split_conformal_interval`, `cqr_interval`,
136+
`certify_conformal_interval` (`EMPIRICAL`). Marginal coverage ≥ 1 − α.
137+
- `transport/` (§7.5) — shim over `identification.transport` + `transport/estimate.py`:
138+
`certify_transported_effect` (torch-free `transport_formula` decision + numpy g-computation),
139+
`transport_gcomp`.
140+
- `scm/continuous/` (§7.1, torch) — `MLPMechanism`, invertible `LocationScaleMechanism`,
141+
`abduct_location_scale` (exact inversion), `AmortizedGaussianAbduction` (ELBO VI),
142+
`certify_counterfactual`. Tests `importorskip("torch.nn")` → CI-verified (torch.nn broken locally).
143+
144+
`Certificate` gained an additive optional `ci: Interval | None` field (round-trips; no shipped field
145+
changed). The new public front doors are lazily exported from `causalrl.__init__` (torch-backed ones
146+
stay lazy, like `NeuralMechanism`).

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "causalrl"
3-
version = "1.3.0"
3+
version = "1.4.0"
44
description = "Causal reinforcement learning: the 9-task causal RL taxonomy, made runnable."
55
readme = "README.md"
66
requires-python = ">=3.11"

src/causalrl/__init__.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,27 @@
128128
"certify_decision": ("causalrl.identification.decision", "certify_decision"),
129129
"certify_estimate": ("causalrl.identification.decision", "certify_estimate"),
130130
"certify_policy": ("causalrl.scale", "certify_policy"),
131+
"certify_effect": ("causalrl.estimate.compiler", "certify_effect"),
132+
"estimate_ate": ("causalrl.estimate.estimators", "estimate_ate"),
133+
"EffectEstimate": ("causalrl.estimate.estimators", "EffectEstimate"),
134+
"msm_sensitivity_bounds": ("causalrl.bounds.continuous", "msm_sensitivity_bounds"),
135+
"certify_sensitivity_bounds": ("causalrl.bounds.continuous", "certify_sensitivity_bounds"),
136+
"certify_quantile": ("causalrl.bounds.continuous", "certify_quantile"),
137+
"certify_mean": ("causalrl.bounds.continuous", "certify_mean"),
138+
"moment_diagnostic": ("causalrl.bounds.continuous", "moment_diagnostic"),
139+
"conformal_quantile": ("causalrl.conformal.core", "conformal_quantile"),
140+
"split_conformal_interval": ("causalrl.conformal.core", "split_conformal_interval"),
141+
"cqr_interval": ("causalrl.conformal.core", "cqr_interval"),
142+
"certify_conformal_interval": ("causalrl.conformal.core", "certify_conformal_interval"),
143+
"certify_transported_effect": ("causalrl.transport.estimate", "certify_transported_effect"),
144+
"MLPMechanism": ("causalrl.scm.continuous.mechanisms", "MLPMechanism"),
145+
"LocationScaleMechanism": ("causalrl.scm.continuous.mechanisms", "LocationScaleMechanism"),
146+
"AmortizedGaussianAbduction": (
147+
"causalrl.scm.continuous.abduction",
148+
"AmortizedGaussianAbduction",
149+
),
150+
"abduct_location_scale": ("causalrl.scm.continuous.abduction", "abduct_location_scale"),
151+
"certify_counterfactual": ("causalrl.scm.continuous.abduction", "certify_counterfactual"),
131152
"DecisionCertificate": ("causalrl.identification.decision", "DecisionCertificate"),
132153
"PolicyValueContrast": ("causalrl.identification.estimate", "PolicyValueContrast"),
133154
"Certificate": ("causalrl.certify", "Certificate"),
@@ -224,6 +245,7 @@
224245
"PAG",
225246
"UCDTR",
226247
"Agent",
248+
"AmortizedGaussianAbduction",
227249
"Assumption",
228250
"BehavioralCloning",
229251
"BenchmarkEstimate",
@@ -249,6 +271,7 @@
249271
"DeepDeconfoundedQ",
250272
"DictNoiseLedger",
251273
"Domain",
274+
"EffectEstimate",
252275
"Estimand",
253276
"EstimandSpec",
254277
"ExogenousPosterior",
@@ -259,7 +282,9 @@
259282
"Interval",
260283
"Kind",
261284
"LinearGaussianMechanism",
285+
"LocationScaleMechanism",
262286
"MABUCEnv",
287+
"MLPMechanism",
263288
"Mechanism",
264289
"NaiveOffline",
265290
"NaivePOMISThompsonSampling",
@@ -290,6 +315,7 @@
290315
"UnverifiedAssumptionError",
291316
"Witness",
292317
"__version__",
318+
"abduct_location_scale",
293319
"apply_potential_shaping",
294320
"as_certificate",
295321
"backdoor_adjustment_set",
@@ -298,13 +324,22 @@
298324
"causal_curriculum",
299325
"causal_potential",
300326
"causal_q_bounds",
327+
"certify_conformal_interval",
328+
"certify_counterfactual",
301329
"certify_decision",
330+
"certify_effect",
302331
"certify_estimate",
332+
"certify_mean",
303333
"certify_policy",
334+
"certify_quantile",
335+
"certify_sensitivity_bounds",
336+
"certify_transported_effect",
304337
"conditional_mutual_information",
338+
"conformal_quantile",
305339
"confounding_bias_bound",
306340
"confounding_bias_per_step_bounds",
307341
"counterfactual_expectation",
342+
"cqr_interval",
308343
"cumulative_regret",
309344
"curriculum_q_learning",
310345
"decision_abstain_mask",
@@ -313,6 +348,7 @@
313348
"discover_interventional",
314349
"discover_latent",
315350
"effect_of_treatment_on_treated",
351+
"estimate_ate",
316352
"estimate_effect",
317353
"estimate_effect_with_experiments",
318354
"estimate_transport_general",
@@ -343,10 +379,12 @@
343379
"mi_flip_threshold",
344380
"minimal_intervention_sets",
345381
"mixed_nash_equilibria",
382+
"moment_diagnostic",
346383
"msm_contribution_bounds",
347384
"msm_per_step_bounds",
348385
"msm_policy_value_bounds",
349386
"msm_policy_value_bounds_certified",
387+
"msm_sensitivity_bounds",
350388
"msm_stratified_bounds",
351389
"pivotality_certificate",
352390
"pomis",
@@ -358,6 +396,7 @@
358396
"run_confounded_chain_benchmark",
359397
"run_episodes",
360398
"run_frontdoor_benchmark",
399+
"split_conformal_interval",
361400
"tipping_gamma",
362401
"transport_estimand",
363402
"transport_formula",

src/causalrl/bounds/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Partial-identification bounds (plan §4 leaf package).
2+
3+
One ``causalrl.bounds`` home that re-exports the shipped nominal-propensity kernels from
4+
:mod:`causalrl.identification.bounds` alongside the continuous / estimated-propensity / heavy-tail
5+
additions in :mod:`causalrl.bounds.continuous`. The shipped module stays the source of truth; this
6+
package adds beside it without moving anything (re-export shim, per plan §4).
7+
"""
8+
9+
from causalrl.bounds.continuous import (
10+
MomentDiagnostic,
11+
certify_mean,
12+
certify_quantile,
13+
certify_sensitivity_bounds,
14+
moment_diagnostic,
15+
msm_sensitivity_bounds,
16+
tail_index_hill,
17+
weighted_quantile,
18+
)
19+
from causalrl.identification.bounds import (
20+
Interval,
21+
causal_q_bounds,
22+
ipw_sensitivity_bounds,
23+
manski_bounds,
24+
msm_contribution_bounds,
25+
msm_per_step_bounds,
26+
msm_policy_value_bounds,
27+
msm_stratified_bounds,
28+
tipping_gamma,
29+
)
30+
31+
__all__ = [
32+
"Interval",
33+
"MomentDiagnostic",
34+
"causal_q_bounds",
35+
"certify_mean",
36+
"certify_quantile",
37+
"certify_sensitivity_bounds",
38+
"ipw_sensitivity_bounds",
39+
"manski_bounds",
40+
"moment_diagnostic",
41+
"msm_contribution_bounds",
42+
"msm_per_step_bounds",
43+
"msm_policy_value_bounds",
44+
"msm_sensitivity_bounds",
45+
"msm_stratified_bounds",
46+
"tail_index_hill",
47+
"tipping_gamma",
48+
"weighted_quantile",
49+
]

0 commit comments

Comments
 (0)