Skip to content

fix(tree): LinearTreeSHAP no longer mutates the caller-owned TreeModel#546

Open
42logos wants to merge 1 commit into
mmschlk:mainfrom
FabianK-Dev:wu/fix-lineartreeshap-deepcopy
Open

fix(tree): LinearTreeSHAP no longer mutates the caller-owned TreeModel#546
42logos wants to merge 1 commit into
mmschlk:mainfrom
FabianK-Dev:wu/fix-lineartreeshap-deepcopy

Conversation

@42logos

@42logos 42logos commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #544.

What was wrong

Constructing a LinearTreeSHAP explainer silently mutated the TreeModel passed to it: validate_tree_model returns TreeModel inputs by reference, and the constructor then called reduce_feature_complexity() on that result, rewriting its features / feature_ids / n_features_in_tree in place. A user inspecting the model afterwards — or reusing it with another explainer — read corrupted feature ids (e.g. the real ids [5, 7] overwritten with internal [0, 1]). See #544 for an end-to-end sklearn reproduction. TreeSHAPIQ already deep-copies before its own reduce_feature_complexity(); LinearTreeSHAP was the inconsistent one.

Fix

Deepcopy the validated tree before reducing — one line in src/shapiq/tree/linear/explainer.py, matching TreeSHAPIQ. The deepcopy is a one-time construction cost on a single TreeModel (a handful of NumPy arrays), negligible next to the N-matrix setup that follows.

Testing

  • New regression test test_linear_tree_shap_does_not_mutate_caller_tree_model in test_tree_bugfix.py: snapshots TreeModel.features, constructs LinearTreeSHAP, asserts the caller's object is untouched and the explainer still works on its private reduced copy. Verified red without the fix, green with it.
  • Full tests_tree_explainer suite passes; ruff check / ruff format clean on the changed files (zero new findings vs main).

validate_tree_model returns TreeModel inputs by reference, and LinearTreeSHAP
immediately called reduce_feature_complexity() on the result — mutating, in
place, a TreeModel the caller may share with other explainers (their feature
ids get remapped under them, silently corrupting subsequent computations).

Deepcopy the validated model before reducing. Minimal reproduction: construct
a TreeModel over data with more features than the tree uses, snapshot
TreeModel.features, construct LinearTreeSHAP(tree_model) — the snapshot
differs without this fix.
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a correctness bug where constructing LinearTreeSHAP could mutate a caller-owned TreeModel in place (via reduce_feature_complexity()), corrupting feature-id metadata for downstream reuse/inspection. The fix aligns LinearTreeSHAP with the existing TreeSHAPIQ behavior by deep-copying the validated TreeModel before any in-place reduction.

Changes:

  • Deep-copy the validated tree in LinearTreeSHAP.__init__ before calling reduce_feature_complexity().
  • Add a regression test ensuring LinearTreeSHAP construction does not mutate the input TreeModel and still produces a finite explanation.
  • Document the bugfix in CHANGELOG.md under an Unreleased section.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/shapiq/tree/linear/explainer.py Prevents in-place mutation of caller-owned TreeModel by deep-copying prior to feature reduction.
tests/shapiq/tests_unit/tests_explainer/tests_tree_explainer/test_tree_bugfix.py Adds regression coverage verifying the input TreeModel is unchanged and the explainer still functions.
CHANGELOG.md Notes the behavioral fix and links the related issue.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

LinearTreeSHAP mutates the caller-owned TreeModel in place (feature ids remapped under other explainers)

2 participants