Skip to content

Switch public uplift trees to the kernel backend, delete uplift.pyx (#955) - #973

Merged
jeongyoonlee merged 1 commit into
masterfrom
feature/955-uplift-switchover
Jul 26, 2026
Merged

Switch public uplift trees to the kernel backend, delete uplift.pyx (#955)#973
jeongyoonlee merged 1 commit into
masterfrom
feature/955-uplift-switchover

Conversation

@jeongyoonlee

Copy link
Copy Markdown
Collaborator

What

Final issue of the tree-unification epic (#945): flip the public UpliftTreeClassifier / UpliftRandomForestClassifier to thin subclasses of the kernel-backed _KernelUpliftTreeClassifier / _KernelUpliftRandomForestClassifier (grown on the shared _tree Cython kernel), and delete the ~2.7 KLoC legacy uplift.pyx duplicate plus its import-time save/load monkey-patch. Closes #955.

Builds on the merged prereq #972 (native NaN support), #971 (#954 serialization + BaseEstimator conformance), and the rest of the epic (#946#953).

Backward compatibility (names / args / defaults / predict semantics preserved)

  • evaluationFunction still selects the criterion (KL/ED/Chi/CTS/DDP/IT/CIT/IDDP); mapped internally to the kernel criterion.
  • Tree .predict(X) returns per-group P(Y=1|T=g) including control (shape (n, n_groups)), matching the historical return; absent-group leaf rates are zero-filled (uplift p_value is NAN #585).
  • .fill(), .uplift_forest, .fitted_uplift_tree, and forest .predict(full_output=...) are all preserved.
  • early_stopping_eval_diff_scale and fit's X_val / treatment_val / y_val are accepted for backward compatibility but ignored (the kernel has no validation-set early stopping) — a UserWarning is emitted when validation arrays are passed.
  • DecisionTree stays importable via a DeprecationWarning shim.

Necessary behavior shims on the public subclasses

  • feature_importances_ exposed non-negative + renormalized (the kernel's raw importances are signed, since an uplift split gain is not a monotone impurity decrease).
  • int max_features clamped to n_features (legacy behavior; the kernel tree validates 0 < max_features <= n_features strictly) (shape mismatch when calling UpliftRandomForestClassifier.predict() #569).
  • IDDP forces the honest approach at construction (legacy behavior).

Removed

  • causalml/inference/tree/uplift.pyx (2742 lines) + its setup.py build entry + the tree/__init__.py monkey-patch.
  • The legacy-vs-kernel parity unit tests and the uplift_tree_kernel_parity example notebook — obsolete now that the public classes are the kernel classes. tests/test_uplift_trees_kernel.py retains the kernel behavioral tests (criteria, regularization / normalization / honesty effects, pruning, plot, serialization, missing-value).

Verification

  • tests/test_uplift_trees.py — 27 passed (acceptance; the one nodeSummary assertion migrated to the kernel's per-node group counts per the epic plan).
  • tests/test_serialization_extended.py — passed.
  • tests/test_uplift_trees_kernel.py — 48 passed (trimmed to kernel behavior).
  • tests/test_causal_trees.py — passed (regression).
  • black clean.

Notes

Part of #945.

…955)

Flip the public UpliftTreeClassifier / UpliftRandomForestClassifier to thin
subclasses of the kernel-backed _KernelUpliftTreeClassifier /
_KernelUpliftRandomForestClassifier (grown on the shared _tree Cython kernel),
and delete the ~2.7 KLoC legacy uplift.pyx duplicate plus its import-time
save/load monkey-patch. Final issue of the tree-unification epic #945.

Legacy constructor names / defaults / predict semantics are preserved:
evaluationFunction still selects the criterion; the tree's predict returns
per-group P(Y=1|T=g) (absent-group leaf rates zero-filled, #585); fill,
uplift_forest, and fitted_uplift_tree remain. early_stopping_eval_diff_scale
and fit's X_val / treatment_val / y_val are accepted but ignored (the kernel
has no validation-set early stopping). DecisionTree stays importable via a
DeprecationWarning shim.

Compatibility shims on the public subclasses:
- feature_importances_ exposed non-negative (kernel split gains are signed)
- int max_features clamped to n_features (#569)
- IDDP forces the honest approach at construction (legacy behavior)

Tests: tests/test_uplift_trees.py and tests/test_serialization_extended.py
pass against the new impl; the legacy-vs-kernel parity tests and the parity
notebook are removed (the public classes now ARE the kernel classes), and the
remaining tests/test_uplift_trees_kernel.py cases cover kernel behavior.
@jeongyoonlee
jeongyoonlee marked this pull request as ready for review July 26, 2026 00:23
@jeongyoonlee jeongyoonlee added the refactoring Code refactoring label Jul 26, 2026
@jeongyoonlee
jeongyoonlee merged commit 31ec3ea into master Jul 26, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactoring Code refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Tree unification 9/9] Switchover + delete legacy uplift.pyx + deprecations

1 participant