fix(dml): fail closed when cross-fit learners cannot be isolated - #816
fix(dml): fail closed when cross-fit learners cannot be isolated#816shawcharles wants to merge 3 commits into
Conversation
Context: - Cross-fit reuse after a failed or identity-returning deepcopy can carry fitted state and prior-fold training data into a later fold. - DMLDiD must reject an invalid learner template before any group-time cell is fitted. Changes: - Require a distinct top-level deepcopy for cross-fit learner templates and raise a sanitised TypeError otherwise. - Probe custom DML learner specifications during configuration validation while retaining the direct cross_fit_predict backstop. - Document the top-level clone boundary and cover copy failures, identity copies, sklearn templates, exact OOF isolation, and pre-cell error propagation. Verification: - pytest -q tests/test_crossfit.py tests/test_dml_did.py tests/test_changelog_fragments.py tests/test_docs_ia.py tests/test_doc_deps_integrity.py - Python 3.9 targeted regression tests, including sklearn preflight - ruff, black, scoped mypy, changelog compiler, and native cross-fit output comparison against upstream/main - Not run: full default suite was stopped after an interrupted long-running attempt.
|
The agreed copy-isolation boundary, preflight placement, error sanitisation, documentation surfaces, changelog fragment, and regression cases are now in place. Local validation covers the DML/cross-fit and documentation suites, targeted Python 3.9 execution, static checks, and native built-in output parity. Could you apply |
Context: - Cross-fit learner templates must fail closed when cloning cannot provide an independent, usable learner, without exposing stale DML results after a failed re-fit. Changes: - Validate clone protocol and weighted-fit capability during DML preflight and for each direct cross-fit fold; delayed errors name the public nuisance parameter. - Clear DMLDiD fitted state before every fit attempt, and cover panel, repeated-cross-section, survey, and direct helper failure paths. - Correct the DML tutorial and documentation registry, and record the warning-to-TypeError behavioural change. Verification: - /tmp/diff-diff-814-python39/bin/python -m pytest -q tests/test_crossfit.py tests/test_dml_did.py tests/test_survey_dml.py tests/test_changelog_fragments.py tests/test_docs_ia.py tests/test_doc_deps_integrity.py (665 passed, 6 skipped) - black --check, ruff check, and mypy --follow-imports=skip on touched source/test modules - python -m json.tool docs/tutorials/32_dml_did.ipynb and python .claude/scripts/changelog_compile.py check - Not run: full repository test suite.
|
Pushed follow-up commit bb187a3 to complete the fail-closed contract.
Verification: Python 3.9 scoped suite passed ( |
|
Thanks - this lands the boundary exactly as agreed, and the test coverage is thorough. Fork PRs cannot run the label-gated CI or the CI AI reviewer here, so I opened a mirror of your branch as #817 to get that feedback. The CI review came back clean (no P0/P1) with one P3, and while preparing the mirror I hit one P2-level wording issue. I would like you to make these changes on this PR so the work stays yours; I will refresh the mirror from your branch afterwards. Three items: 1. Narrow the "before any cell" claim to the one-copy preflight (P2). The changelog fragment, 2. Stale fallback-era phrase (P3 from the CI review). The DMLDiD 3. Make the scikit-learn tests actually run in CI. The two new Once those are pushed I will refresh #817, let CI run, and then move this PR forward. |
Context: - PR igerber#816 review identified overbroad pre-cell failure claims and optional sklearn tests that CI never exercised. Changes: - Adapt the documentation corrections from mirror commit 50109fe to distinguish preflight failures from later hard TypeErrors. - Require unfitted templates and distinct copies in both seed descriptions. - Add unpinned scikit-learn to the dev extra and both Rust workflow dependency lists; retain importorskip guards. - Leave estimator execution and the separate StackedDiD sentinel contract unchanged. Verification: - Python 3.12 focused cross-fit, DML, changelog and documentation suites: 582 passed. - Both sklearn integration tests passed without skips on Python 3.9. - Black, Ruff, changelog compiler, TOML/YAML and notebook JSON validation passed. - Known failure: test_sklearn_clone_round_trip_if_available[StackedDiD] fails on sentinel parameter equality with sklearn installed; reproduced on Python 3.9 in both this branch and unchanged base e982340. - Not run: full repository suite, Sphinx build or remote CI matrix.
|
Thanks, pushed 47e98ad with all three requested changes. I used 50109fe as the reference for the preflight/later-fold distinction, replaced both "where copyable" descriptions with the unfitted-template requirement, and added unpinned scikit-learn to the dev extra and both workflow install steps. The importorskip guards remain. Validation: 582 focused tests passed on Python 3.12, and both DML/cross-fit sklearn tests ran without skips on Python 3.9. Black, Ruff, changelog and configuration-format checks passed. One additional CI consequence surfaced during review: installing sklearn enables the existing python -m pytest -q tests/test_base_estimator.py -k 'sklearn_clone and StackedDiD'The requested dependency additions are in place, but this newly enabled failure needs resolving before CI can pass. I left the shared deprecation-sentinel behaviour unchanged to keep this revision within the agreed scope. The full repository suite and remote CI matrix were not run locally. |
|
I have a tested local fix for the newly enabled StackedDiD clone test. The production change is limited to the stateless def __copy__(self) -> "_NotSupplied":
return self
def __deepcopy__(self, memo: Any) -> "_NotSupplied":
return selfThis preserves the omitted-parameter marker during sklearn cloning without changing type-based alias resolution. Tests cover direct copy identity, nested mutable values remaining independently copied, and default/canonical/deprecated StackedDiD configurations with their existing warning behaviour. It does not relax the distinct-object requirement for DML learner templates. The base-estimator, mechanical/semantic rename, cross-fit and DML suites pass: Python 3.9/sklearn 1.6.1: 590 passed, 19 skipped; Python 3.12/sklearn 1.7.2: 596 passed, 13 skipped. The previously failing clone test runs and passes on both. Black, Ruff, scoped mypy and changelog checks also pass; the full suite and remote CI were not run. I recommend adding this as one small follow-up commit here, with regression tests and a changelog fragment, to unblock the dependency change. Would you prefer that or a separate PR? The patch is prepared locally; #816 remains at 47e98ad pending your choice. |
Fixes #814
Summary
deepcopyfails or returns the original object before any group-time cell is fitted.cross_fit_predictcallers.Verification
pytest -q tests/test_crossfit.py tests/test_dml_did.py tests/test_changelog_fragments.py tests/test_docs_ia.py tests/test_doc_deps_integrity.py(576 passed)upstream/main