Add interpretability SHAP C API#12224
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a dedicated interpretability-focused C API entry point for computing SHAP values, and updates the Python interpretability API to use it. The goal is to provide a stable C-level interface with separate output buffers for feature attributions and the bias term, while reserving a background DMatrixHandle for future interventional SHAP support.
Changes:
- Added new C API function
XGBoosterInterpretShapValueswith JSON configuration and split outputs (values,bias). - Implemented the C API by reusing the existing contribution prediction path and separating the last “bias” column into its own output buffer.
- Updated Python
xgboost.interpret.shap_valuesand added C++/Python tests to validate behavior and expected error handling for unsupported interventional SHAP.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| include/xgboost/c_api.h | Declares and documents the new public C API function for SHAP values. |
| src/c_api/c_api.cc | Implements XGBoosterInterpretShapValues by splitting contribution predictions into values + bias. |
| src/common/api_entry.h | Extends thread-local return buffers to support multiple float/shape outputs. |
| python-package/xgboost/interpret.py | Routes Python shap_values through the new C API and returns split outputs. |
| tests/cpp/c_api/test_c_api.cc | Adds a C++ C-API test validating shapes/contents vs contribution prediction and unsupported paths. |
| tests/python/test_interpret.py | Updates expected exception type/message when passing background data (now errors from C API). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| auto *ctx = learner->Ctx(); | ||
| auto out_ctx = ctx->IsCUDA() ? *ctx : ctx->MakeCPU(); | ||
| auto &values = local.ret_hdv_float; | ||
| auto &bias = local.ret_hdv_float_1; |
There was a problem hiding this comment.
(Interactive) SHAP value has large return buffers. If we could pre-allocate memory in Python then we could avoid drawing memory from thread local, which is a global variable and cannot be easily freed.
|
This needs more work. I need to figure out how to route internally without going through learner->predict. |
Summary
This adds a dedicated C API entry point for SHAP values:
XGBoosterInterpretShapValuesDMatrixHandlereserved for interventional SHAPxgboost.interpret.shap_valuesnow routes through the new C APIThe initial implementation supports the existing tree-path-dependent SHAP behavior by reusing the current contribution prediction path and splitting the final bias column into a separate output. Interventional SHAP is not implemented yet, but the C API shape now includes the background handle needed for it.
Testing
pre-commit run --files include/xgboost/c_api.h src/c_api/c_api.cc src/common/api_entry.h tests/cpp/c_api/test_c_api.cc python-package/xgboost/interpret.py tests/python/test_interpret.pybuild-cpu/testxgboost --gtest_filter=CAPI.InterpretShapValuesPYTHONPATH=/home/nfs/rorym/xgboost-wt/interpret-capi/python-package LD_LIBRARY_PATH=/home/nfs/rorym/xgboost-wt/interpret-capi/lib conda run -n xgboost python -m pytest tests/python/test_interpret.py