Skip to content

Commit 5095ad3

Browse files
committed
Make Captum interpretation optional
- Move Captum-backed method resolution behind lazy imports - Keep interpretation method validation independent of Captum installation - Add optional interpret dependency group for Captum support - Update installation docs for core, interpret, and AnnData extras - Preserve core package imports without requiring Captum
1 parent 06cda7d commit 5095ad3

7 files changed

Lines changed: 213 additions & 103 deletions

File tree

docs/installation.md

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,38 @@
22

33
## Install from PyPI
44

5-
Install `edge2torch` from PyPI with:
5+
Install the core `edge2torch` package from PyPI with:
66

77
```bash
88
pip install edge2torch
99
```
1010

11-
For optional `AnnData` support:
11+
The core installation supports compiling sparse PyTorch models from edgelists,
12+
aligning named input features, customizing compiled models, and training with
13+
ordinary PyTorch.
14+
15+
## Optional interpretation support
16+
17+
`interpret_model()` uses Captum and is installed as an optional dependency.
18+
19+
To install `edge2torch` with interpretation support:
20+
21+
```bash
22+
pip install "edge2torch[interpret]"
23+
```
24+
25+
## Optional AnnData support
26+
27+
For optional `AnnData` input support:
28+
29+
```bash
30+
pip install "edge2torch[anndata]"
31+
```
32+
33+
To install both interpretation and `AnnData` support:
1234

1335
```bash
14-
pip install "edge2torch[bio]"
36+
pip install "edge2torch[interpret,anndata]"
1537
```
1638

1739
## Development installation
@@ -25,24 +47,36 @@ cd edge2torch
2547
pip install -e .
2648
```
2749

50+
For optional interpretation support during development:
51+
52+
```bash
53+
pip install -e ".[interpret]"
54+
```
55+
2856
For optional `AnnData` support during development:
2957

3058
```bash
31-
pip install -e .[bio]
59+
pip install -e ".[anndata]"
60+
```
61+
62+
For both optional interpretation and `AnnData` support:
63+
64+
```bash
65+
pip install -e ".[interpret,anndata]"
3266
```
3367

3468
## Optional dependency groups
3569

3670
Install development dependencies with:
3771

3872
```bash
39-
pip install -e .[dev]
73+
pip install -e ".[dev]"
4074
```
4175

4276
Install documentation dependencies with:
4377

4478
```bash
45-
pip install -e .[docs]
79+
pip install -e ".[docs]"
4680
```
4781

4882
## Notebook and documentation note
@@ -59,8 +93,14 @@ sudo apt install graphviz
5993

6094
## Verify the installation
6195

62-
A minimal smoke test is:
96+
A minimal core-installation smoke test is:
6397

6498
```bash
6599
python -c "import edge2torch; print('edge2torch imported successfully')"
66100
```
101+
102+
To verify interpretation support, install `edge2torch[interpret]` and run:
103+
104+
```bash
105+
python -c "from edge2torch import interpret_model; print(interpret_model)"
106+
```

pyproject.toml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ authors = [
1515
dependencies = [
1616
"pandas",
1717
"torch",
18-
"captum",
1918
]
2019
classifiers = [
2120
"Development Status :: 3 - Alpha",
@@ -35,7 +34,14 @@ Repository = "https://github.com/Thomas-Rauter/edge2torch"
3534
Issues = "https://github.com/Thomas-Rauter/edge2torch/issues"
3635

3736
[project.optional-dependencies]
38-
bio = [
37+
all = [
38+
"captum",
39+
"anndata",
40+
]
41+
interpret = [
42+
"captum",
43+
]
44+
anndata = [
3945
"anndata",
4046
]
4147
dev = [
@@ -44,6 +50,7 @@ dev = [
4450
"ruff",
4551
"mypy",
4652
"build",
53+
"captum",
4754
"anndata",
4855
]
4956
docs = [
@@ -59,6 +66,8 @@ docs = [
5966
"IPython",
6067
"seaborn",
6168
"black",
69+
"captum",
70+
"anndata",
6271
]
6372

6473
[tool.setuptools]
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
Lazy Captum class resolution for optional interpretation support.
3+
4+
Why this file exists
5+
--------------------
6+
Captum is an optional dependency of edge2torch. This module imports Captum
7+
classes only when interpretation is actually executed.
8+
9+
Role in the package
10+
-------------------
11+
This is an internal dependency bridge. It maps validated Captum method names to
12+
Captum attribution classes. It should not validate public inputs, execute
13+
attribution, prepare input data, or map attribution results.
14+
"""
15+
16+
from typing import Any
17+
18+
from ..utils.errors import Edge2TorchError
19+
20+
21+
def get_captum_class(method: str) -> type[Any]:
22+
"""
23+
Return the Captum attribution class for a supported method name.
24+
"""
25+
try:
26+
from captum.attr import (
27+
LRP,
28+
Deconvolution,
29+
DeepLift,
30+
DeepLiftShap,
31+
FeatureAblation,
32+
FeaturePermutation,
33+
GradientShap,
34+
GuidedBackprop,
35+
InputXGradient,
36+
IntegratedGradients,
37+
InternalInfluence,
38+
KernelShap,
39+
LayerActivation,
40+
LayerConductance,
41+
LayerDeepLift,
42+
LayerDeepLiftShap,
43+
LayerFeatureAblation,
44+
LayerFeaturePermutation,
45+
LayerGradientShap,
46+
LayerGradientXActivation,
47+
LayerIntegratedGradients,
48+
LayerLRP,
49+
Lime,
50+
Occlusion,
51+
Saliency,
52+
ShapleyValues,
53+
ShapleyValueSampling,
54+
)
55+
except ImportError as exc:
56+
raise Edge2TorchError(
57+
"interpret_model() requires Captum, which is an optional "
58+
"dependency. Install interpretation support with "
59+
"'pip install \"edge2torch[interpret]\"'."
60+
) from exc
61+
62+
classes: dict[str, type[Any]] = {
63+
"IntegratedGradients": IntegratedGradients,
64+
"DeepLift": DeepLift,
65+
"DeepLiftShap": DeepLiftShap,
66+
"GradientShap": GradientShap,
67+
"Saliency": Saliency,
68+
"InputXGradient": InputXGradient,
69+
"GuidedBackprop": GuidedBackprop,
70+
"Deconvolution": Deconvolution,
71+
"FeatureAblation": FeatureAblation,
72+
"Occlusion": Occlusion,
73+
"FeaturePermutation": FeaturePermutation,
74+
"ShapleyValueSampling": ShapleyValueSampling,
75+
"ShapleyValues": ShapleyValues,
76+
"Lime": Lime,
77+
"KernelShap": KernelShap,
78+
"LRP": LRP,
79+
"LayerActivation": LayerActivation,
80+
"LayerConductance": LayerConductance,
81+
"InternalInfluence": InternalInfluence,
82+
"LayerGradientXActivation": LayerGradientXActivation,
83+
"LayerDeepLift": LayerDeepLift,
84+
"LayerDeepLiftShap": LayerDeepLiftShap,
85+
"LayerGradientShap": LayerGradientShap,
86+
"LayerIntegratedGradients": LayerIntegratedGradients,
87+
"LayerFeatureAblation": LayerFeatureAblation,
88+
"LayerFeaturePermutation": LayerFeaturePermutation,
89+
"LayerLRP": LayerLRP,
90+
}
91+
92+
return classes[method]

src/edge2torch/interpret/feature_attribution.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@
2323

2424
from ..compile.artifact import CompileArtifact
2525
from ..utils.errors import Edge2TorchError
26+
from .captum_classes import get_captum_class
2627
from .method_registry import (
27-
FEATURE_INTERPRETERS_WITH_CONSTRUCTOR_KWARGS,
28-
FEATURE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS,
28+
FEATURE_METHODS_WITH_CONSTRUCTOR_KWARGS,
29+
FEATURE_METHODS_WITHOUT_CONSTRUCTOR_KWARGS,
2930
)
3031

3132
# Level 2 functions (called by level 1 functions) ------------------------------
@@ -95,17 +96,15 @@ def _build_feature_interpreter(
9596
"""
9697
Build a Captum interpreter for feature-level attribution.
9798
"""
98-
if method in FEATURE_INTERPRETERS_WITH_CONSTRUCTOR_KWARGS:
99-
interpreter_class = FEATURE_INTERPRETERS_WITH_CONSTRUCTOR_KWARGS[method]
99+
if method in FEATURE_METHODS_WITH_CONSTRUCTOR_KWARGS:
100+
interpreter_class = get_captum_class(method)
100101
return interpreter_class(
101102
model,
102103
**constructor_kwargs,
103104
)
104105

105-
if method in FEATURE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS:
106-
interpreter_class = FEATURE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS[
107-
method
108-
]
106+
if method in FEATURE_METHODS_WITHOUT_CONSTRUCTOR_KWARGS:
107+
interpreter_class = get_captum_class(method)
109108
return interpreter_class(model)
110109

111110
raise Edge2TorchError(

src/edge2torch/interpret/feedforward_node_attribution.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525
from ..compile.artifact import CompileArtifact
2626
from ..utils.constants import INTERNAL_NODE_PREFIX
2727
from ..utils.errors import Edge2TorchError
28+
from .captum_classes import get_captum_class
2829
from .method_registry import (
29-
FEEDFORWARD_NODE_INTERPRETERS_WITH_CONSTRUCTOR_KWARGS,
30-
FEEDFORWARD_NODE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS,
30+
FEEDFORWARD_NODE_METHODS_WITH_CONSTRUCTOR_KWARGS,
31+
FEEDFORWARD_NODE_METHODS_WITHOUT_CONSTRUCTOR_KWARGS,
3132
)
3233

3334
# Level 3 functions (called by level 2 functions) ------------------------------
@@ -113,20 +114,16 @@ def _build_feedforward_layer_interpreter(
113114
"""
114115
Build a Captum interpreter for feedforward layer-level attribution.
115116
"""
116-
if method in FEEDFORWARD_NODE_INTERPRETERS_WITH_CONSTRUCTOR_KWARGS:
117-
interpreter_class = (
118-
FEEDFORWARD_NODE_INTERPRETERS_WITH_CONSTRUCTOR_KWARGS[method]
119-
)
117+
if method in FEEDFORWARD_NODE_METHODS_WITH_CONSTRUCTOR_KWARGS:
118+
interpreter_class = get_captum_class(method)
120119
return interpreter_class(
121120
model,
122121
layer_block,
123122
**constructor_kwargs,
124123
)
125124

126-
if method in FEEDFORWARD_NODE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS:
127-
interpreter_class = (
128-
FEEDFORWARD_NODE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS[method]
129-
)
125+
if method in FEEDFORWARD_NODE_METHODS_WITHOUT_CONSTRUCTOR_KWARGS:
126+
interpreter_class = get_captum_class(method)
130127
return interpreter_class(
131128
model,
132129
layer_block,

src/edge2torch/interpret/input_validation.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929

3030
from ..utils.errors import Edge2TorchError
3131
from .method_registry import (
32-
FEATURE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS,
3332
FEATURE_METHODS,
34-
FEEDFORWARD_NODE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS,
33+
FEATURE_METHODS_WITHOUT_CONSTRUCTOR_KWARGS,
3534
FEEDFORWARD_NODE_METHODS,
35+
FEEDFORWARD_NODE_METHODS_WITHOUT_CONSTRUCTOR_KWARGS,
3636
SUPPORTED_METHODS,
3737
)
3838

@@ -155,7 +155,7 @@ def _validate_interpret_options(
155155

156156
if (
157157
target == "features"
158-
and method in FEATURE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS
158+
and method in FEATURE_METHODS_WITHOUT_CONSTRUCTOR_KWARGS
159159
and constructor_kwargs
160160
):
161161
raise Edge2TorchError(
@@ -165,7 +165,7 @@ def _validate_interpret_options(
165165

166166
if (
167167
target == "nodes"
168-
and method in FEEDFORWARD_NODE_INTERPRETERS_WITHOUT_CONSTRUCTOR_KWARGS
168+
and method in FEEDFORWARD_NODE_METHODS_WITHOUT_CONSTRUCTOR_KWARGS
169169
and constructor_kwargs
170170
):
171171
raise Edge2TorchError(

0 commit comments

Comments
 (0)