shapiq 1.2.2__tar.gz → 1.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. {shapiq-1.2.2 → shapiq-1.2.3}/CHANGELOG.md +11 -0
  2. {shapiq-1.2.2/shapiq.egg-info → shapiq-1.2.3}/PKG-INFO +41 -69
  3. {shapiq-1.2.2 → shapiq-1.2.3}/README.md +2 -0
  4. shapiq-1.2.3/pyproject.toml +121 -0
  5. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/__init__.py +3 -1
  6. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/__init__.py +15 -1
  7. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/_base.py +31 -97
  8. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/marginals/owen.py +33 -9
  9. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/marginals/stratified.py +15 -4
  10. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/montecarlo/_base.py +35 -8
  11. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/permutation/sii.py +13 -3
  12. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/permutation/stii.py +49 -12
  13. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/permutation/sv.py +27 -5
  14. shapiq-1.2.3/shapiq/approximator/regression/__init__.py +17 -0
  15. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/regression/_base.py +162 -124
  16. shapiq-1.2.2/shapiq/approximator/regression/fsi.py → shapiq-1.2.3/shapiq/approximator/regression/faithful.py +46 -0
  17. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/sampling.py +5 -2
  18. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/benchmark/load.py +1 -2
  19. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/datasets/_all.py +3 -6
  20. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/_base.py +0 -2
  21. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tabular.py +15 -2
  22. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/base.py +21 -0
  23. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/conversion/xgboost.py +2 -6
  24. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/explainer.py +12 -2
  25. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/treeshapiq.py +46 -20
  26. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/validation.py +2 -2
  27. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/game_theory/__init__.py +2 -2
  28. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/game_theory/aggregation.py +13 -10
  29. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/game_theory/exact.py +19 -15
  30. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/game_theory/indices.py +4 -4
  31. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/game_theory/moebius_converter.py +2 -2
  32. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/base.py +2 -2
  33. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/__init__.py +48 -32
  34. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/ensemble_selection/__init__.py +7 -5
  35. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/local_xai/base.py +0 -1
  36. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/synthetic/__init__.py +2 -1
  37. shapiq-1.2.3/shapiq/games/benchmark/synthetic/random.py +33 -0
  38. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/imputer/conditional_imputer.py +4 -1
  39. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/imputer/marginal_imputer.py +1 -11
  40. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/interaction_values.py +72 -10
  41. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/utils/modules.py +9 -13
  42. {shapiq-1.2.2 → shapiq-1.2.3/shapiq.egg-info}/PKG-INFO +41 -69
  43. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq.egg-info/SOURCES.txt +2 -5
  44. shapiq-1.2.3/shapiq.egg-info/requires.txt +23 -0
  45. {shapiq-1.2.2 → shapiq-1.2.3}/tests/test_base_interaction_values.py +16 -5
  46. shapiq-1.2.2/MANIFEST.in +0 -3
  47. shapiq-1.2.2/pyproject.toml +0 -16
  48. shapiq-1.2.2/setup.py +0 -110
  49. shapiq-1.2.2/shapiq/approximator/regression/__init__.py +0 -8
  50. shapiq-1.2.2/shapiq.egg-info/requires.txt +0 -53
  51. shapiq-1.2.2/shapiq.egg-info/zip-safe +0 -1
  52. shapiq-1.2.2/tests/test_abstract_classes.py +0 -64
  53. {shapiq-1.2.2 → shapiq-1.2.3}/LICENSE +0 -0
  54. {shapiq-1.2.2 → shapiq-1.2.3}/setup.cfg +0 -0
  55. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/marginals/__init__.py +0 -0
  56. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/montecarlo/__init__.py +0 -0
  57. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/montecarlo/shapiq.py +0 -0
  58. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/montecarlo/svarmiq.py +0 -0
  59. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/permutation/__init__.py +0 -0
  60. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/regression/kadd_shap.py +0 -0
  61. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/regression/kernelshap.py +0 -0
  62. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/approximator/regression/kernelshapiq.py +0 -0
  63. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/benchmark/__init__.py +0 -0
  64. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/benchmark/configuration.py +0 -0
  65. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/benchmark/metrics.py +0 -0
  66. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/benchmark/plot.py +0 -0
  67. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/benchmark/precompute.py +0 -0
  68. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/benchmark/run.py +0 -0
  69. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/datasets/__init__.py +0 -0
  70. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/__init__.py +0 -0
  71. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tabpfn.py +0 -0
  72. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/__init__.py +0 -0
  73. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/conversion/__init__.py +0 -0
  74. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/conversion/edges.py +0 -0
  75. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/conversion/lightgbm.py +0 -0
  76. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/conversion/sklearn.py +0 -0
  77. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/tree/utils.py +0 -0
  78. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/explainer/utils.py +0 -0
  79. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/game_theory/core.py +0 -0
  80. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/__init__.py +0 -0
  81. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/_setup/__init__.py +0 -0
  82. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/_setup/_california_torch_setup.py +0 -0
  83. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/_setup/_resnet_setup.py +0 -0
  84. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/_setup/_vit_setup.py +0 -0
  85. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/data_valuation/__init__.py +0 -0
  86. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/data_valuation/base.py +0 -0
  87. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/data_valuation/benchmark.py +0 -0
  88. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/dataset_valuation/__init__.py +0 -0
  89. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/dataset_valuation/base.py +0 -0
  90. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/dataset_valuation/benchmark.py +0 -0
  91. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/ensemble_selection/base.py +0 -0
  92. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/ensemble_selection/benchmark.py +0 -0
  93. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/ensemble_selection/benchmark_random_forest.py +0 -0
  94. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/feature_selection/__init__.py +0 -0
  95. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/feature_selection/base.py +0 -0
  96. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/feature_selection/benchmark.py +0 -0
  97. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/global_xai/__init__.py +0 -0
  98. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/global_xai/base.py +0 -0
  99. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/global_xai/benchmark_tabular.py +0 -0
  100. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/local_xai/__init__.py +0 -0
  101. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/local_xai/benchmark_image.py +0 -0
  102. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/local_xai/benchmark_language.py +0 -0
  103. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/local_xai/benchmark_tabular.py +0 -0
  104. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/setup.py +0 -0
  105. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/synthetic/dummy.py +0 -0
  106. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/synthetic/soum.py +0 -0
  107. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/treeshapiq_xai/__init__.py +0 -0
  108. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/treeshapiq_xai/base.py +0 -0
  109. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/treeshapiq_xai/benchmark.py +0 -0
  110. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/uncertainty/__init__.py +0 -0
  111. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/uncertainty/base.py +0 -0
  112. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/uncertainty/benchmark.py +0 -0
  113. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/unsupervised_cluster/__init__.py +0 -0
  114. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/unsupervised_cluster/base.py +0 -0
  115. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/unsupervised_cluster/benchmark.py +0 -0
  116. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/unsupervised_data/__init__.py +0 -0
  117. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/unsupervised_data/base.py +0 -0
  118. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/benchmark/unsupervised_data/benchmark.py +0 -0
  119. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/imputer/__init__.py +0 -0
  120. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/imputer/base.py +0 -0
  121. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/imputer/baseline_imputer.py +0 -0
  122. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/games/imputer/tabpfn_imputer.py +0 -0
  123. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/__init__.py +0 -0
  124. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/_config.py +0 -0
  125. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/bar.py +0 -0
  126. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/force.py +0 -0
  127. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/network.py +0 -0
  128. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/sentence.py +0 -0
  129. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/si_graph.py +0 -0
  130. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/stacked_bar.py +0 -0
  131. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/upset.py +0 -0
  132. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/utils.py +0 -0
  133. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/plot/watefall.py +0 -0
  134. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/utils/__init__.py +0 -0
  135. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/utils/datasets.py +0 -0
  136. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/utils/sets.py +0 -0
  137. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq/utils/types.py +0 -0
  138. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq.egg-info/dependency_links.txt +0 -0
  139. {shapiq-1.2.2 → shapiq-1.2.3}/shapiq.egg-info/top_level.txt +0 -0
  140. {shapiq-1.2.2 → shapiq-1.2.3}/tests/test_configuration.py +0 -0
  141. {shapiq-1.2.2 → shapiq-1.2.3}/tests/test_integration_import_all.py +0 -0
@@ -2,6 +2,17 @@
2
2
 
3
3
  ### Development
4
4
 
5
+ ### v1.2.3 (2025-03-24)
6
+ - substantially improves the runtime of all `Regression` approximators by a) a faster pre-computation of the regression matrices and b) a faster computation of the weighted least squares regression [#340](https://github.com/mmschlk/shapiq/issues/340)
7
+ - removes `sample_replacements` parameter from `MarginalImputer` and removes the DeprecationWarning for it
8
+ - adds a trivial computation to `TreeSHAP-IQ` for trees that use only one feature in the tree (this works for decision stumps or trees splitting on only one feature multiple times). In such trees, the computation is trivial as the whole effect of $\nu(N) - \nu(\emptyset)$ is all on the main effect of the single feature and there is no interaction effect. This expands on the fix in v1.2.1 [#286](https://github.com/mmschlk/shapiq/issues/286).
9
+ - fixes a bug with xgboost where feature names where trees that did not contain all features would lead `TreeExplainer` to fail
10
+ - fixes a bug with `stacked_bar_plot` where the higher order interactions were inflated by the lower order interactions, thus wrongly showing the higher order interactions as higher than they are
11
+ - fixes a bug where `InteractionValues.get_subset()` returns a faulty `coalition_lookup` dictionary pointing to indices outside the subset of players [#336](https://github.com/mmschlk/shapiq/issues/336)
12
+ - updates default value of `TreeExplainer`'s `min_order` parameter from 1 to 0 to include the baseline value in the interaction values as per default
13
+ - adds the `RegressionFBII` approximator to estimate Faithful Banzhaf interactions via least squares regression [#333](https://github.com/mmschlk/shapiq/pull/333). Additionally, FBII support was introduced in TabularExplainer and MonteCarlo-Approximator.
14
+ - adds a `RandomGame` class as part of `shapiq.games.benchmark` which always returns a random vector of integers between 0 and 100.
15
+
5
16
  ### v1.2.2 (2025-03-11)
6
17
  - changes python support to 3.10-3.13 [#318](https://github.com/mmschlk/shapiq/pull/318)
7
18
  - fixes a bug that prohibited importing shapiq in environments without write access [#326](https://github.com/mmschlk/shapiq/issues/326)
@@ -1,27 +1,30 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: shapiq
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: Shapley Interactions for Machine Learning
5
- Home-page: https://github.com/mmschlk/shapiq
6
- Author: Maximilian Muschalik et al.
7
- Author-email: maximilian.muschalik@ifi.lmu.de
8
- License: MIT
9
- Project-URL: Tracker, https://github.com/mmschlk/shapiq/issues
10
- Project-URL: Source, https://github.com/mmschlk/shapiq
11
- Project-URL: Documentation, https://shapiq.readthedocs.io
5
+ Author: Hubert Baniecki, Fabian Fumagalli
6
+ Author-email: Maximilian Muschalik <Maximilian.Muschalik@lmu.de>
7
+ Maintainer-email: Maximilian Muschalik <Maximilian.Muschalik@lmu.de>
8
+ License-Expression: MIT
9
+ Project-URL: documentation, https://shapiq.readthedocs.io
10
+ Project-URL: source, https://github.com/mmschlk/shapiq
11
+ Project-URL: tracker, https://github.com/mmschlk/shapiq/issues
12
+ Project-URL: changelog, https://github.com/mmschlk/shapiq/blob/main/CHANGELOG.md
12
13
  Keywords: python,machine learning,interpretable machine learning,shap,xai,explainable ai,interaction,shapley interactions,shapley values,feature interaction
13
14
  Classifier: Development Status :: 4 - Beta
14
- Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Intended Audience :: Developers
16
16
  Classifier: Intended Audience :: Science/Research
17
17
  Classifier: Topic :: Scientific/Engineering
18
18
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
- Classifier: Programming Language :: Python
19
+ Classifier: Operating System :: Microsoft :: Windows
20
+ Classifier: Operating System :: Unix
21
+ Classifier: Operating System :: MacOS
22
+ Classifier: Programming Language :: Python :: 3
20
23
  Classifier: Programming Language :: Python :: 3.10
21
24
  Classifier: Programming Language :: Python :: 3.11
22
25
  Classifier: Programming Language :: Python :: 3.12
23
26
  Classifier: Programming Language :: Python :: 3.13
24
- Requires-Python: >=3.10.0
27
+ Requires-Python: >=3.10
25
28
  Description-Content-Type: text/markdown
26
29
  License-File: LICENSE
27
30
  Requires-Dist: numpy
@@ -31,63 +34,20 @@ Requires-Dist: scikit-learn
31
34
  Requires-Dist: tqdm
32
35
  Requires-Dist: requests
33
36
  Requires-Dist: matplotlib
34
- Requires-Dist: colour
35
37
  Requires-Dist: networkx
36
- Provides-Extra: docs
37
- Requires-Dist: numpy; extra == "docs"
38
- Requires-Dist: scipy; extra == "docs"
39
- Requires-Dist: pandas; extra == "docs"
40
- Requires-Dist: scikit-learn; extra == "docs"
41
- Requires-Dist: tqdm; extra == "docs"
42
- Requires-Dist: requests; extra == "docs"
43
- Requires-Dist: matplotlib; extra == "docs"
44
- Requires-Dist: colour; extra == "docs"
45
- Requires-Dist: networkx; extra == "docs"
46
- Requires-Dist: sphinx; extra == "docs"
47
- Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
48
- Requires-Dist: sphinx_rtd_theme; extra == "docs"
49
- Requires-Dist: sphinx_toolbox; extra == "docs"
50
- Requires-Dist: nbsphinx; extra == "docs"
51
- Requires-Dist: pandoc; extra == "docs"
52
- Requires-Dist: furo; extra == "docs"
53
- Requires-Dist: sphinx-copybutton; extra == "docs"
54
- Requires-Dist: myst-parser; extra == "docs"
55
- Provides-Extra: dev
56
- Requires-Dist: numpy; extra == "dev"
57
- Requires-Dist: scipy; extra == "dev"
58
- Requires-Dist: pandas; extra == "dev"
59
- Requires-Dist: scikit-learn; extra == "dev"
60
- Requires-Dist: tqdm; extra == "dev"
61
- Requires-Dist: requests; extra == "dev"
62
- Requires-Dist: matplotlib; extra == "dev"
63
- Requires-Dist: colour; extra == "dev"
64
- Requires-Dist: networkx; extra == "dev"
65
- Requires-Dist: sphinx; extra == "dev"
66
- Requires-Dist: sphinx-autodoc-typehints; extra == "dev"
67
- Requires-Dist: sphinx_rtd_theme; extra == "dev"
68
- Requires-Dist: sphinx_toolbox; extra == "dev"
69
- Requires-Dist: nbsphinx; extra == "dev"
70
- Requires-Dist: pandoc; extra == "dev"
71
- Requires-Dist: furo; extra == "dev"
72
- Requires-Dist: sphinx-copybutton; extra == "dev"
73
- Requires-Dist: myst-parser; extra == "dev"
74
- Requires-Dist: build; extra == "dev"
75
- Requires-Dist: black; extra == "dev"
76
- Requires-Dist: pytest; extra == "dev"
77
- Requires-Dist: coverage; extra == "dev"
78
- Dynamic: author
79
- Dynamic: author-email
80
- Dynamic: classifier
81
- Dynamic: description
82
- Dynamic: description-content-type
83
- Dynamic: home-page
84
- Dynamic: keywords
85
- Dynamic: license
86
- Dynamic: project-url
87
- Dynamic: provides-extra
88
- Dynamic: requires-dist
89
- Dynamic: requires-python
90
- Dynamic: summary
38
+ Requires-Dist: colour
39
+ Provides-Extra: ml
40
+ Requires-Dist: tabpfn; extra == "ml"
41
+ Requires-Dist: torchvision; extra == "ml"
42
+ Requires-Dist: torch; extra == "ml"
43
+ Requires-Dist: xgboost; extra == "ml"
44
+ Requires-Dist: lightgbm; extra == "ml"
45
+ Requires-Dist: transformers; extra == "ml"
46
+ Requires-Dist: scikit-image; extra == "ml"
47
+ Requires-Dist: joblib; extra == "ml"
48
+ Requires-Dist: tensorflow; python_version < "3.13" and extra == "ml"
49
+ Requires-Dist: tf-keras; python_version < "3.13" and extra == "ml"
50
+ Dynamic: license-file
91
51
 
92
52
  # shapiq: Shapley Interactions for Machine Learning <img src="https://raw.githubusercontent.com/mmschlk/shapiq/main/docs/source/_static/logo/logo_shapiq_light.svg" alt="shapiq_logo" align="right" height="250px"/>
93
53
 
@@ -102,6 +62,8 @@ Dynamic: summary
102
62
  [![PePy](https://static.pepy.tech/badge/shapiq?style=flat-square)](https://pepy.tech/project/shapiq)
103
63
 
104
64
  [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
65
+ ![Contributions Welcome](https://img.shields.io/badge/contributions-welcome-brightgreen)
66
+ ![Last Commit](https://img.shields.io/github/last-commit/mmschlk/shapiq)
105
67
 
106
68
  > An interaction may speak more than a thousand main effects.
107
69
 
@@ -279,11 +241,21 @@ Some authors acknowledge the financial support by the German Research Foundation
279
241
  ---
280
242
  Built with ❤️ by the shapiq team.
281
243
 
282
-
283
244
  ## Changelog
284
245
 
285
246
  ### Development
286
247
 
248
+ ### v1.2.3 (2025-03-24)
249
+ - substantially improves the runtime of all `Regression` approximators by a) a faster pre-computation of the regression matrices and b) a faster computation of the weighted least squares regression [#340](https://github.com/mmschlk/shapiq/issues/340)
250
+ - removes `sample_replacements` parameter from `MarginalImputer` and removes the DeprecationWarning for it
251
+ - adds a trivial computation to `TreeSHAP-IQ` for trees that use only one feature in the tree (this works for decision stumps or trees splitting on only one feature multiple times). In such trees, the computation is trivial as the whole effect of $\nu(N) - \nu(\emptyset)$ is all on the main effect of the single feature and there is no interaction effect. This expands on the fix in v1.2.1 [#286](https://github.com/mmschlk/shapiq/issues/286).
252
+ - fixes a bug with xgboost where feature names where trees that did not contain all features would lead `TreeExplainer` to fail
253
+ - fixes a bug with `stacked_bar_plot` where the higher order interactions were inflated by the lower order interactions, thus wrongly showing the higher order interactions as higher than they are
254
+ - fixes a bug where `InteractionValues.get_subset()` returns a faulty `coalition_lookup` dictionary pointing to indices outside the subset of players [#336](https://github.com/mmschlk/shapiq/issues/336)
255
+ - updates default value of `TreeExplainer`'s `min_order` parameter from 1 to 0 to include the baseline value in the interaction values as per default
256
+ - adds the `RegressionFBII` approximator to estimate Faithful Banzhaf interactions via least squares regression [#333](https://github.com/mmschlk/shapiq/pull/333). Additionally, FBII support was introduced in TabularExplainer and MonteCarlo-Approximator.
257
+ - adds a `RandomGame` class as part of `shapiq.games.benchmark` which always returns a random vector of integers between 0 and 100.
258
+
287
259
  ### v1.2.2 (2025-03-11)
288
260
  - changes python support to 3.10-3.13 [#318](https://github.com/mmschlk/shapiq/pull/318)
289
261
  - fixes a bug that prohibited importing shapiq in environments without write access [#326](https://github.com/mmschlk/shapiq/issues/326)
@@ -11,6 +11,8 @@
11
11
  [![PePy](https://static.pepy.tech/badge/shapiq?style=flat-square)](https://pepy.tech/project/shapiq)
12
12
 
13
13
  [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
14
+ ![Contributions Welcome](https://img.shields.io/badge/contributions-welcome-brightgreen)
15
+ ![Last Commit](https://img.shields.io/github/last-commit/mmschlk/shapiq)
14
16
 
15
17
  > An interaction may speak more than a thousand main effects.
16
18
 
@@ -0,0 +1,121 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "shapiq"
7
+ dynamic = ["version", "readme"]
8
+ description = "Shapley Interactions for Machine Learning"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ # core
12
+ "numpy",
13
+ "scipy",
14
+ "pandas",
15
+ "scikit-learn",
16
+ "tqdm",
17
+ "requests",
18
+ # plotting
19
+ "matplotlib",
20
+ "networkx",
21
+ "colour"
22
+ ]
23
+ authors = [
24
+ {name = "Maximilian Muschalik", email = "Maximilian.Muschalik@lmu.de"},
25
+ {name = "Hubert Baniecki"},
26
+ {name = "Fabian Fumagalli"},
27
+ ]
28
+ maintainers = [
29
+ {name = "Maximilian Muschalik", email = "Maximilian.Muschalik@lmu.de"},
30
+ ]
31
+ license = "MIT"
32
+ classifiers = [
33
+ "Development Status :: 4 - Beta",
34
+ "Intended Audience :: Developers",
35
+ "Intended Audience :: Science/Research",
36
+ "Topic :: Scientific/Engineering",
37
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
38
+ 'Operating System :: Microsoft :: Windows',
39
+ 'Operating System :: Unix',
40
+ 'Operating System :: MacOS',
41
+ 'Programming Language :: Python :: 3',
42
+ 'Programming Language :: Python :: 3.10',
43
+ 'Programming Language :: Python :: 3.11',
44
+ 'Programming Language :: Python :: 3.12',
45
+ 'Programming Language :: Python :: 3.13',
46
+ ]
47
+ keywords = [
48
+ "python",
49
+ "machine learning",
50
+ "interpretable machine learning",
51
+ "shap",
52
+ "xai",
53
+ "explainable ai",
54
+ "interaction",
55
+ "shapley interactions",
56
+ "shapley values",
57
+ "feature interaction",
58
+ ]
59
+
60
+ [tool.setuptools.packages.find]
61
+ where = ["."]
62
+ include = ["shapiq*"]
63
+
64
+ [tool.setuptools.dynamic]
65
+ version = {attr = "shapiq.__version__"}
66
+ readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"}
67
+
68
+ [project.urls]
69
+ documentation = "https://shapiq.readthedocs.io"
70
+ source = "https://github.com/mmschlk/shapiq"
71
+ tracker = "https://github.com/mmschlk/shapiq/issues"
72
+ changelog = "https://github.com/mmschlk/shapiq/blob/main/CHANGELOG.md"
73
+
74
+ [project.optional-dependencies]
75
+ ml = [
76
+ "tabpfn",
77
+ "torchvision",
78
+ "torch",
79
+ "xgboost",
80
+ "lightgbm",
81
+ "transformers",
82
+ "scikit-image",
83
+ "joblib",
84
+ "tensorflow; python_version < '3.13'",
85
+ "tf-keras; python_version < '3.13'",
86
+ ]
87
+
88
+ [tool.pytest.ini_options]
89
+ testpaths = ["tests"]
90
+ minversion = "8.0"
91
+
92
+ [tool.ruff]
93
+ lint.select = ["E", "F", "I", "UP"] # https://beta.ruff.rs/docs/rules/
94
+ lint.ignore = ["E501"]
95
+ line-length = 100
96
+ target-version = "py310"
97
+
98
+ [tool.ruff.lint.isort]
99
+ known-first-party = ["shapiq"]
100
+ extra-standard-library = ["typing_extensions"]
101
+ combine-as-imports = true
102
+ force-wrap-aliases = true
103
+
104
+ [dependency-groups]
105
+ test = [
106
+ "pytest>=8.3.5",
107
+ "pytest-cov>=6.0.0",
108
+ "pytest-xdist>=3.6.1",
109
+ ]
110
+ lint = [
111
+ "ruff>=0.11.2",
112
+ "pre-commit>=4.2.0",
113
+ ]
114
+ dev = [
115
+ "build>=1.2.2.post1",
116
+ "twine>=6.1.0",
117
+ "notebook>=7.3.3",
118
+ "ipywidgets",
119
+ {include-group = "test"},
120
+ {include-group = "lint"},
121
+ ]
@@ -2,7 +2,7 @@
2
2
  the well established Shapley value and its generalization to interaction.
3
3
  """
4
4
 
5
- __version__ = "1.2.2"
5
+ __version__ = "1.2.3"
6
6
 
7
7
  # approximator classes
8
8
  from .approximator import (
@@ -16,6 +16,7 @@ from .approximator import (
16
16
  PermutationSamplingSII,
17
17
  PermutationSamplingSTII,
18
18
  PermutationSamplingSV,
19
+ RegressionFBII,
19
20
  RegressionFSII,
20
21
  StratifiedSamplingSV,
21
22
  UnbiasedKernelSHAP,
@@ -86,6 +87,7 @@ __all__ = [
86
87
  "OwenSamplingSV",
87
88
  "KernelSHAP",
88
89
  "RegressionFSII",
90
+ "RegressionFBII",
89
91
  "KernelSHAPIQ",
90
92
  "InconsistentKernelSHAPIQ",
91
93
  "SHAPIQ",
@@ -6,7 +6,14 @@ from .montecarlo import SHAPIQ, SVARM, SVARMIQ, UnbiasedKernelSHAP
6
6
  from .permutation.sii import PermutationSamplingSII
7
7
  from .permutation.stii import PermutationSamplingSTII
8
8
  from .permutation.sv import PermutationSamplingSV
9
- from .regression import InconsistentKernelSHAPIQ, KernelSHAP, KernelSHAPIQ, RegressionFSII, kADDSHAP
9
+ from .regression import (
10
+ InconsistentKernelSHAPIQ,
11
+ KernelSHAP,
12
+ KernelSHAPIQ,
13
+ RegressionFBII,
14
+ RegressionFSII,
15
+ kADDSHAP,
16
+ )
10
17
 
11
18
  # contains all SV approximators
12
19
  SV_APPROXIMATORS: list[Approximator.__class__] = [
@@ -57,6 +64,11 @@ FSII_APPROXIMATORS: list[Approximator.__class__] = [
57
64
  SHAPIQ,
58
65
  ]
59
66
 
67
+ # contains all approximators that can be used for FBII
68
+ FBII_APPROXIMATORS: list[Approximator.__class__] = [
69
+ RegressionFBII,
70
+ ]
71
+
60
72
  __all__ = [
61
73
  "PermutationSamplingSII",
62
74
  "PermutationSamplingSTII",
@@ -65,6 +77,7 @@ __all__ = [
65
77
  "OwenSamplingSV",
66
78
  "KernelSHAP",
67
79
  "RegressionFSII",
80
+ "RegressionFBII",
68
81
  "KernelSHAPIQ",
69
82
  "InconsistentKernelSHAPIQ",
70
83
  "SHAPIQ",
@@ -77,6 +90,7 @@ __all__ = [
77
90
  "SII_APPROXIMATORS",
78
91
  "STII_APPROXIMATORS",
79
92
  "FSII_APPROXIMATORS",
93
+ "FBII_APPROXIMATORS",
80
94
  ]
81
95
 
82
96
  # Path: shapiq/approximator/__init__.py
@@ -1,17 +1,16 @@
1
1
  """This module contains the base approximator classes for the shapiq package."""
2
2
 
3
- import copy
3
+ import warnings
4
4
  from abc import ABC, abstractmethod
5
5
  from collections.abc import Callable
6
6
 
7
7
  import numpy as np
8
+ from scipy.special import binom
8
9
 
9
10
  from ..approximator.sampling import CoalitionSampler
10
11
  from ..game_theory.indices import (
11
12
  AVAILABLE_INDICES_FOR_APPROXIMATION,
12
13
  get_computation_index,
13
- is_empty_value_the_baseline,
14
- is_index_aggregated,
15
14
  )
16
15
  from ..interaction_values import InteractionValues
17
16
  from ..utils.sets import generate_interaction_lookup
@@ -110,7 +109,7 @@ class Approximator(ABC):
110
109
  self, budget: int, game: Callable[[np.ndarray], np.ndarray], *args, **kwargs
111
110
  ) -> InteractionValues:
112
111
  """Calls the approximate method."""
113
- return self.approximate(budget, game, *args, **kwargs)
112
+ return self.approximate(budget=budget, game=game, *args, **kwargs)
114
113
 
115
114
  @abstractmethod
116
115
  def approximate(
@@ -129,7 +128,9 @@ class Approximator(ABC):
129
128
  Raises:
130
129
  NotImplementedError: If the method is not implemented.
131
130
  """
132
- raise NotImplementedError("The approximate method needs to be implemented.")
131
+ raise NotImplementedError(
132
+ "The approximate method must be implemented in the subclass."
133
+ ) # pragma: no cover
133
134
 
134
135
  def _init_sampling_weights(self) -> np.ndarray:
135
136
  """Initializes the weights for sampling subsets.
@@ -141,13 +142,29 @@ class Approximator(ABC):
141
142
  The weights for sampling subsets of size ``s`` in shape ``(n + 1,)``.
142
143
  """
143
144
  weight_vector = np.zeros(shape=self.n + 1)
144
- for coalition_size in range(0, self.n + 1):
145
- if (coalition_size < self.max_order) or (coalition_size > self.n - self.max_order):
146
- # prioritize these subsets
147
- weight_vector[coalition_size] = self._big_M
148
- else:
149
- # KernelSHAP sampling weights
150
- weight_vector[coalition_size] = 1 / (coalition_size * (self.n - coalition_size))
145
+ if self.index in ["FBII"]:
146
+
147
+ try:
148
+ for coalition_size in range(0, self.n + 1):
149
+ weight_vector[coalition_size] = binom(self.n, coalition_size) / 2**self.n
150
+ except OverflowError:
151
+ for coalition_size in range(0, self.n + 1):
152
+ weight_vector[coalition_size] = (
153
+ 1
154
+ / np.sqrt(2 * np.pi * 0.5)
155
+ * np.exp(-(coalition_size - self.n / 2) * +2 / (self.n / 2))
156
+ )
157
+ warnings.warn(
158
+ "The weights are approximated for n > 1000. While this is very close to the truth for sets for size in the region n/2, the approximation is not exact for size n or 0."
159
+ )
160
+ else:
161
+ for coalition_size in range(0, self.n + 1):
162
+ if (coalition_size < self.max_order) or (coalition_size > self.n - self.max_order):
163
+ # prioritize these subsets
164
+ weight_vector[coalition_size] = self._big_M
165
+ else:
166
+ # KernelSHAP sampling weights
167
+ weight_vector[coalition_size] = 1 / (coalition_size * (self.n - coalition_size))
151
168
  sampling_weight = weight_vector / np.sum(weight_vector)
152
169
  return sampling_weight
153
170
 
@@ -173,65 +190,6 @@ class Approximator(ABC):
173
190
  """
174
191
  return range(self.min_order, self.max_order + 1)
175
192
 
176
- def _finalize_result(
177
- self,
178
- result,
179
- baseline_value: float,
180
- *,
181
- estimated: bool | None = None,
182
- budget: int | None = None,
183
- ) -> InteractionValues:
184
- """Finalizes the result dictionary.
185
-
186
- Args:
187
- result: Interaction values.
188
- baseline_value: Baseline value.
189
- estimated: Whether interaction values were estimated.
190
- budget: The budget for the approximation.
191
-
192
- Returns:
193
- The interaction values.
194
-
195
- Raises:
196
- ValueError: If the baseline value is not provided for SII and k-SII.
197
- """
198
-
199
- if budget is None: # try to get budget from sampler
200
- budget = self._sampler.n_coalitions
201
- if budget == 0:
202
- raise ValueError("Budget is 0. Cannot finalize interaction values.")
203
- # Note to developer: This should not happen, the finalize method should be called
204
- # with a valid budget.
205
-
206
- if estimated is None:
207
- estimated = False if budget >= 2**self.n else True
208
-
209
- # set empty value as baseline value if necessary
210
- if tuple() in self._interaction_lookup:
211
- idx = self._interaction_lookup[tuple()]
212
- empty_value = result[idx]
213
- # only for SII empty value is not the baseline value
214
- if empty_value != baseline_value and is_empty_value_the_baseline(self.index):
215
- result[idx] = baseline_value
216
-
217
- interactions = InteractionValues(
218
- values=result,
219
- estimated=estimated,
220
- estimation_budget=budget,
221
- index=self.approximation_index, # can be different from self.index
222
- min_order=self.min_order,
223
- max_order=self.max_order,
224
- n_players=self.n,
225
- interaction_lookup=copy.deepcopy(self.interaction_lookup),
226
- baseline_value=baseline_value,
227
- )
228
-
229
- # if index needs to be aggregated
230
- if is_index_aggregated(self.index):
231
- interactions = self.aggregate_interaction_values(interactions)
232
-
233
- return interactions
234
-
235
193
  @staticmethod
236
194
  def _calc_iteration_count(budget: int, batch_size: int, iteration_cost: int) -> tuple[int, int]:
237
195
  """Computes the number of iterations and the size of the last batch given the batch size and
@@ -304,7 +262,6 @@ class Approximator(ABC):
304
262
  def aggregate_interaction_values(
305
263
  base_interactions: InteractionValues,
306
264
  order: int | None = None,
307
- player_set: set[int] | None = None,
308
265
  ) -> InteractionValues:
309
266
  """Aggregates the interaction values.
310
267
 
@@ -312,33 +269,10 @@ class Approximator(ABC):
312
269
  base_interactions: The base interaction values to aggregate.
313
270
  order: The order of the aggregation. For example, the order of the k-SII aggregation.
314
271
  If ``None`` (default), the maximum order of the base interactions is used.
315
- player_set: The set of players to consider for the aggregation. If ``None`` (default),
316
- all players are considered.
317
272
 
318
273
  Returns:
319
274
  The aggregated interaction values.
320
275
  """
321
- from shapiq.game_theory.aggregation import aggregate_interaction_values
322
-
323
- if player_set is not None:
324
- raise NotImplementedError(
325
- "Aggregating interaction values for a subset of players is not implemented."
326
- )
327
-
328
- return aggregate_interaction_values(base_interactions, order=order)
329
-
330
- @staticmethod
331
- def aggregate_to_one_dimension(
332
- interaction_values: InteractionValues,
333
- ) -> tuple[np.ndarray, np.ndarray]:
334
- """Aggregates the interaction values to one dimension.
335
-
336
- Args:
337
- interaction_values: The interaction values to aggregate.
338
-
339
- Returns:
340
- tuple[np.ndarray, np.ndarray]: The positive and negative aggregated values.
341
- """
342
- from shapiq.game_theory.aggregation import aggregate_to_one_dimension
276
+ from shapiq.game_theory.aggregation import aggregate_base_interaction
343
277
 
344
- return aggregate_to_one_dimension(interaction_values)
278
+ return aggregate_base_interaction(base_interactions, order=order)
@@ -6,7 +6,7 @@ from collections.abc import Callable
6
6
 
7
7
  import numpy as np
8
8
 
9
- from ...interaction_values import InteractionValues
9
+ from ...interaction_values import InteractionValues, finalize_computed_interactions
10
10
  from .._base import Approximator
11
11
 
12
12
 
@@ -41,13 +41,14 @@ class OwenSamplingSV(Approximator):
41
41
  self.n_anchor_points = n_anchor_points
42
42
 
43
43
  def approximate(
44
- self, budget: int, game: Callable[[np.ndarray], np.ndarray]
44
+ self, budget: int, game: Callable[[np.ndarray], np.ndarray], *args, **kwargs
45
45
  ) -> InteractionValues:
46
46
  """Approximates the Shapley values using Owen Sampling.
47
47
 
48
48
  Args:
49
49
  budget: The number of game evaluations for approximation
50
50
  game: The game function as a callable that takes a set of players and returns the value.
51
+ args and kwargs: Additional arguments and keyword arguments not used in this method.
51
52
 
52
53
  Returns:
53
54
  The estimated interaction values.
@@ -100,16 +101,39 @@ class OwenSamplingSV(Approximator):
100
101
  idx = self._interaction_lookup[(player,)]
101
102
  result_to_finalize[idx] = result[player]
102
103
 
103
- return self._finalize_result(
104
- result_to_finalize, baseline_value=empty_value, budget=used_budget, estimated=True
104
+ interaction = InteractionValues(
105
+ n_players=self.n,
106
+ values=result_to_finalize,
107
+ index=self.approximation_index,
108
+ interaction_lookup=self._interaction_lookup,
109
+ baseline_value=empty_value,
110
+ min_order=self.min_order,
111
+ max_order=self.max_order,
112
+ estimated=True,
113
+ estimation_budget=used_budget,
114
+ )
115
+
116
+ return finalize_computed_interactions(
117
+ interaction,
118
+ target_index=self.index,
105
119
  )
106
120
 
107
121
  @staticmethod
108
- def get_anchor_points(m: int):
109
- if m <= 0:
110
- raise ValueError("The number of anchor points needs to be greater than 0.")
122
+ def get_anchor_points(n_anchor_points: int):
123
+ """Returns the anchor points for the Owen Sampling approximation.
124
+
125
+ Args:
126
+ n_anchor_points: The number of anchor points.
111
127
 
112
- if m == 1:
128
+ Returns:
129
+ An array of anchor points.
130
+
131
+ Raises:
132
+ ValueError: If the number of anchor points is less than or equal to 0.
133
+ """
134
+ if n_anchor_points <= 0:
135
+ raise ValueError("The number of anchor points needs to be greater than 0.")
136
+ elif n_anchor_points == 1:
113
137
  return np.array([0.5])
114
138
  else:
115
- return np.linspace(0.0, 1.0, num=m)
139
+ return np.linspace(0.0, 1.0, num=n_anchor_points)
@@ -4,7 +4,7 @@ from collections.abc import Callable
4
4
 
5
5
  import numpy as np
6
6
 
7
- from ...interaction_values import InteractionValues
7
+ from ...interaction_values import InteractionValues, finalize_computed_interactions
8
8
  from .._base import Approximator
9
9
 
10
10
 
@@ -37,13 +37,14 @@ class StratifiedSamplingSV(Approximator):
37
37
  self.iteration_cost: int = 2
38
38
 
39
39
  def approximate(
40
- self, budget: int, game: Callable[[np.ndarray], np.ndarray]
40
+ self, budget: int, game: Callable[[np.ndarray], np.ndarray], *args, **kwargs
41
41
  ) -> InteractionValues:
42
42
  """Approximates the Shapley values using ApproShapley.
43
43
 
44
44
  Args:
45
45
  budget: The number of game evaluations for approximation
46
46
  game: The game function as a callable that takes a set of players and returns the value.
47
+ *args and **kwargs: Additional arguments not used.
47
48
 
48
49
  Returns:
49
50
  The estimated interaction values.
@@ -114,6 +115,16 @@ class StratifiedSamplingSV(Approximator):
114
115
  idx = self._interaction_lookup[(player,)]
115
116
  result_to_finalize[idx] = result[player]
116
117
 
117
- return self._finalize_result(
118
- result_to_finalize, baseline_value=empty_value, budget=used_budget, estimated=True
118
+ interactions = InteractionValues(
119
+ n_players=self.n,
120
+ values=result_to_finalize,
121
+ index=self.approximation_index,
122
+ interaction_lookup=self._interaction_lookup,
123
+ baseline_value=float(empty_value),
124
+ min_order=self.min_order,
125
+ max_order=self.max_order,
126
+ estimated=True,
127
+ estimation_budget=used_budget,
119
128
  )
129
+
130
+ return finalize_computed_interactions(interactions, target_index=self.index)