• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mmschlk / shapiq / 18499875864

14 Oct 2025 02:26PM UTC coverage: 92.799% (-0.7%) from 93.522%
18499875864

push

github

web-flow
Enhance type safety and fix bugs across the codebase (#430)

* First Pyright cleanup

* TypeChecked game

* fixed introduced bugs in game and interaction_values

* Pyright Save Sampling

* TypeSafe Approximator

* Typechecked Datasets

* Explainer folder typechecked

* GameTheory Typechecked

* Imputer Typechecked

* Plot Typechecked

* Added static typechecking to pre-commit

* Refactoring

* Add pyright change to CHANGELOG

* Activate code quality show diff

* changed uv sync in pre-commit hook

* made fixtures local import

* Introduced Generic TypeVar in Approximator, reducing ignores

* Introduced Generic Types for Explainer. Approximator, Imputer and ExactComputer can either exist or not, depending on dynamic Type

* Bug fix caused through refactoring

* updated overrides

* tightened CoalitionMatrix to accept only bool arrays

* Remove Python reinstallation step in CI workflow

Removed the step to reinstall Python on Windows due to issues with tkinter. The linked GitHub issue was solved. Doing this as a first try.

* Add Python reinstallation and Tkinter installation steps

Reinstall Python and install Tkinter for Windows tests. prior commit did not help

* Fix command for installing Tkinter in workflow

* Update Windows workflow to install Tkinter via Chocolatey

* Remove Tkinter installation step from Windows workflow and adjust matplotlib usage for headless environments

* adapted some pyright types

* removed generics from explainer again

* tightened index type check

* made n_players None at assignment again

* moved comments

---------

Co-authored-by: Maximilian <maximilian.muschalik@gmail.com>

304 of 360 new or added lines in 51 files covered. (84.44%)

12 existing lines in 9 files now uncovered.

4987 of 5374 relevant lines covered (92.8%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.75
/src/shapiq/explainer/configuration.py
1
"""Configuration for the ``shapiq`` explainers."""
2

3
from __future__ import annotations
1✔
4

5
from typing import Literal
1✔
6

7
from shapiq import (
1✔
8
    SHAPIQ,
9
    SPEX,
10
    SVARM,
11
    SVARMIQ,
12
    KernelSHAP,
13
    KernelSHAPIQ,
14
    PermutationSamplingSII,
15
    PermutationSamplingSTII,
16
    PermutationSamplingSV,
17
    RegressionFBII,
18
    RegressionFSII,
19
    UnbiasedKernelSHAP,
20
    kADDSHAP,
21
)
22
from shapiq.approximator.base import Approximator, ValidApproximationIndices
1✔
23
from shapiq.approximator.regression.base import Regression
1✔
24
from shapiq.game_theory.indices import index_generalizes_bv, index_generalizes_sv
1✔
25

26
ValidApproximatorTypes = Literal["spex", "montecarlo", "svarm", "permutation", "regression"]
1✔
27
APPROXIMATOR_CONFIGURATIONS: dict[
1✔
28
    ValidApproximatorTypes, dict[ValidApproximationIndices, type[Approximator]]
29
] = {
30
    "regression": {
31
        "SII": KernelSHAPIQ,
32
        "FSII": RegressionFSII,
33
        "FBII": RegressionFBII,
34
        "k-SII": KernelSHAPIQ,
35
        "SV": KernelSHAP,
36
        "BV": RegressionFBII,
37
        "kADD-SHAP": kADDSHAP,
38
    },
39
    "permutation": {
40
        "SII": PermutationSamplingSII,
41
        "STII": PermutationSamplingSTII,
42
        "k-SII": PermutationSamplingSII,
43
        "SV": PermutationSamplingSV,
44
    },
45
    "montecarlo": {
46
        "SII": SHAPIQ,
47
        "STII": SHAPIQ,
48
        "FSII": SHAPIQ,
49
        "FBII": SHAPIQ,
50
        "k-SII": SHAPIQ,
51
        "SV": UnbiasedKernelSHAP,
52
        "BV": SHAPIQ,
53
        "BII": SHAPIQ,
54
        "CHII": SHAPIQ,
55
    },
56
    "svarm": {
57
        "SII": SVARMIQ,
58
        "STII": SVARMIQ,
59
        "FSII": SVARMIQ,
60
        "FBII": SVARMIQ,
61
        "k-SII": SVARMIQ,
62
        "SV": SVARM,
63
        "BV": SVARM,
64
        "BII": SVARMIQ,
65
        "CHII": SVARMIQ,
66
    },
67
    "spex": {
68
        "SII": SPEX,
69
        "STII": SPEX,
70
        "FSII": SPEX,
71
        "FBII": SPEX,
72
        "k-SII": SPEX,
73
        "SV": SPEX,
74
        "BV": SPEX,
75
    },
76
}
77

78

79
def choose_spex(max_order: int, n_players: int) -> bool:
1✔
80
    """Decide whether to use SPEX based on the number of players and max order.
81

82
    Args:
83
        max_order: The maximum order of interactions to be computed.
84
        n_players: The number of players in the game.
85

86
    Returns:
87
        True if SPEX should be used, False otherwise.
88
    """
89
    if max_order == 2 and n_players > 64:
1✔
90
        return True
1✔
91
    if max_order == 3 and n_players > 32:
1✔
92
        return True
1✔
93
    if max_order == 4 and n_players > 16:
1✔
94
        return True
1✔
95
    return bool(max_order > 4 and n_players > 16)
1✔
96

97

98
def setup_approximator_automatically(
1✔
99
    index: ValidApproximationIndices,
100
    max_order: int,
101
    n_players: int,
102
    random_state: int | None = None,
103
) -> Approximator:
104
    """Select the approximator automatically based on the index and max_order.
105

106
    Args:
107
        index: The index to be used for the approximator.
108
        max_order: The maximum order of interactions to be computed.
109
        n_players: The number of players in the game.
110
        random_state: The random state to initialize the approximator with.
111

112
    Returns:
113
        The selected approximator.
114
    """
115
    if choose_spex(max_order=max_order, n_players=n_players) and index in SPEX.valid_indices:
1✔
NEW
116
        return SPEX(
×
117
            n=n_players,
118
            max_order=max_order,
119
            index=index,
120
            random_state=random_state,
121
        )
122
    if index == "SV" or (max_order == 1 and (index == "SV" or index_generalizes_sv(index))):
1✔
123
        return KernelSHAP(n=n_players, random_state=random_state)
1✔
124
    if index == "BV" or (max_order == 1 and (index == "BV" or index_generalizes_bv(index))):
1✔
125
        return RegressionFBII(n=n_players, max_order=1, random_state=random_state)
1✔
126
    if index == "FSII":
1✔
127
        return RegressionFSII(n=n_players, max_order=max_order, random_state=random_state)
1✔
128
    if index == "FBII":
1✔
129
        return RegressionFBII(n=n_players, max_order=max_order, random_state=random_state)
1✔
130
    if index in KernelSHAPIQ.valid_indices:
1✔
131
        return KernelSHAPIQ(
1✔
132
            n=n_players,
133
            max_order=max_order,
134
            index=index,
135
            random_state=random_state,
136
        )
137
    if index in SVARMIQ.valid_indices:
1✔
138
        return SVARMIQ(
1✔
139
            n=n_players,
140
            max_order=max_order,
141
            top_order=False,
142
            random_state=random_state,
143
            index=index,
144
        )
NEW
145
    msg = f"Could not set up approximator automatically for index {index}."
×
NEW
146
    raise ValueError(msg)
×
147

148

149
def setup_approximator(
1✔
150
    approximator: ValidApproximatorTypes | Approximator | Literal["auto"],
151
    index: ValidApproximationIndices,
152
    max_order: int,
153
    n_players: int,
154
    random_state: int | None = None,
155
) -> Approximator:
156
    """Set up the approximator for the explainer based on the selected index and order.
157

158
    Args:
159
        approximator: The approximator to be used. If ``"auto"``, the approximator is selected based
160
            on the index and max_order.
161
        index: The index to be used for the approximator.
162
        max_order: The maximum order of interactions to be computed.
163
        n_players: The number of players in the game.
164
        random_state: The random state to initialize the approximator with.
165

166
    Returns:
167
        The initialized approximator.
168
    """
169
    # we simply return the approximator if it is already an instance of Approximator
170
    if isinstance(approximator, Approximator):
1✔
171
        return approximator
1✔
172
    if approximator == "auto":  # if the approximator is "auto", we set it up automatically
1✔
173
        return setup_approximator_automatically(
1✔
174
            index=index,
175
            max_order=max_order,
176
            n_players=n_players,
177
            random_state=random_state,
178
        )
179
    if isinstance(approximator, str):
1✔
180
        # if the approx is a string and not "auto", we get it from the configurations and set it up
181
        if approximator in APPROXIMATOR_CONFIGURATIONS:
1✔
182
            approximator_cls: type[Approximator] = APPROXIMATOR_CONFIGURATIONS[approximator][index]
1✔
183
        else:
184
            msg = (
1✔
185
                f"Invalid approximator `{approximator}`. "
186
                f"Valid configurations are described in {APPROXIMATOR_CONFIGURATIONS}."
187
            )
188
            raise ValueError(msg)
1✔
189
    else:
190
        msg = f"Invalid approximator `{approximator}`. "
1✔
191
        raise TypeError(msg)
1✔
192

193
    # initialize the approximator class with params
194
    if issubclass(approximator_cls, Regression):
1✔
195
        return approximator_cls(
1✔
196
            n=n_players,
197
            max_order=max_order,
198
            random_state=random_state,
199
            index=index,
200
        )
201
    return approximator_cls(
1✔
202
        n=n_players,
203
        max_order=max_order,
204
        random_state=random_state,
205
        index=index,
206
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc