S-, T- and X-learner bootstrap confidence intervals are not reproducible

Aperta
#1,036 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

@su-jin1425 ci sta già lavorando.

Dal 28/8/2026.

  • #1041 di @su-jin1425 — aperta

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
52/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Tranquilla
Stack tecnologico
numpy, python, scikit-learn

Direzione di ricerca

Inizia dalla dipendenza #1035 e da BaseLearner.bootstrap() in base.py:117, quindi esamina i call site degli S-, X- e legacy T-learner elencati in slearner.py, xlearner.py e tlearner.py. Il lavoro è completo quando le classi learner interessate espongono il parametro seeded, gli intervalli bootstrap sono riproducibili indipendentemente dal flusso NumPy globale ed è presente la copertura del round-trip di clone()/get_params.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug

Depends on #1035 — that issue establishes the single check_random_state normalization path this one threads through the S- and X-learner families. Landing this first would mean either duplicating that decision or redoing it.

BaseLearner.bootstrap() (base.py:117) takes an optional rng and falls back to the global numpy stream when it is not supplied (base.py:133-137). #1032 fixed this for the R-learner (#1029). The remaining callers still pass no rng, so on master the R-learner is the only meta-learner whose bootstrap confidence intervals are reproducible:

  • slearner.py:215, :308
  • xlearner.py:305, :422
  • tlearner.py:297, :395 — the legacy loop only. T-learner's fit(store_bootstraps=True) → fit_bootstrap_ensemble() path from #886 is already seeded, and that is what predict(return_ci=True) uses.

DR-learner is unaffected: it threads an explicit per-call seed.

So BaseSRegressor(...).estimate_ate(..., bootstrap_ci=True) returns different intervals on every call, and unrelated consumption of the global numpy stream between calls changes the result. There is currently no way for a caller to pin them.

Note this is not purely a threading fix: grep -c random_state is 0 for both slearner.py and xlearner.py, so they have no random_state parameter to thread. Adding one is a backward-compatible API addition, which is why #1029 scoped itself to the R-learner.

Since #912 these are BaseEstimators, so the new parameter must be stored verbatim in __init__ per the scikit-learn convention, and each affected class wants clone() / get_params round-trip coverage. That is seven classes across two families: BaseSLearner, BaseSRegressor, BaseSClassifier, LRSRegressor (which constructs StatsmodelsOLS for the caller and needs care), plus BaseXLearner, BaseXRegressor, BaseXClassifier.

Suggested order: land #1035 first, then add random_state to BaseSLearner and BaseXLearner and pass rng at the six call sites.

Observed on master (477cd0a).

🤖 Generated with Claude Code

Lingua principale
Python
Stelle
6k
Fork
892
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di uber/causalml

Tutte le issue di uber/causalml

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.