xplia 1.0.1__py3-none-any.whl

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 (870) hide show
  1. venv/Lib/site-packages/_distutils_hack/__init__.py +222 -0
  2. venv/Lib/site-packages/_distutils_hack/override.py +1 -0
  3. venv/Lib/site-packages/pip/__init__.py +13 -0
  4. venv/Lib/site-packages/pip/__main__.py +24 -0
  5. venv/Lib/site-packages/pip/__pip-runner__.py +50 -0
  6. venv/Lib/site-packages/pip/_internal/__init__.py +19 -0
  7. venv/Lib/site-packages/pip/_internal/build_env.py +311 -0
  8. venv/Lib/site-packages/pip/_internal/cache.py +292 -0
  9. venv/Lib/site-packages/pip/_internal/cli/__init__.py +4 -0
  10. venv/Lib/site-packages/pip/_internal/cli/autocompletion.py +171 -0
  11. venv/Lib/site-packages/pip/_internal/cli/base_command.py +236 -0
  12. venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py +1074 -0
  13. venv/Lib/site-packages/pip/_internal/cli/command_context.py +27 -0
  14. venv/Lib/site-packages/pip/_internal/cli/main.py +79 -0
  15. venv/Lib/site-packages/pip/_internal/cli/main_parser.py +134 -0
  16. venv/Lib/site-packages/pip/_internal/cli/parser.py +294 -0
  17. venv/Lib/site-packages/pip/_internal/cli/progress_bars.py +68 -0
  18. venv/Lib/site-packages/pip/_internal/cli/req_command.py +508 -0
  19. venv/Lib/site-packages/pip/_internal/cli/spinners.py +159 -0
  20. venv/Lib/site-packages/pip/_internal/cli/status_codes.py +6 -0
  21. venv/Lib/site-packages/pip/_internal/commands/__init__.py +132 -0
  22. venv/Lib/site-packages/pip/_internal/commands/cache.py +222 -0
  23. venv/Lib/site-packages/pip/_internal/commands/check.py +54 -0
  24. venv/Lib/site-packages/pip/_internal/commands/completion.py +121 -0
  25. venv/Lib/site-packages/pip/_internal/commands/configuration.py +282 -0
  26. venv/Lib/site-packages/pip/_internal/commands/debug.py +199 -0
  27. venv/Lib/site-packages/pip/_internal/commands/download.py +147 -0
  28. venv/Lib/site-packages/pip/_internal/commands/freeze.py +108 -0
  29. venv/Lib/site-packages/pip/_internal/commands/hash.py +59 -0
  30. venv/Lib/site-packages/pip/_internal/commands/help.py +41 -0
  31. venv/Lib/site-packages/pip/_internal/commands/index.py +139 -0
  32. venv/Lib/site-packages/pip/_internal/commands/inspect.py +92 -0
  33. venv/Lib/site-packages/pip/_internal/commands/install.py +778 -0
  34. venv/Lib/site-packages/pip/_internal/commands/list.py +368 -0
  35. venv/Lib/site-packages/pip/_internal/commands/search.py +174 -0
  36. venv/Lib/site-packages/pip/_internal/commands/show.py +189 -0
  37. venv/Lib/site-packages/pip/_internal/commands/uninstall.py +113 -0
  38. venv/Lib/site-packages/pip/_internal/commands/wheel.py +183 -0
  39. venv/Lib/site-packages/pip/_internal/configuration.py +381 -0
  40. venv/Lib/site-packages/pip/_internal/distributions/__init__.py +21 -0
  41. venv/Lib/site-packages/pip/_internal/distributions/base.py +39 -0
  42. venv/Lib/site-packages/pip/_internal/distributions/installed.py +23 -0
  43. venv/Lib/site-packages/pip/_internal/distributions/sdist.py +150 -0
  44. venv/Lib/site-packages/pip/_internal/distributions/wheel.py +34 -0
  45. venv/Lib/site-packages/pip/_internal/exceptions.py +733 -0
  46. venv/Lib/site-packages/pip/_internal/index/__init__.py +2 -0
  47. venv/Lib/site-packages/pip/_internal/index/collector.py +505 -0
  48. venv/Lib/site-packages/pip/_internal/index/package_finder.py +1029 -0
  49. venv/Lib/site-packages/pip/_internal/index/sources.py +223 -0
  50. venv/Lib/site-packages/pip/_internal/locations/__init__.py +467 -0
  51. venv/Lib/site-packages/pip/_internal/locations/_distutils.py +173 -0
  52. venv/Lib/site-packages/pip/_internal/locations/_sysconfig.py +213 -0
  53. venv/Lib/site-packages/pip/_internal/locations/base.py +81 -0
  54. venv/Lib/site-packages/pip/_internal/main.py +12 -0
  55. venv/Lib/site-packages/pip/_internal/metadata/__init__.py +127 -0
  56. venv/Lib/site-packages/pip/_internal/metadata/_json.py +84 -0
  57. venv/Lib/site-packages/pip/_internal/metadata/base.py +688 -0
  58. venv/Lib/site-packages/pip/_internal/metadata/importlib/__init__.py +4 -0
  59. venv/Lib/site-packages/pip/_internal/metadata/importlib/_compat.py +55 -0
  60. venv/Lib/site-packages/pip/_internal/metadata/importlib/_dists.py +224 -0
  61. venv/Lib/site-packages/pip/_internal/metadata/importlib/_envs.py +188 -0
  62. venv/Lib/site-packages/pip/_internal/metadata/pkg_resources.py +270 -0
  63. venv/Lib/site-packages/pip/_internal/models/__init__.py +2 -0
  64. venv/Lib/site-packages/pip/_internal/models/candidate.py +34 -0
  65. venv/Lib/site-packages/pip/_internal/models/direct_url.py +237 -0
  66. venv/Lib/site-packages/pip/_internal/models/format_control.py +80 -0
  67. venv/Lib/site-packages/pip/_internal/models/index.py +28 -0
  68. venv/Lib/site-packages/pip/_internal/models/installation_report.py +53 -0
  69. venv/Lib/site-packages/pip/_internal/models/link.py +581 -0
  70. venv/Lib/site-packages/pip/_internal/models/scheme.py +31 -0
  71. venv/Lib/site-packages/pip/_internal/models/search_scope.py +132 -0
  72. venv/Lib/site-packages/pip/_internal/models/selection_prefs.py +51 -0
  73. venv/Lib/site-packages/pip/_internal/models/target_python.py +110 -0
  74. venv/Lib/site-packages/pip/_internal/models/wheel.py +92 -0
  75. venv/Lib/site-packages/pip/_internal/network/__init__.py +2 -0
  76. venv/Lib/site-packages/pip/_internal/network/auth.py +561 -0
  77. venv/Lib/site-packages/pip/_internal/network/cache.py +69 -0
  78. venv/Lib/site-packages/pip/_internal/network/download.py +186 -0
  79. venv/Lib/site-packages/pip/_internal/network/lazy_wheel.py +210 -0
  80. venv/Lib/site-packages/pip/_internal/network/session.py +519 -0
  81. venv/Lib/site-packages/pip/_internal/network/utils.py +96 -0
  82. venv/Lib/site-packages/pip/_internal/network/xmlrpc.py +60 -0
  83. venv/Lib/site-packages/pip/_internal/operations/__init__.py +0 -0
  84. venv/Lib/site-packages/pip/_internal/operations/build/__init__.py +0 -0
  85. venv/Lib/site-packages/pip/_internal/operations/build/build_tracker.py +124 -0
  86. venv/Lib/site-packages/pip/_internal/operations/build/metadata.py +39 -0
  87. venv/Lib/site-packages/pip/_internal/operations/build/metadata_editable.py +41 -0
  88. venv/Lib/site-packages/pip/_internal/operations/build/metadata_legacy.py +74 -0
  89. venv/Lib/site-packages/pip/_internal/operations/build/wheel.py +37 -0
  90. venv/Lib/site-packages/pip/_internal/operations/build/wheel_editable.py +46 -0
  91. venv/Lib/site-packages/pip/_internal/operations/build/wheel_legacy.py +102 -0
  92. venv/Lib/site-packages/pip/_internal/operations/check.py +187 -0
  93. venv/Lib/site-packages/pip/_internal/operations/freeze.py +255 -0
  94. venv/Lib/site-packages/pip/_internal/operations/install/__init__.py +2 -0
  95. venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py +46 -0
  96. venv/Lib/site-packages/pip/_internal/operations/install/wheel.py +740 -0
  97. venv/Lib/site-packages/pip/_internal/operations/prepare.py +743 -0
  98. venv/Lib/site-packages/pip/_internal/pyproject.py +179 -0
  99. venv/Lib/site-packages/pip/_internal/req/__init__.py +92 -0
  100. venv/Lib/site-packages/pip/_internal/req/constructors.py +506 -0
  101. venv/Lib/site-packages/pip/_internal/req/req_file.py +552 -0
  102. venv/Lib/site-packages/pip/_internal/req/req_install.py +874 -0
  103. venv/Lib/site-packages/pip/_internal/req/req_set.py +119 -0
  104. venv/Lib/site-packages/pip/_internal/req/req_uninstall.py +650 -0
  105. venv/Lib/site-packages/pip/_internal/resolution/__init__.py +0 -0
  106. venv/Lib/site-packages/pip/_internal/resolution/base.py +20 -0
  107. venv/Lib/site-packages/pip/_internal/resolution/legacy/__init__.py +0 -0
  108. venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py +600 -0
  109. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__init__.py +0 -0
  110. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py +141 -0
  111. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py +555 -0
  112. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py +730 -0
  113. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py +155 -0
  114. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py +255 -0
  115. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/reporter.py +80 -0
  116. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py +165 -0
  117. venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py +299 -0
  118. venv/Lib/site-packages/pip/_internal/self_outdated_check.py +242 -0
  119. venv/Lib/site-packages/pip/_internal/utils/__init__.py +0 -0
  120. venv/Lib/site-packages/pip/_internal/utils/_jaraco_text.py +109 -0
  121. venv/Lib/site-packages/pip/_internal/utils/_log.py +38 -0
  122. venv/Lib/site-packages/pip/_internal/utils/appdirs.py +52 -0
  123. venv/Lib/site-packages/pip/_internal/utils/compat.py +63 -0
  124. venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py +165 -0
  125. venv/Lib/site-packages/pip/_internal/utils/datetime.py +11 -0
  126. venv/Lib/site-packages/pip/_internal/utils/deprecation.py +120 -0
  127. venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py +87 -0
  128. venv/Lib/site-packages/pip/_internal/utils/egg_link.py +72 -0
  129. venv/Lib/site-packages/pip/_internal/utils/encoding.py +36 -0
  130. venv/Lib/site-packages/pip/_internal/utils/entrypoints.py +84 -0
  131. venv/Lib/site-packages/pip/_internal/utils/filesystem.py +153 -0
  132. venv/Lib/site-packages/pip/_internal/utils/filetypes.py +27 -0
  133. venv/Lib/site-packages/pip/_internal/utils/glibc.py +88 -0
  134. venv/Lib/site-packages/pip/_internal/utils/hashes.py +151 -0
  135. venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py +35 -0
  136. venv/Lib/site-packages/pip/_internal/utils/logging.py +348 -0
  137. venv/Lib/site-packages/pip/_internal/utils/misc.py +735 -0
  138. venv/Lib/site-packages/pip/_internal/utils/models.py +39 -0
  139. venv/Lib/site-packages/pip/_internal/utils/packaging.py +57 -0
  140. venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py +146 -0
  141. venv/Lib/site-packages/pip/_internal/utils/subprocess.py +260 -0
  142. venv/Lib/site-packages/pip/_internal/utils/temp_dir.py +246 -0
  143. venv/Lib/site-packages/pip/_internal/utils/unpacking.py +257 -0
  144. venv/Lib/site-packages/pip/_internal/utils/urls.py +62 -0
  145. venv/Lib/site-packages/pip/_internal/utils/virtualenv.py +104 -0
  146. venv/Lib/site-packages/pip/_internal/utils/wheel.py +136 -0
  147. venv/Lib/site-packages/pip/_internal/vcs/__init__.py +15 -0
  148. venv/Lib/site-packages/pip/_internal/vcs/bazaar.py +112 -0
  149. venv/Lib/site-packages/pip/_internal/vcs/git.py +526 -0
  150. venv/Lib/site-packages/pip/_internal/vcs/mercurial.py +163 -0
  151. venv/Lib/site-packages/pip/_internal/vcs/subversion.py +324 -0
  152. venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py +705 -0
  153. venv/Lib/site-packages/pip/_internal/wheel_builder.py +355 -0
  154. venv/Lib/site-packages/pip/_vendor/__init__.py +120 -0
  155. venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py +18 -0
  156. venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py +61 -0
  157. venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py +137 -0
  158. venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py +65 -0
  159. venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py +9 -0
  160. venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py +188 -0
  161. venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py +39 -0
  162. venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py +32 -0
  163. venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py +439 -0
  164. venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py +111 -0
  165. venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py +139 -0
  166. venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py +190 -0
  167. venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py +33 -0
  168. venv/Lib/site-packages/pip/_vendor/certifi/__init__.py +4 -0
  169. venv/Lib/site-packages/pip/_vendor/certifi/__main__.py +12 -0
  170. venv/Lib/site-packages/pip/_vendor/certifi/core.py +108 -0
  171. venv/Lib/site-packages/pip/_vendor/chardet/__init__.py +115 -0
  172. venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py +386 -0
  173. venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py +47 -0
  174. venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py +261 -0
  175. venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py +106 -0
  176. venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py +147 -0
  177. venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py +0 -0
  178. venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py +112 -0
  179. venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py +90 -0
  180. venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachinedict.py +19 -0
  181. venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py +49 -0
  182. venv/Lib/site-packages/pip/_vendor/chardet/enums.py +85 -0
  183. venv/Lib/site-packages/pip/_vendor/chardet/escprober.py +102 -0
  184. venv/Lib/site-packages/pip/_vendor/chardet/escsm.py +261 -0
  185. venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py +102 -0
  186. venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py +196 -0
  187. venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py +47 -0
  188. venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py +388 -0
  189. venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py +47 -0
  190. venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py +284 -0
  191. venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py +47 -0
  192. venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py +316 -0
  193. venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py +325 -0
  194. venv/Lib/site-packages/pip/_vendor/chardet/johabfreq.py +2382 -0
  195. venv/Lib/site-packages/pip/_vendor/chardet/johabprober.py +47 -0
  196. venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py +238 -0
  197. venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py +4649 -0
  198. venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py +4397 -0
  199. venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py +4380 -0
  200. venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py +4649 -0
  201. venv/Lib/site-packages/pip/_vendor/chardet/langrussianmodel.py +5725 -0
  202. venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py +4380 -0
  203. venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py +4380 -0
  204. venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py +147 -0
  205. venv/Lib/site-packages/pip/_vendor/chardet/macromanprober.py +162 -0
  206. venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py +95 -0
  207. venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py +57 -0
  208. venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py +661 -0
  209. venv/Lib/site-packages/pip/_vendor/chardet/metadata/__init__.py +0 -0
  210. venv/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py +352 -0
  211. venv/Lib/site-packages/pip/_vendor/chardet/resultdict.py +16 -0
  212. venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py +162 -0
  213. venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py +88 -0
  214. venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py +105 -0
  215. venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py +362 -0
  216. venv/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py +225 -0
  217. venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py +82 -0
  218. venv/Lib/site-packages/pip/_vendor/chardet/version.py +9 -0
  219. venv/Lib/site-packages/pip/_vendor/colorama/__init__.py +7 -0
  220. venv/Lib/site-packages/pip/_vendor/colorama/ansi.py +102 -0
  221. venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py +277 -0
  222. venv/Lib/site-packages/pip/_vendor/colorama/initialise.py +121 -0
  223. venv/Lib/site-packages/pip/_vendor/colorama/tests/__init__.py +1 -0
  224. venv/Lib/site-packages/pip/_vendor/colorama/tests/ansi_test.py +76 -0
  225. venv/Lib/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py +294 -0
  226. venv/Lib/site-packages/pip/_vendor/colorama/tests/initialise_test.py +189 -0
  227. venv/Lib/site-packages/pip/_vendor/colorama/tests/isatty_test.py +57 -0
  228. venv/Lib/site-packages/pip/_vendor/colorama/tests/utils.py +49 -0
  229. venv/Lib/site-packages/pip/_vendor/colorama/tests/winterm_test.py +131 -0
  230. venv/Lib/site-packages/pip/_vendor/colorama/win32.py +180 -0
  231. venv/Lib/site-packages/pip/_vendor/colorama/winterm.py +195 -0
  232. venv/Lib/site-packages/pip/_vendor/distlib/__init__.py +23 -0
  233. venv/Lib/site-packages/pip/_vendor/distlib/compat.py +1116 -0
  234. venv/Lib/site-packages/pip/_vendor/distlib/database.py +1350 -0
  235. venv/Lib/site-packages/pip/_vendor/distlib/index.py +508 -0
  236. venv/Lib/site-packages/pip/_vendor/distlib/locators.py +1300 -0
  237. venv/Lib/site-packages/pip/_vendor/distlib/manifest.py +393 -0
  238. venv/Lib/site-packages/pip/_vendor/distlib/markers.py +152 -0
  239. venv/Lib/site-packages/pip/_vendor/distlib/metadata.py +1076 -0
  240. venv/Lib/site-packages/pip/_vendor/distlib/resources.py +358 -0
  241. venv/Lib/site-packages/pip/_vendor/distlib/scripts.py +437 -0
  242. venv/Lib/site-packages/pip/_vendor/distlib/util.py +1932 -0
  243. venv/Lib/site-packages/pip/_vendor/distlib/version.py +739 -0
  244. venv/Lib/site-packages/pip/_vendor/distlib/wheel.py +1082 -0
  245. venv/Lib/site-packages/pip/_vendor/distro/__init__.py +54 -0
  246. venv/Lib/site-packages/pip/_vendor/distro/__main__.py +4 -0
  247. venv/Lib/site-packages/pip/_vendor/distro/distro.py +1399 -0
  248. venv/Lib/site-packages/pip/_vendor/idna/__init__.py +44 -0
  249. venv/Lib/site-packages/pip/_vendor/idna/codec.py +112 -0
  250. venv/Lib/site-packages/pip/_vendor/idna/compat.py +13 -0
  251. venv/Lib/site-packages/pip/_vendor/idna/core.py +400 -0
  252. venv/Lib/site-packages/pip/_vendor/idna/idnadata.py +2151 -0
  253. venv/Lib/site-packages/pip/_vendor/idna/intranges.py +54 -0
  254. venv/Lib/site-packages/pip/_vendor/idna/package_data.py +2 -0
  255. venv/Lib/site-packages/pip/_vendor/idna/uts46data.py +8600 -0
  256. venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py +57 -0
  257. venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py +48 -0
  258. venv/Lib/site-packages/pip/_vendor/msgpack/ext.py +193 -0
  259. venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py +1010 -0
  260. venv/Lib/site-packages/pip/_vendor/packaging/__about__.py +26 -0
  261. venv/Lib/site-packages/pip/_vendor/packaging/__init__.py +25 -0
  262. venv/Lib/site-packages/pip/_vendor/packaging/_manylinux.py +301 -0
  263. venv/Lib/site-packages/pip/_vendor/packaging/_musllinux.py +136 -0
  264. venv/Lib/site-packages/pip/_vendor/packaging/_structures.py +61 -0
  265. venv/Lib/site-packages/pip/_vendor/packaging/markers.py +304 -0
  266. venv/Lib/site-packages/pip/_vendor/packaging/requirements.py +146 -0
  267. venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py +802 -0
  268. venv/Lib/site-packages/pip/_vendor/packaging/tags.py +487 -0
  269. venv/Lib/site-packages/pip/_vendor/packaging/utils.py +136 -0
  270. venv/Lib/site-packages/pip/_vendor/packaging/version.py +504 -0
  271. venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py +3361 -0
  272. venv/Lib/site-packages/pip/_vendor/platformdirs/__init__.py +566 -0
  273. venv/Lib/site-packages/pip/_vendor/platformdirs/__main__.py +53 -0
  274. venv/Lib/site-packages/pip/_vendor/platformdirs/android.py +210 -0
  275. venv/Lib/site-packages/pip/_vendor/platformdirs/api.py +223 -0
  276. venv/Lib/site-packages/pip/_vendor/platformdirs/macos.py +91 -0
  277. venv/Lib/site-packages/pip/_vendor/platformdirs/unix.py +223 -0
  278. venv/Lib/site-packages/pip/_vendor/platformdirs/version.py +4 -0
  279. venv/Lib/site-packages/pip/_vendor/platformdirs/windows.py +255 -0
  280. venv/Lib/site-packages/pip/_vendor/pygments/__init__.py +82 -0
  281. venv/Lib/site-packages/pip/_vendor/pygments/__main__.py +17 -0
  282. venv/Lib/site-packages/pip/_vendor/pygments/cmdline.py +668 -0
  283. venv/Lib/site-packages/pip/_vendor/pygments/console.py +70 -0
  284. venv/Lib/site-packages/pip/_vendor/pygments/filter.py +71 -0
  285. venv/Lib/site-packages/pip/_vendor/pygments/filters/__init__.py +940 -0
  286. venv/Lib/site-packages/pip/_vendor/pygments/formatter.py +124 -0
  287. venv/Lib/site-packages/pip/_vendor/pygments/formatters/__init__.py +158 -0
  288. venv/Lib/site-packages/pip/_vendor/pygments/formatters/_mapping.py +23 -0
  289. venv/Lib/site-packages/pip/_vendor/pygments/formatters/bbcode.py +108 -0
  290. venv/Lib/site-packages/pip/_vendor/pygments/formatters/groff.py +170 -0
  291. venv/Lib/site-packages/pip/_vendor/pygments/formatters/html.py +989 -0
  292. venv/Lib/site-packages/pip/_vendor/pygments/formatters/img.py +645 -0
  293. venv/Lib/site-packages/pip/_vendor/pygments/formatters/irc.py +154 -0
  294. venv/Lib/site-packages/pip/_vendor/pygments/formatters/latex.py +521 -0
  295. venv/Lib/site-packages/pip/_vendor/pygments/formatters/other.py +161 -0
  296. venv/Lib/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py +83 -0
  297. venv/Lib/site-packages/pip/_vendor/pygments/formatters/rtf.py +146 -0
  298. venv/Lib/site-packages/pip/_vendor/pygments/formatters/svg.py +188 -0
  299. venv/Lib/site-packages/pip/_vendor/pygments/formatters/terminal.py +127 -0
  300. venv/Lib/site-packages/pip/_vendor/pygments/formatters/terminal256.py +338 -0
  301. venv/Lib/site-packages/pip/_vendor/pygments/lexer.py +943 -0
  302. venv/Lib/site-packages/pip/_vendor/pygments/lexers/__init__.py +362 -0
  303. venv/Lib/site-packages/pip/_vendor/pygments/lexers/_mapping.py +559 -0
  304. venv/Lib/site-packages/pip/_vendor/pygments/lexers/python.py +1198 -0
  305. venv/Lib/site-packages/pip/_vendor/pygments/modeline.py +43 -0
  306. venv/Lib/site-packages/pip/_vendor/pygments/plugin.py +88 -0
  307. venv/Lib/site-packages/pip/_vendor/pygments/regexopt.py +91 -0
  308. venv/Lib/site-packages/pip/_vendor/pygments/scanner.py +104 -0
  309. venv/Lib/site-packages/pip/_vendor/pygments/sphinxext.py +217 -0
  310. venv/Lib/site-packages/pip/_vendor/pygments/style.py +197 -0
  311. venv/Lib/site-packages/pip/_vendor/pygments/styles/__init__.py +103 -0
  312. venv/Lib/site-packages/pip/_vendor/pygments/token.py +213 -0
  313. venv/Lib/site-packages/pip/_vendor/pygments/unistring.py +153 -0
  314. venv/Lib/site-packages/pip/_vendor/pygments/util.py +330 -0
  315. venv/Lib/site-packages/pip/_vendor/pyparsing/__init__.py +322 -0
  316. venv/Lib/site-packages/pip/_vendor/pyparsing/actions.py +217 -0
  317. venv/Lib/site-packages/pip/_vendor/pyparsing/common.py +432 -0
  318. venv/Lib/site-packages/pip/_vendor/pyparsing/core.py +6115 -0
  319. venv/Lib/site-packages/pip/_vendor/pyparsing/diagram/__init__.py +656 -0
  320. venv/Lib/site-packages/pip/_vendor/pyparsing/exceptions.py +299 -0
  321. venv/Lib/site-packages/pip/_vendor/pyparsing/helpers.py +1100 -0
  322. venv/Lib/site-packages/pip/_vendor/pyparsing/results.py +796 -0
  323. venv/Lib/site-packages/pip/_vendor/pyparsing/testing.py +331 -0
  324. venv/Lib/site-packages/pip/_vendor/pyparsing/unicode.py +361 -0
  325. venv/Lib/site-packages/pip/_vendor/pyparsing/util.py +284 -0
  326. venv/Lib/site-packages/pip/_vendor/pyproject_hooks/__init__.py +23 -0
  327. venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_compat.py +8 -0
  328. venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_impl.py +330 -0
  329. venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py +18 -0
  330. venv/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py +353 -0
  331. venv/Lib/site-packages/pip/_vendor/requests/__init__.py +182 -0
  332. venv/Lib/site-packages/pip/_vendor/requests/__version__.py +14 -0
  333. venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py +50 -0
  334. venv/Lib/site-packages/pip/_vendor/requests/adapters.py +538 -0
  335. venv/Lib/site-packages/pip/_vendor/requests/api.py +157 -0
  336. venv/Lib/site-packages/pip/_vendor/requests/auth.py +315 -0
  337. venv/Lib/site-packages/pip/_vendor/requests/certs.py +24 -0
  338. venv/Lib/site-packages/pip/_vendor/requests/compat.py +67 -0
  339. venv/Lib/site-packages/pip/_vendor/requests/cookies.py +561 -0
  340. venv/Lib/site-packages/pip/_vendor/requests/exceptions.py +141 -0
  341. venv/Lib/site-packages/pip/_vendor/requests/help.py +131 -0
  342. venv/Lib/site-packages/pip/_vendor/requests/hooks.py +33 -0
  343. venv/Lib/site-packages/pip/_vendor/requests/models.py +1034 -0
  344. venv/Lib/site-packages/pip/_vendor/requests/packages.py +16 -0
  345. venv/Lib/site-packages/pip/_vendor/requests/sessions.py +833 -0
  346. venv/Lib/site-packages/pip/_vendor/requests/status_codes.py +128 -0
  347. venv/Lib/site-packages/pip/_vendor/requests/structures.py +99 -0
  348. venv/Lib/site-packages/pip/_vendor/requests/utils.py +1094 -0
  349. venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py +26 -0
  350. venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__init__.py +0 -0
  351. venv/Lib/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py +6 -0
  352. venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py +133 -0
  353. venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py +43 -0
  354. venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py +547 -0
  355. venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py +170 -0
  356. venv/Lib/site-packages/pip/_vendor/rich/__init__.py +177 -0
  357. venv/Lib/site-packages/pip/_vendor/rich/__main__.py +274 -0
  358. venv/Lib/site-packages/pip/_vendor/rich/_cell_widths.py +451 -0
  359. venv/Lib/site-packages/pip/_vendor/rich/_emoji_codes.py +3610 -0
  360. venv/Lib/site-packages/pip/_vendor/rich/_emoji_replace.py +32 -0
  361. venv/Lib/site-packages/pip/_vendor/rich/_export_format.py +76 -0
  362. venv/Lib/site-packages/pip/_vendor/rich/_extension.py +10 -0
  363. venv/Lib/site-packages/pip/_vendor/rich/_fileno.py +24 -0
  364. venv/Lib/site-packages/pip/_vendor/rich/_inspect.py +270 -0
  365. venv/Lib/site-packages/pip/_vendor/rich/_log_render.py +94 -0
  366. venv/Lib/site-packages/pip/_vendor/rich/_loop.py +43 -0
  367. venv/Lib/site-packages/pip/_vendor/rich/_null_file.py +69 -0
  368. venv/Lib/site-packages/pip/_vendor/rich/_palettes.py +309 -0
  369. venv/Lib/site-packages/pip/_vendor/rich/_pick.py +17 -0
  370. venv/Lib/site-packages/pip/_vendor/rich/_ratio.py +160 -0
  371. venv/Lib/site-packages/pip/_vendor/rich/_spinners.py +482 -0
  372. venv/Lib/site-packages/pip/_vendor/rich/_stack.py +16 -0
  373. venv/Lib/site-packages/pip/_vendor/rich/_timer.py +19 -0
  374. venv/Lib/site-packages/pip/_vendor/rich/_win32_console.py +662 -0
  375. venv/Lib/site-packages/pip/_vendor/rich/_windows.py +72 -0
  376. venv/Lib/site-packages/pip/_vendor/rich/_windows_renderer.py +56 -0
  377. venv/Lib/site-packages/pip/_vendor/rich/_wrap.py +56 -0
  378. venv/Lib/site-packages/pip/_vendor/rich/abc.py +33 -0
  379. venv/Lib/site-packages/pip/_vendor/rich/align.py +311 -0
  380. venv/Lib/site-packages/pip/_vendor/rich/ansi.py +240 -0
  381. venv/Lib/site-packages/pip/_vendor/rich/bar.py +94 -0
  382. venv/Lib/site-packages/pip/_vendor/rich/box.py +517 -0
  383. venv/Lib/site-packages/pip/_vendor/rich/cells.py +154 -0
  384. venv/Lib/site-packages/pip/_vendor/rich/color.py +622 -0
  385. venv/Lib/site-packages/pip/_vendor/rich/color_triplet.py +38 -0
  386. venv/Lib/site-packages/pip/_vendor/rich/columns.py +187 -0
  387. venv/Lib/site-packages/pip/_vendor/rich/console.py +2633 -0
  388. venv/Lib/site-packages/pip/_vendor/rich/constrain.py +37 -0
  389. venv/Lib/site-packages/pip/_vendor/rich/containers.py +167 -0
  390. venv/Lib/site-packages/pip/_vendor/rich/control.py +225 -0
  391. venv/Lib/site-packages/pip/_vendor/rich/default_styles.py +190 -0
  392. venv/Lib/site-packages/pip/_vendor/rich/diagnose.py +37 -0
  393. venv/Lib/site-packages/pip/_vendor/rich/emoji.py +96 -0
  394. venv/Lib/site-packages/pip/_vendor/rich/errors.py +34 -0
  395. venv/Lib/site-packages/pip/_vendor/rich/file_proxy.py +57 -0
  396. venv/Lib/site-packages/pip/_vendor/rich/filesize.py +89 -0
  397. venv/Lib/site-packages/pip/_vendor/rich/highlighter.py +232 -0
  398. venv/Lib/site-packages/pip/_vendor/rich/json.py +140 -0
  399. venv/Lib/site-packages/pip/_vendor/rich/jupyter.py +101 -0
  400. venv/Lib/site-packages/pip/_vendor/rich/layout.py +443 -0
  401. venv/Lib/site-packages/pip/_vendor/rich/live.py +375 -0
  402. venv/Lib/site-packages/pip/_vendor/rich/live_render.py +113 -0
  403. venv/Lib/site-packages/pip/_vendor/rich/logging.py +289 -0
  404. venv/Lib/site-packages/pip/_vendor/rich/markup.py +246 -0
  405. venv/Lib/site-packages/pip/_vendor/rich/measure.py +151 -0
  406. venv/Lib/site-packages/pip/_vendor/rich/padding.py +141 -0
  407. venv/Lib/site-packages/pip/_vendor/rich/pager.py +34 -0
  408. venv/Lib/site-packages/pip/_vendor/rich/palette.py +100 -0
  409. venv/Lib/site-packages/pip/_vendor/rich/panel.py +308 -0
  410. venv/Lib/site-packages/pip/_vendor/rich/pretty.py +994 -0
  411. venv/Lib/site-packages/pip/_vendor/rich/progress.py +1702 -0
  412. venv/Lib/site-packages/pip/_vendor/rich/progress_bar.py +224 -0
  413. venv/Lib/site-packages/pip/_vendor/rich/prompt.py +376 -0
  414. venv/Lib/site-packages/pip/_vendor/rich/protocol.py +42 -0
  415. venv/Lib/site-packages/pip/_vendor/rich/region.py +10 -0
  416. venv/Lib/site-packages/pip/_vendor/rich/repr.py +149 -0
  417. venv/Lib/site-packages/pip/_vendor/rich/rule.py +130 -0
  418. venv/Lib/site-packages/pip/_vendor/rich/scope.py +86 -0
  419. venv/Lib/site-packages/pip/_vendor/rich/screen.py +54 -0
  420. venv/Lib/site-packages/pip/_vendor/rich/segment.py +739 -0
  421. venv/Lib/site-packages/pip/_vendor/rich/spinner.py +137 -0
  422. venv/Lib/site-packages/pip/_vendor/rich/status.py +132 -0
  423. venv/Lib/site-packages/pip/_vendor/rich/style.py +796 -0
  424. venv/Lib/site-packages/pip/_vendor/rich/styled.py +42 -0
  425. venv/Lib/site-packages/pip/_vendor/rich/syntax.py +948 -0
  426. venv/Lib/site-packages/pip/_vendor/rich/table.py +1002 -0
  427. venv/Lib/site-packages/pip/_vendor/rich/terminal_theme.py +153 -0
  428. venv/Lib/site-packages/pip/_vendor/rich/text.py +1307 -0
  429. venv/Lib/site-packages/pip/_vendor/rich/theme.py +115 -0
  430. venv/Lib/site-packages/pip/_vendor/rich/themes.py +5 -0
  431. venv/Lib/site-packages/pip/_vendor/rich/traceback.py +756 -0
  432. venv/Lib/site-packages/pip/_vendor/rich/tree.py +251 -0
  433. venv/Lib/site-packages/pip/_vendor/six.py +998 -0
  434. venv/Lib/site-packages/pip/_vendor/tenacity/__init__.py +608 -0
  435. venv/Lib/site-packages/pip/_vendor/tenacity/_asyncio.py +94 -0
  436. venv/Lib/site-packages/pip/_vendor/tenacity/_utils.py +76 -0
  437. venv/Lib/site-packages/pip/_vendor/tenacity/after.py +51 -0
  438. venv/Lib/site-packages/pip/_vendor/tenacity/before.py +46 -0
  439. venv/Lib/site-packages/pip/_vendor/tenacity/before_sleep.py +71 -0
  440. venv/Lib/site-packages/pip/_vendor/tenacity/nap.py +43 -0
  441. venv/Lib/site-packages/pip/_vendor/tenacity/retry.py +272 -0
  442. venv/Lib/site-packages/pip/_vendor/tenacity/stop.py +103 -0
  443. venv/Lib/site-packages/pip/_vendor/tenacity/tornadoweb.py +59 -0
  444. venv/Lib/site-packages/pip/_vendor/tenacity/wait.py +228 -0
  445. venv/Lib/site-packages/pip/_vendor/tomli/__init__.py +11 -0
  446. venv/Lib/site-packages/pip/_vendor/tomli/_parser.py +691 -0
  447. venv/Lib/site-packages/pip/_vendor/tomli/_re.py +107 -0
  448. venv/Lib/site-packages/pip/_vendor/tomli/_types.py +10 -0
  449. venv/Lib/site-packages/pip/_vendor/typing_extensions.py +3072 -0
  450. venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py +102 -0
  451. venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py +337 -0
  452. venv/Lib/site-packages/pip/_vendor/urllib3/_version.py +2 -0
  453. venv/Lib/site-packages/pip/_vendor/urllib3/connection.py +572 -0
  454. venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py +1132 -0
  455. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__init__.py +0 -0
  456. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py +36 -0
  457. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py +0 -0
  458. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py +519 -0
  459. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py +397 -0
  460. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py +314 -0
  461. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py +130 -0
  462. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +518 -0
  463. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py +921 -0
  464. venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py +216 -0
  465. venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py +323 -0
  466. venv/Lib/site-packages/pip/_vendor/urllib3/fields.py +274 -0
  467. venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py +98 -0
  468. venv/Lib/site-packages/pip/_vendor/urllib3/packages/__init__.py +0 -0
  469. venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py +0 -0
  470. venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py +51 -0
  471. venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py +155 -0
  472. venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py +1076 -0
  473. venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py +537 -0
  474. venv/Lib/site-packages/pip/_vendor/urllib3/request.py +170 -0
  475. venv/Lib/site-packages/pip/_vendor/urllib3/response.py +879 -0
  476. venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py +49 -0
  477. venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py +149 -0
  478. venv/Lib/site-packages/pip/_vendor/urllib3/util/proxy.py +57 -0
  479. venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py +22 -0
  480. venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py +137 -0
  481. venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py +107 -0
  482. venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py +620 -0
  483. venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py +495 -0
  484. venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py +159 -0
  485. venv/Lib/site-packages/pip/_vendor/urllib3/util/ssltransport.py +221 -0
  486. venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py +271 -0
  487. venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py +435 -0
  488. venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py +152 -0
  489. venv/Lib/site-packages/pip/_vendor/webencodings/__init__.py +342 -0
  490. venv/Lib/site-packages/pip/_vendor/webencodings/labels.py +231 -0
  491. venv/Lib/site-packages/pip/_vendor/webencodings/mklabels.py +59 -0
  492. venv/Lib/site-packages/pip/_vendor/webencodings/tests.py +153 -0
  493. venv/Lib/site-packages/pip/_vendor/webencodings/x_user_defined.py +325 -0
  494. venv/Lib/site-packages/pip/py.typed +4 -0
  495. venv/Lib/site-packages/pkg_resources/__init__.py +3296 -0
  496. venv/Lib/site-packages/pkg_resources/_vendor/__init__.py +0 -0
  497. venv/Lib/site-packages/pkg_resources/_vendor/appdirs.py +608 -0
  498. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py +36 -0
  499. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py +170 -0
  500. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py +104 -0
  501. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py +98 -0
  502. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py +35 -0
  503. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py +121 -0
  504. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py +137 -0
  505. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py +122 -0
  506. venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py +116 -0
  507. venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__init__.py +0 -0
  508. venv/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py +213 -0
  509. venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools.py +525 -0
  510. venv/Lib/site-packages/pkg_resources/_vendor/jaraco/text/__init__.py +599 -0
  511. venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py +4 -0
  512. venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py +4316 -0
  513. venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py +698 -0
  514. venv/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py +26 -0
  515. venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py +25 -0
  516. venv/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py +301 -0
  517. venv/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py +136 -0
  518. venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py +61 -0
  519. venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py +304 -0
  520. venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py +146 -0
  521. venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py +802 -0
  522. venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py +487 -0
  523. venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py +136 -0
  524. venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py +504 -0
  525. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/__init__.py +331 -0
  526. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/actions.py +207 -0
  527. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/common.py +424 -0
  528. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/core.py +5814 -0
  529. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py +642 -0
  530. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py +267 -0
  531. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/helpers.py +1088 -0
  532. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/results.py +760 -0
  533. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/testing.py +331 -0
  534. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/unicode.py +352 -0
  535. venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/util.py +235 -0
  536. venv/Lib/site-packages/pkg_resources/_vendor/zipp.py +329 -0
  537. venv/Lib/site-packages/pkg_resources/extern/__init__.py +76 -0
  538. venv/Lib/site-packages/setuptools/__init__.py +247 -0
  539. venv/Lib/site-packages/setuptools/_deprecation_warning.py +7 -0
  540. venv/Lib/site-packages/setuptools/_distutils/__init__.py +24 -0
  541. venv/Lib/site-packages/setuptools/_distutils/_collections.py +56 -0
  542. venv/Lib/site-packages/setuptools/_distutils/_functools.py +20 -0
  543. venv/Lib/site-packages/setuptools/_distutils/_macos_compat.py +12 -0
  544. venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py +572 -0
  545. venv/Lib/site-packages/setuptools/_distutils/archive_util.py +280 -0
  546. venv/Lib/site-packages/setuptools/_distutils/bcppcompiler.py +408 -0
  547. venv/Lib/site-packages/setuptools/_distutils/ccompiler.py +1220 -0
  548. venv/Lib/site-packages/setuptools/_distutils/cmd.py +436 -0
  549. venv/Lib/site-packages/setuptools/_distutils/command/__init__.py +25 -0
  550. venv/Lib/site-packages/setuptools/_distutils/command/_framework_compat.py +55 -0
  551. venv/Lib/site-packages/setuptools/_distutils/command/bdist.py +157 -0
  552. venv/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py +144 -0
  553. venv/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py +615 -0
  554. venv/Lib/site-packages/setuptools/_distutils/command/build.py +153 -0
  555. venv/Lib/site-packages/setuptools/_distutils/command/build_clib.py +208 -0
  556. venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py +787 -0
  557. venv/Lib/site-packages/setuptools/_distutils/command/build_py.py +407 -0
  558. venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py +173 -0
  559. venv/Lib/site-packages/setuptools/_distutils/command/check.py +151 -0
  560. venv/Lib/site-packages/setuptools/_distutils/command/clean.py +76 -0
  561. venv/Lib/site-packages/setuptools/_distutils/command/config.py +377 -0
  562. venv/Lib/site-packages/setuptools/_distutils/command/install.py +814 -0
  563. venv/Lib/site-packages/setuptools/_distutils/command/install_data.py +84 -0
  564. venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py +91 -0
  565. venv/Lib/site-packages/setuptools/_distutils/command/install_headers.py +45 -0
  566. venv/Lib/site-packages/setuptools/_distutils/command/install_lib.py +238 -0
  567. venv/Lib/site-packages/setuptools/_distutils/command/install_scripts.py +61 -0
  568. venv/Lib/site-packages/setuptools/_distutils/command/py37compat.py +31 -0
  569. venv/Lib/site-packages/setuptools/_distutils/command/register.py +319 -0
  570. venv/Lib/site-packages/setuptools/_distutils/command/sdist.py +531 -0
  571. venv/Lib/site-packages/setuptools/_distutils/command/upload.py +205 -0
  572. venv/Lib/site-packages/setuptools/_distutils/config.py +139 -0
  573. venv/Lib/site-packages/setuptools/_distutils/core.py +291 -0
  574. venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py +364 -0
  575. venv/Lib/site-packages/setuptools/_distutils/debug.py +5 -0
  576. venv/Lib/site-packages/setuptools/_distutils/dep_util.py +96 -0
  577. venv/Lib/site-packages/setuptools/_distutils/dir_util.py +243 -0
  578. venv/Lib/site-packages/setuptools/_distutils/dist.py +1286 -0
  579. venv/Lib/site-packages/setuptools/_distutils/errors.py +127 -0
  580. venv/Lib/site-packages/setuptools/_distutils/extension.py +248 -0
  581. venv/Lib/site-packages/setuptools/_distutils/fancy_getopt.py +470 -0
  582. venv/Lib/site-packages/setuptools/_distutils/file_util.py +249 -0
  583. venv/Lib/site-packages/setuptools/_distutils/filelist.py +371 -0
  584. venv/Lib/site-packages/setuptools/_distutils/log.py +80 -0
  585. venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py +832 -0
  586. venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py +695 -0
  587. venv/Lib/site-packages/setuptools/_distutils/py38compat.py +8 -0
  588. venv/Lib/site-packages/setuptools/_distutils/py39compat.py +22 -0
  589. venv/Lib/site-packages/setuptools/_distutils/spawn.py +109 -0
  590. venv/Lib/site-packages/setuptools/_distutils/sysconfig.py +558 -0
  591. venv/Lib/site-packages/setuptools/_distutils/text_file.py +287 -0
  592. venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py +401 -0
  593. venv/Lib/site-packages/setuptools/_distutils/util.py +513 -0
  594. venv/Lib/site-packages/setuptools/_distutils/version.py +358 -0
  595. venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py +175 -0
  596. venv/Lib/site-packages/setuptools/_entry_points.py +86 -0
  597. venv/Lib/site-packages/setuptools/_imp.py +82 -0
  598. venv/Lib/site-packages/setuptools/_importlib.py +47 -0
  599. venv/Lib/site-packages/setuptools/_itertools.py +23 -0
  600. venv/Lib/site-packages/setuptools/_path.py +29 -0
  601. venv/Lib/site-packages/setuptools/_reqs.py +19 -0
  602. venv/Lib/site-packages/setuptools/_vendor/__init__.py +0 -0
  603. venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py +1047 -0
  604. venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py +68 -0
  605. venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_collections.py +30 -0
  606. venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py +71 -0
  607. venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_functools.py +104 -0
  608. venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py +73 -0
  609. venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py +48 -0
  610. venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_text.py +99 -0
  611. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__init__.py +36 -0
  612. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_adapters.py +170 -0
  613. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_common.py +104 -0
  614. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py +98 -0
  615. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_itertools.py +35 -0
  616. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py +121 -0
  617. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py +137 -0
  618. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py +122 -0
  619. venv/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py +116 -0
  620. venv/Lib/site-packages/setuptools/_vendor/jaraco/__init__.py +0 -0
  621. venv/Lib/site-packages/setuptools/_vendor/jaraco/context.py +213 -0
  622. venv/Lib/site-packages/setuptools/_vendor/jaraco/functools.py +525 -0
  623. venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__init__.py +599 -0
  624. venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.py +4 -0
  625. venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.py +3824 -0
  626. venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.py +620 -0
  627. venv/Lib/site-packages/setuptools/_vendor/ordered_set.py +488 -0
  628. venv/Lib/site-packages/setuptools/_vendor/packaging/__about__.py +26 -0
  629. venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py +25 -0
  630. venv/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py +301 -0
  631. venv/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py +136 -0
  632. venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py +61 -0
  633. venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py +304 -0
  634. venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py +146 -0
  635. venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py +802 -0
  636. venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py +487 -0
  637. venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py +136 -0
  638. venv/Lib/site-packages/setuptools/_vendor/packaging/version.py +504 -0
  639. venv/Lib/site-packages/setuptools/_vendor/pyparsing/__init__.py +331 -0
  640. venv/Lib/site-packages/setuptools/_vendor/pyparsing/actions.py +207 -0
  641. venv/Lib/site-packages/setuptools/_vendor/pyparsing/common.py +424 -0
  642. venv/Lib/site-packages/setuptools/_vendor/pyparsing/core.py +5814 -0
  643. venv/Lib/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py +642 -0
  644. venv/Lib/site-packages/setuptools/_vendor/pyparsing/exceptions.py +267 -0
  645. venv/Lib/site-packages/setuptools/_vendor/pyparsing/helpers.py +1088 -0
  646. venv/Lib/site-packages/setuptools/_vendor/pyparsing/results.py +760 -0
  647. venv/Lib/site-packages/setuptools/_vendor/pyparsing/testing.py +331 -0
  648. venv/Lib/site-packages/setuptools/_vendor/pyparsing/unicode.py +352 -0
  649. venv/Lib/site-packages/setuptools/_vendor/pyparsing/util.py +235 -0
  650. venv/Lib/site-packages/setuptools/_vendor/tomli/__init__.py +11 -0
  651. venv/Lib/site-packages/setuptools/_vendor/tomli/_parser.py +691 -0
  652. venv/Lib/site-packages/setuptools/_vendor/tomli/_re.py +107 -0
  653. venv/Lib/site-packages/setuptools/_vendor/tomli/_types.py +10 -0
  654. venv/Lib/site-packages/setuptools/_vendor/typing_extensions.py +2296 -0
  655. venv/Lib/site-packages/setuptools/_vendor/zipp.py +329 -0
  656. venv/Lib/site-packages/setuptools/archive_util.py +213 -0
  657. venv/Lib/site-packages/setuptools/build_meta.py +511 -0
  658. venv/Lib/site-packages/setuptools/command/__init__.py +12 -0
  659. venv/Lib/site-packages/setuptools/command/alias.py +78 -0
  660. venv/Lib/site-packages/setuptools/command/bdist_egg.py +457 -0
  661. venv/Lib/site-packages/setuptools/command/bdist_rpm.py +40 -0
  662. venv/Lib/site-packages/setuptools/command/build.py +146 -0
  663. venv/Lib/site-packages/setuptools/command/build_clib.py +101 -0
  664. venv/Lib/site-packages/setuptools/command/build_ext.py +383 -0
  665. venv/Lib/site-packages/setuptools/command/build_py.py +368 -0
  666. venv/Lib/site-packages/setuptools/command/develop.py +193 -0
  667. venv/Lib/site-packages/setuptools/command/dist_info.py +142 -0
  668. venv/Lib/site-packages/setuptools/command/easy_install.py +2312 -0
  669. venv/Lib/site-packages/setuptools/command/editable_wheel.py +844 -0
  670. venv/Lib/site-packages/setuptools/command/egg_info.py +763 -0
  671. venv/Lib/site-packages/setuptools/command/install.py +139 -0
  672. venv/Lib/site-packages/setuptools/command/install_egg_info.py +63 -0
  673. venv/Lib/site-packages/setuptools/command/install_lib.py +122 -0
  674. venv/Lib/site-packages/setuptools/command/install_scripts.py +70 -0
  675. venv/Lib/site-packages/setuptools/command/py36compat.py +134 -0
  676. venv/Lib/site-packages/setuptools/command/register.py +18 -0
  677. venv/Lib/site-packages/setuptools/command/rotate.py +64 -0
  678. venv/Lib/site-packages/setuptools/command/saveopts.py +22 -0
  679. venv/Lib/site-packages/setuptools/command/sdist.py +210 -0
  680. venv/Lib/site-packages/setuptools/command/setopt.py +149 -0
  681. venv/Lib/site-packages/setuptools/command/test.py +251 -0
  682. venv/Lib/site-packages/setuptools/command/upload.py +17 -0
  683. venv/Lib/site-packages/setuptools/command/upload_docs.py +213 -0
  684. venv/Lib/site-packages/setuptools/config/__init__.py +35 -0
  685. venv/Lib/site-packages/setuptools/config/_apply_pyprojecttoml.py +377 -0
  686. venv/Lib/site-packages/setuptools/config/_validate_pyproject/__init__.py +34 -0
  687. venv/Lib/site-packages/setuptools/config/_validate_pyproject/error_reporting.py +318 -0
  688. venv/Lib/site-packages/setuptools/config/_validate_pyproject/extra_validations.py +36 -0
  689. venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py +51 -0
  690. venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py +1035 -0
  691. venv/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py +259 -0
  692. venv/Lib/site-packages/setuptools/config/expand.py +462 -0
  693. venv/Lib/site-packages/setuptools/config/pyprojecttoml.py +493 -0
  694. venv/Lib/site-packages/setuptools/config/setupcfg.py +762 -0
  695. venv/Lib/site-packages/setuptools/dep_util.py +25 -0
  696. venv/Lib/site-packages/setuptools/depends.py +176 -0
  697. venv/Lib/site-packages/setuptools/discovery.py +600 -0
  698. venv/Lib/site-packages/setuptools/dist.py +1222 -0
  699. venv/Lib/site-packages/setuptools/errors.py +58 -0
  700. venv/Lib/site-packages/setuptools/extension.py +148 -0
  701. venv/Lib/site-packages/setuptools/extern/__init__.py +76 -0
  702. venv/Lib/site-packages/setuptools/glob.py +167 -0
  703. venv/Lib/site-packages/setuptools/installer.py +104 -0
  704. venv/Lib/site-packages/setuptools/launch.py +36 -0
  705. venv/Lib/site-packages/setuptools/logging.py +36 -0
  706. venv/Lib/site-packages/setuptools/monkey.py +165 -0
  707. venv/Lib/site-packages/setuptools/msvc.py +1703 -0
  708. venv/Lib/site-packages/setuptools/namespaces.py +107 -0
  709. venv/Lib/site-packages/setuptools/package_index.py +1126 -0
  710. venv/Lib/site-packages/setuptools/py34compat.py +13 -0
  711. venv/Lib/site-packages/setuptools/sandbox.py +530 -0
  712. venv/Lib/site-packages/setuptools/unicode_utils.py +42 -0
  713. venv/Lib/site-packages/setuptools/version.py +6 -0
  714. venv/Lib/site-packages/setuptools/wheel.py +222 -0
  715. venv/Lib/site-packages/setuptools/windows_support.py +29 -0
  716. xplia/__init__.py +72 -0
  717. xplia/api/__init__.py +432 -0
  718. xplia/api/fastapi_app.py +453 -0
  719. xplia/cli.py +321 -0
  720. xplia/compliance/__init__.py +39 -0
  721. xplia/compliance/ai_act.py +538 -0
  722. xplia/compliance/compliance_checker.py +511 -0
  723. xplia/compliance/compliance_report.py +236 -0
  724. xplia/compliance/expert_review/__init__.py +18 -0
  725. xplia/compliance/expert_review/evaluation_criteria.py +209 -0
  726. xplia/compliance/expert_review/integration.py +270 -0
  727. xplia/compliance/expert_review/trust_expert_evaluator.py +379 -0
  728. xplia/compliance/explanation_rights.py +45 -0
  729. xplia/compliance/formatters/__init__.py +35 -0
  730. xplia/compliance/formatters/csv_formatter.py +179 -0
  731. xplia/compliance/formatters/html_formatter.py +689 -0
  732. xplia/compliance/formatters/html_trust_formatter.py +147 -0
  733. xplia/compliance/formatters/json_formatter.py +107 -0
  734. xplia/compliance/formatters/pdf_formatter.py +641 -0
  735. xplia/compliance/formatters/pdf_trust_formatter.py +309 -0
  736. xplia/compliance/formatters/trust_formatter_mixin.py +267 -0
  737. xplia/compliance/formatters/xml_formatter.py +173 -0
  738. xplia/compliance/gdpr.py +803 -0
  739. xplia/compliance/hipaa.py +134 -0
  740. xplia/compliance/report_base.py +205 -0
  741. xplia/compliance/report_generator.py +820 -0
  742. xplia/compliance/translations.py +299 -0
  743. xplia/core/__init__.py +98 -0
  744. xplia/core/base.py +391 -0
  745. xplia/core/config.py +297 -0
  746. xplia/core/factory.py +416 -0
  747. xplia/core/model_adapters/__init__.py +47 -0
  748. xplia/core/model_adapters/base.py +160 -0
  749. xplia/core/model_adapters/pytorch_adapter.py +339 -0
  750. xplia/core/model_adapters/sklearn_adapter.py +215 -0
  751. xplia/core/model_adapters/tensorflow_adapter.py +280 -0
  752. xplia/core/model_adapters/xgboost_adapter.py +295 -0
  753. xplia/core/optimizations.py +322 -0
  754. xplia/core/performance/__init__.py +57 -0
  755. xplia/core/performance/cache_manager.py +502 -0
  756. xplia/core/performance/memory_optimizer.py +465 -0
  757. xplia/core/performance/parallel_executor.py +327 -0
  758. xplia/core/registry.py +1234 -0
  759. xplia/explainers/__init__.py +70 -0
  760. xplia/explainers/__init__updated.py +62 -0
  761. xplia/explainers/adaptive/__init__.py +24 -0
  762. xplia/explainers/adaptive/explainer_selector.py +405 -0
  763. xplia/explainers/adaptive/explanation_quality.py +395 -0
  764. xplia/explainers/adaptive/fusion_strategies.py +297 -0
  765. xplia/explainers/adaptive/meta_explainer.py +320 -0
  766. xplia/explainers/adversarial/__init__.py +21 -0
  767. xplia/explainers/adversarial/adversarial_xai.py +678 -0
  768. xplia/explainers/anchor_explainer.py +1769 -0
  769. xplia/explainers/attention_explainer.py +996 -0
  770. xplia/explainers/bayesian/__init__.py +8 -0
  771. xplia/explainers/bayesian/bayesian_explainer.py +127 -0
  772. xplia/explainers/bias/__init__.py +17 -0
  773. xplia/explainers/bias/advanced_bias_detection.py +934 -0
  774. xplia/explainers/calibration/__init__.py +26 -0
  775. xplia/explainers/calibration/audience_adapter.py +376 -0
  776. xplia/explainers/calibration/audience_profiles.py +372 -0
  777. xplia/explainers/calibration/calibration_metrics.py +299 -0
  778. xplia/explainers/calibration/explanation_calibrator.py +460 -0
  779. xplia/explainers/causal/__init__.py +19 -0
  780. xplia/explainers/causal/causal_inference.py +669 -0
  781. xplia/explainers/certified/__init__.py +21 -0
  782. xplia/explainers/certified/certified_explanations.py +619 -0
  783. xplia/explainers/continual/__init__.py +8 -0
  784. xplia/explainers/continual/continual_explainer.py +102 -0
  785. xplia/explainers/counterfactual_explainer.py +804 -0
  786. xplia/explainers/counterfactuals/__init__.py +17 -0
  787. xplia/explainers/counterfactuals/advanced_counterfactuals.py +259 -0
  788. xplia/explainers/expert_evaluator.py +538 -0
  789. xplia/explainers/feature_importance_explainer.py +376 -0
  790. xplia/explainers/federated/__init__.py +17 -0
  791. xplia/explainers/federated/federated_xai.py +664 -0
  792. xplia/explainers/generative/__init__.py +15 -0
  793. xplia/explainers/generative/generative_explainer.py +243 -0
  794. xplia/explainers/gradient_explainer.py +3590 -0
  795. xplia/explainers/graph/__init__.py +26 -0
  796. xplia/explainers/graph/gnn_explainer.py +638 -0
  797. xplia/explainers/graph/molecular_explainer.py +438 -0
  798. xplia/explainers/lime_explainer.py +1580 -0
  799. xplia/explainers/llm/__init__.py +23 -0
  800. xplia/explainers/llm/llm_explainability.py +737 -0
  801. xplia/explainers/metalearning/__init__.py +15 -0
  802. xplia/explainers/metalearning/metalearning_explainer.py +276 -0
  803. xplia/explainers/moe/__init__.py +8 -0
  804. xplia/explainers/moe/moe_explainer.py +108 -0
  805. xplia/explainers/multimodal/__init__.py +30 -0
  806. xplia/explainers/multimodal/base.py +262 -0
  807. xplia/explainers/multimodal/diffusion_explainer.py +608 -0
  808. xplia/explainers/multimodal/foundation_model_explainer.py +323 -0
  809. xplia/explainers/multimodal/registry.py +139 -0
  810. xplia/explainers/multimodal/text_image_explainer.py +381 -0
  811. xplia/explainers/multimodal/vision_language_explainer.py +608 -0
  812. xplia/explainers/nas/__init__.py +5 -0
  813. xplia/explainers/nas/nas_explainer.py +65 -0
  814. xplia/explainers/neuralodes/__init__.py +5 -0
  815. xplia/explainers/neuralodes/neuralode_explainer.py +64 -0
  816. xplia/explainers/neurosymbolic/__init__.py +8 -0
  817. xplia/explainers/neurosymbolic/neurosymbolic_explainer.py +97 -0
  818. xplia/explainers/partial_dependence_explainer.py +509 -0
  819. xplia/explainers/privacy/__init__.py +21 -0
  820. xplia/explainers/privacy/differential_privacy_xai.py +624 -0
  821. xplia/explainers/quantum/__init__.py +5 -0
  822. xplia/explainers/quantum/quantum_explainer.py +79 -0
  823. xplia/explainers/recommender/__init__.py +8 -0
  824. xplia/explainers/recommender/recsys_explainer.py +124 -0
  825. xplia/explainers/reinforcement/__init__.py +15 -0
  826. xplia/explainers/reinforcement/rl_explainer.py +173 -0
  827. xplia/explainers/shap_explainer.py +2238 -0
  828. xplia/explainers/streaming/__init__.py +19 -0
  829. xplia/explainers/streaming/streaming_xai.py +703 -0
  830. xplia/explainers/timeseries/__init__.py +15 -0
  831. xplia/explainers/timeseries/timeseries_explainer.py +252 -0
  832. xplia/explainers/trust/__init__.py +24 -0
  833. xplia/explainers/trust/confidence_report.py +368 -0
  834. xplia/explainers/trust/fairwashing.py +489 -0
  835. xplia/explainers/trust/uncertainty.py +453 -0
  836. xplia/explainers/unified_explainer.py +566 -0
  837. xplia/explainers/unified_explainer_utils.py +309 -0
  838. xplia/integrations/__init__.py +3 -0
  839. xplia/integrations/mlflow_integration.py +331 -0
  840. xplia/integrations/wandb_integration.py +375 -0
  841. xplia/plugins/__init__.py +36 -0
  842. xplia/plugins/example_visualizer.py +11 -0
  843. xplia/utils/__init__.py +18 -0
  844. xplia/utils/performance.py +119 -0
  845. xplia/utils/validation.py +109 -0
  846. xplia/visualizations/__init__.py +31 -0
  847. xplia/visualizations/base.py +256 -0
  848. xplia/visualizations/boxplot_chart.py +224 -0
  849. xplia/visualizations/charts_impl.py +65 -0
  850. xplia/visualizations/gauge_chart.py +211 -0
  851. xplia/visualizations/gradient_viz.py +117 -0
  852. xplia/visualizations/heatmap_chart.py +173 -0
  853. xplia/visualizations/histogram_chart.py +176 -0
  854. xplia/visualizations/line_chart.py +100 -0
  855. xplia/visualizations/pie_chart.py +134 -0
  856. xplia/visualizations/radar_chart.py +154 -0
  857. xplia/visualizations/registry.py +76 -0
  858. xplia/visualizations/sankey_chart.py +190 -0
  859. xplia/visualizations/scatter_chart.py +252 -0
  860. xplia/visualizations/table_chart.py +263 -0
  861. xplia/visualizations/treemap_chart.py +216 -0
  862. xplia/visualizations.py +535 -0
  863. xplia/visualizers/__init__.py +87 -0
  864. xplia/visualizers/base_visualizer.py +294 -0
  865. xplia-1.0.1.dist-info/METADATA +685 -0
  866. xplia-1.0.1.dist-info/RECORD +870 -0
  867. xplia-1.0.1.dist-info/WHEEL +5 -0
  868. xplia-1.0.1.dist-info/entry_points.txt +2 -0
  869. xplia-1.0.1.dist-info/licenses/LICENSE +21 -0
  870. xplia-1.0.1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,2238 @@
1
+ """
2
+ SHAP Explainer pour XPLIA
3
+ ========================
4
+
5
+ Module d'explicabilité avancé basé sur SHAP (SHapley Additive exPlanations) pour XPLIA.
6
+
7
+ Ce module implémente une intégration sophistiquée des explications SHAP dans le framework
8
+ XPLIA, offrant une solution complète et optimisée pour l'explicabilité des modèles d'IA
9
+ via la méthode des valeurs de Shapley.
10
+
11
+ Caractéristiques principales:
12
+ -----------------------------
13
+ * Support multi-framework automatique (scikit-learn, XGBoost, LightGBM, TensorFlow, PyTorch)
14
+ * Optimisations de performance avancées pour grands jeux de données
15
+ * Échantillonnage intelligent et parallélisation
16
+ * Métriques de qualité d'explication intégrées (fidélité, cohérence, stabilité)
17
+ * Adaptation sophistiquée par niveau d'audience (technique, métier, réglementaire)
18
+ * Génération automatique de narratives en langage naturel
19
+ * Gestion des valeurs d'interaction et des analyses contrefactuelles
20
+ * Robustesse face aux valeurs manquantes et aberrantes
21
+ * Visualisations avancées et interactives
22
+ * Intégration transparente avec les modules de conformité réglementaire
23
+ * Benchmarking de performance et qualité intégré
24
+ * Cache intelligent pour réexplications rapides
25
+
26
+ Références théoriques:
27
+ ---------------------
28
+ * Lundberg, S. M., & Lee, S. I. (2017). A unified approach to interpreting model predictions.
29
+ * Štrumbelj, E., & Kononenko, I. (2014). Explaining prediction models and individual predictions.
30
+ * Sundararajan, M., & Najmi, A. (2020). The many Shapley values for model explanation.
31
+ """
32
+
33
+ import logging
34
+ import time
35
+ import warnings
36
+ import inspect
37
+ import os
38
+ import json
39
+ import hashlib
40
+ from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
41
+ from functools import lru_cache, partial
42
+ from dataclasses import dataclass, field
43
+ from typing import Any, Dict, List, Optional, Tuple, Union, Callable, Set, Type
44
+
45
+ import numpy as np
46
+ import pandas as pd
47
+ import matplotlib.pyplot as plt
48
+ from scipy import stats
49
+
50
+ # Import des optimisations de performance XPLIA
51
+ from ..core.optimizations import (
52
+ optimize, parallel_map, chunked_processing, cached_call,
53
+ optimize_memory, XPLIAOptimizer
54
+ )
55
+ from ..core.performance import (
56
+ ParallelExecutor, cached_result, memory_efficient,
57
+ process_in_chunks, optimize_explanations as opt_explanations
58
+ )
59
+
60
+ from ..core.base import (
61
+ AudienceLevel, ExplainerBase, ExplainabilityMethod,
62
+ ExplanationResult, FeatureImportance, ModelMetadata,
63
+ ExplanationQuality, ExplanationFormat
64
+ )
65
+ from ..core.registry import register_explainer
66
+ from ..utils.performance import Timer, MemoryTracker
67
+ from ..compliance import ComplianceChecker
68
+
69
+ # Définition de types personnalisés pour une meilleure lisibilité
70
+ DataType = Union[np.ndarray, pd.DataFrame, List[Union[List, Dict]]]
71
+ ModelType = Any
72
+ ShapValuesType = Union[np.ndarray, List[np.ndarray]]
73
+
74
+ @dataclass
75
+ class ShapConfig:
76
+ """Configuration avancée pour l'explainer SHAP."""
77
+ # Paramètres d'initialisation
78
+ background_data: Optional[DataType] = None
79
+ n_samples: int = 100
80
+ link: str = 'identity'
81
+ shap_type: str = 'auto' # 'kernel', 'tree', 'deep', 'gradient', 'auto'
82
+
83
+ # Paramètres de performance
84
+ use_gpu: bool = False
85
+ n_jobs: int = -1 # -1 = tous les CPU disponibles
86
+ batch_size: int = 100
87
+ cache_size: int = 128 # Taille du cache LRU
88
+ timeout: Optional[float] = None # Timeout en secondes
89
+
90
+ # Paramètres d'explicabilité
91
+ include_interactions: bool = False
92
+ feature_perturbation: str = 'interventional' # ou 'tree_path_dependent'
93
+ masker_type: Optional[str] = None # 'independent', 'tabular', 'image', 'text'
94
+ approximate: bool = False # Approximation pour gagner en performance
95
+ clustering: bool = False # Clustering pour réduire la dimensionnalité
96
+
97
+ # Métriques de qualité
98
+ compute_metrics: bool = True
99
+
100
+ # Contraintes réglementaires
101
+ compliance_mode: bool = False
102
+ compliance_regs: List[str] = field(default_factory=list)
103
+
104
+ def __post_init__(self):
105
+ if self.n_jobs == -1:
106
+ import multiprocessing
107
+ self.n_jobs = multiprocessing.cpu_count()
108
+
109
+ # Vérification de la cohérence des paramètres
110
+ if self.clustering and not self.approximate:
111
+ warnings.warn("Le clustering est plus efficace avec approximate=True")
112
+
113
+ if self.use_gpu:
114
+ try:
115
+ import tensorflow as tf
116
+ if not tf.test.is_gpu_available():
117
+ warnings.warn("GPU demandé mais non disponible. Utilisation du CPU.")
118
+ self.use_gpu = False
119
+ except ImportError:
120
+ warnings.warn("TensorFlow requis pour l'utilisation du GPU. Utilisation du CPU.")
121
+ self.use_gpu = False
122
+
123
+
124
+ @register_explainer
125
+ class ShapExplainer(ExplainerBase):
126
+ """
127
+ Explainer avancé basé sur SHAP (SHapley Additive exPlanations).
128
+
129
+ Cette classe implémente une solution sophistiquée d'explicabilité basée sur la théorie
130
+ des jeux coopératifs, attribuant à chaque caractéristique une valeur d'importance
131
+ optimale pour des décisions plus transparentes et justifiables.
132
+
133
+ Caractéristiques principales:
134
+ --------------------------
135
+ * Détection automatique et optimisée du type de modèle et du framework
136
+ * Sélection intelligente de l'implémentation SHAP optimale (kernel, tree, deep, etc.)
137
+ * Optimisations avancées de performance:
138
+ - Calcul parallélisé multi-CPU/GPU
139
+ - Mise en cache intelligente des résultats
140
+ - Échantillonnage adaptatif pour grands jeux de données
141
+ - Gestion avancée de la mémoire et timeouts configurables
142
+ * Métriques de qualité d'explication intégrées:
143
+ - Fidélité (conformité aux prédictions du modèle)
144
+ - Cohérence (stabilité face à des variations mineures)
145
+ - Sparsité (concision des explications)
146
+ - Exactitude (précision des attributions)
147
+ * Support de cas d'usage spécialisés:
148
+ - Analysis d'interactions entre variables
149
+ - Évaluation contrefactuelle
150
+ - Robustesse à l'adversarial noise
151
+ - Agrégation multi-modèles
152
+ * Interface flexible et multi-format:
153
+ - Visualisations adaptatives selon le niveau d'audience
154
+ - Génération de narratives en langage naturel
155
+ - Exportations multi-formats (JSON, HTML, PDF)
156
+ * Intégration complète avec les modules de conformité réglementaire
157
+ * Robustesse face aux données manquantes et aberrantes
158
+ * Documentation contextuelle et exemples intégrés
159
+
160
+ Cette implémentation inclut les dernières avancées de recherche en matière
161
+ d'explicabilité et d'interprétabilité des modèles d'IA, tout en offrant
162
+ une interface simple et des performances optimales pour l'utilisation en
163
+ production.
164
+ """
165
+
166
+ # Constantes de la classe
167
+ _SHAP_TYPES = ["kernel", "tree", "deep", "gradient", "partition", "permutation", "auto"]
168
+ _LINK_FUNCTIONS = ["identity", "logit"]
169
+ _MODEL_TYPES = {
170
+ "tree": [
171
+ "RandomForestClassifier", "RandomForestRegressor",
172
+ "GradientBoostingClassifier", "GradientBoostingRegressor",
173
+ "XGBClassifier", "XGBRegressor", "Booster",
174
+ "LGBMClassifier", "LGBMRegressor",
175
+ "CatBoostClassifier", "CatBoostRegressor",
176
+ "DecisionTreeClassifier", "DecisionTreeRegressor",
177
+ "GradientBoosting", "RandomForest", "ExtraTreesClassifier",
178
+ "ExtraTreesRegressor", "IsolationForest"
179
+ ],
180
+ "neural": [
181
+ "Sequential", "Model", "Module", "Functional",
182
+ "MLPClassifier", "MLPRegressor", "KerasClassifier", "KerasRegressor"
183
+ ],
184
+ "differentiable": [
185
+ "LogisticRegression", "LinearRegression",
186
+ "SGDClassifier", "SGDRegressor", "LinearSVC", "LinearSVR",
187
+ "Ridge", "Lasso", "ElasticNet"
188
+ ],
189
+ "generic": [
190
+ "SVC", "SVR", "KNeighborsClassifier", "KNeighborsRegressor",
191
+ "GaussianProcessClassifier", "GaussianProcessRegressor",
192
+ "Pipeline", "BaggingClassifier", "BaggingRegressor",
193
+ "VotingClassifier", "VotingRegressor", "StackingClassifier", "StackingRegressor"
194
+ ]
195
+ }
196
+ _FRAMEWORK_IDENTIFIERS = {
197
+ "sklearn": ["sklearn"],
198
+ "xgboost": ["xgboost"],
199
+ "lightgbm": ["lightgbm"],
200
+ "catboost": ["catboost"],
201
+ "tensorflow": ["tensorflow", "keras", "tf"],
202
+ "pytorch": ["torch"],
203
+ "mxnet": ["mxnet"]
204
+ }
205
+
206
+ def __init__(self, model: ModelType, **kwargs):
207
+ """
208
+ Initialise l'explainer SHAP avancé avec détection intelligente et optimisations.
209
+
210
+ Args:
211
+ model: Le modèle à expliquer (support multi-framework)
212
+ **kwargs: Configuration avancée (voir ShapConfig pour tous les paramètres)
213
+ background_data: Données d'arrière-plan pour les explications
214
+ n_samples: Nombre d'échantillons pour l'approximation
215
+ link: Fonction de lien ('identity', 'logit')
216
+ shap_type: Type SHAP ('kernel', 'tree', 'deep', 'gradient', 'auto')
217
+ use_gpu: Utiliser l'accélération GPU si disponible
218
+ n_jobs: Nombre de processus parallèles (-1 = tous les CPU)
219
+ batch_size: Taille des batches pour les grands datasets
220
+ include_interactions: Calculer les interactions entre variables
221
+ compute_metrics: Calculer les métriques de qualité d'explication
222
+ approximate: Utiliser des approximations pour grandes dimensions
223
+ compliance_mode: Activer le mode conformité réglementaire
224
+ compliance_regs: Liste des réglementations à vérifier
225
+
226
+ Exemples:
227
+ >>> explainer = ShapExplainer(model)
228
+ >>> explainer = ShapExplainer(model, background_data=X_train[:100])
229
+ >>> explainer = ShapExplainer(
230
+ ... model,
231
+ ... shap_type='tree',
232
+ ... use_gpu=True,
233
+ ... compute_metrics=True,
234
+ ... include_interactions=True
235
+ ... )
236
+ """
237
+ super().__init__(model, **kwargs)
238
+ self._method = ExplainabilityMethod.SHAP
239
+
240
+ # Configuration avancée
241
+ if isinstance(kwargs.get('config'), ShapConfig):
242
+ self._config = kwargs.get('config')
243
+ else:
244
+ self._config = ShapConfig(**kwargs)
245
+
246
+ # Journalisation avancée
247
+ self._logger = logging.getLogger(__name__)
248
+ self._logger.info(f"Initialisation de ShapExplainer avec {self._config.shap_type} SHAP")
249
+
250
+ # Statistiques internes et métriques
251
+ self._stats = {
252
+ "explanations_count": 0,
253
+ "total_time_seconds": 0,
254
+ "avg_time_seconds": 0,
255
+ "max_time_seconds": 0,
256
+ "min_time_seconds": float('inf'),
257
+ "cache_hits": 0,
258
+ "feature_importance_consistency": [],
259
+ "explanation_quality": {}
260
+ }
261
+
262
+ # Extraction de métadonnées et configuration du mode de fonctionnement
263
+ self._model_type = None
264
+ self._model_framework = None
265
+ self._is_classifier = False
266
+ self._output_names = None
267
+ self._feature_names = None
268
+ self._extract_model_metadata()
269
+
270
+ # Initialisation des hooks de pré/post-processing
271
+ self._pre_explain_hooks = []
272
+ self._post_explain_hooks = []
273
+
274
+ # Compatibilité réglementaire si activée
275
+ self._compliance_checker = None
276
+ if self._config.compliance_mode:
277
+ self._setup_compliance()
278
+
279
+ # Configuration du cache selon les paramètres
280
+ self._setup_caching()
281
+
282
+ # Initialisation de l'explainer SHAP adapté au modèle
283
+ self._shap_explainer = None
284
+ with Timer() as timer:
285
+ self._initialize_explainer()
286
+ self._logger.info(f"Initialisation de l'explainer SHAP terminée en {timer.duration:.3f}s")
287
+
288
+ def _extract_model_metadata(self):
289
+ """Extrait des métadonnées complètes du modèle pour optimiser l'explication."""
290
+ # Détection du type de modèle et du framework
291
+ self._model_type = self._detect_model_type()
292
+ self._model_framework = self._detect_model_framework()
293
+ self._is_classifier = self._detect_is_classifier()
294
+
295
+ # Recherche des noms de variables
296
+ self._feature_names = self._extract_feature_names()
297
+ self._output_names = self._extract_output_names()
298
+
299
+ self._logger.info(f"Modèle détecté: {self._model_framework} - {self._model_type} - "
300
+ f"{'classificateur' if self._is_classifier else 'régresseur'}")
301
+
302
+ def _setup_caching(self):
303
+ """Configure le cache intelligent pour les explications."""
304
+ cache_size = self._config.cache_size
305
+ if cache_size > 0:
306
+ # Application du décorateur LRU cache à certaines méthodes internes
307
+ self._compute_shap_values = lru_cache(maxsize=cache_size)(self._compute_shap_values)
308
+ self._feature_importance_to_explanation = lru_cache(maxsize=cache_size)(self._feature_importance_to_explanation)
309
+ self._logger.info(f"Cache LRU activé avec {cache_size} entrées")
310
+
311
+ def _setup_compliance(self):
312
+ """Configure le vérificateur de conformité réglementaire pour les explications."""
313
+ try:
314
+ self._compliance_checker = ComplianceChecker()
315
+ self._logger.info(f"Module de conformité initialisé avec les réglementations: "
316
+ f"{self._config.compliance_regs or ['par défaut']}")
317
+ except Exception as e:
318
+ self._logger.warning(f"Impossible d'initialiser le module de conformité: {str(e)}")
319
+ self._compliance_checker = None
320
+
321
+ def _maybe_use_gpu_context(self):
322
+ """
323
+ Contexte de gestion des ressources GPU pour l'explication.
324
+
325
+ Permet d'optimiser automatiquement l'utilisation des ressources GPU
326
+ lors du calcul des valeurs SHAP, particulièrement pour les grands jeux de données
327
+ et les modèles complexes.
328
+
329
+ Returns:
330
+ Un gestionnaire de contexte qui configure l'environnement GPU optimalement
331
+ """
332
+ if not self._config.use_gpu:
333
+ # Si GPU non activé, retourner un contexte vide (no-op)
334
+ from contextlib import nullcontext
335
+ return nullcontext()
336
+
337
+ # Contexte GPU adapté au framework détecté
338
+ model_type = self._detect_model_type()
339
+
340
+ class _GPUContext:
341
+ def __init__(self, explainer):
342
+ self._explainer = explainer
343
+ self._prev_state = None
344
+ self._tf_context = None
345
+ self._torch_context = None
346
+
347
+ def __enter__(self):
348
+ try:
349
+ if 'tensorflow' in model_type:
350
+ import tensorflow as tf
351
+ # Activer GPU pour TensorFlow
352
+ self._explainer._set_gpu_memory_growth()
353
+ # Enregistrer les devices disponibles pour les restaurer ensuite
354
+ self._prev_state = tf.config.get_visible_devices()
355
+ # N'autoriser que GPU si disponible
356
+ gpus = tf.config.list_physical_devices('GPU')
357
+ if gpus:
358
+ tf.config.set_visible_devices(gpus, 'GPU')
359
+ self._explainer._logger.info(f"GPU activé pour TensorFlow: {len(gpus)} périphériques")
360
+ else:
361
+ self._explainer._logger.warning("GPU demandé mais non disponible pour TensorFlow")
362
+
363
+ elif 'pytorch' in model_type or 'torch' in model_type:
364
+ import torch
365
+ # Activer CUDA pour PyTorch si disponible
366
+ if torch.cuda.is_available():
367
+ # Préparer le device et définir le contexte par défaut
368
+ self._prev_state = torch.cuda.current_device()
369
+ torch.cuda.set_device(0) # Premier GPU par défaut
370
+ self._explainer._logger.info(f"GPU activé pour PyTorch: {torch.cuda.get_device_name(0)}")
371
+ else:
372
+ self._explainer._logger.warning("GPU demandé mais CUDA non disponible pour PyTorch")
373
+
374
+ # Configuration spécifique pour SHAP
375
+ # L'API SHAP n'a pas d'API publique pour configurer le GPU, mais certaines
376
+ # implémentations de SHAP détectent automatiquement et utilisent le GPU si disponible
377
+ except Exception as e:
378
+ self._explainer._logger.error(f"Erreur lors de l'activation GPU: {str(e)}")
379
+
380
+ return self
381
+
382
+ def __exit__(self, exc_type, exc_val, exc_tb):
383
+ try:
384
+ # Restaurer l'état précédent
385
+ if 'tensorflow' in model_type and self._prev_state is not None:
386
+ import tensorflow as tf
387
+ tf.config.set_visible_devices(self._prev_state)
388
+ elif ('pytorch' in model_type or 'torch' in model_type) and self._prev_state is not None:
389
+ import torch
390
+ if torch.cuda.is_available():
391
+ torch.cuda.set_device(self._prev_state)
392
+ # Libérer la mémoire GPU
393
+ torch.cuda.empty_cache()
394
+ except Exception as e:
395
+ self._explainer._logger.error(f"Erreur lors de la restauration du contexte GPU: {str(e)}")
396
+
397
+ # Retourner une instance du gestionnaire de contexte
398
+ return _GPUContext(self)
399
+
400
+ def _set_gpu_memory_growth(self):
401
+ """
402
+ Configure la croissance mémoire GPU dynamique pour TensorFlow.
403
+
404
+ Cette méthode contextuelle permet une utilisation optimale de la mémoire GPU
405
+ en configurant une allocation dynamique, évitant ainsi les erreurs OOM et
406
+ permettant une meilleure répartition des ressources entre plusieurs processus.
407
+ """
408
+ try:
409
+ import tensorflow as tf
410
+ gpus = tf.config.list_physical_devices('GPU')
411
+ if gpus:
412
+ # Configurer la croissance mémoire dynamique sur tous les GPU
413
+ for gpu in gpus:
414
+ tf.config.experimental.set_memory_growth(gpu, True)
415
+ self._logger.info(f"Allocation mémoire GPU dynamique activée pour {len(gpus)} GPU(s)")
416
+ except ImportError:
417
+ self._logger.info("TensorFlow non installé, croissance mémoire GPU non configurée")
418
+ except Exception as e:
419
+ self._logger.warning(f"Erreur lors de la configuration de la mémoire GPU: {str(e)}")
420
+
421
+ def _initialize_explainer(self):
422
+ """
423
+ Initialise l'explainer SHAP optimal en fonction du type de modèle détecté.
424
+
425
+ Cette méthode implémente une sélection intelligente et optimisée de l'explainer
426
+ SHAP approprié selon une analyse approfondie du modèle et du contexte.
427
+ Inclut des optimisations avancées pour maximiser performance et précision.
428
+ """
429
+ import shap
430
+
431
+ # Mesure des performances
432
+ with Timer() as timer, MemoryTracker() as mem_tracker:
433
+ # Détection sophistiquée du type de modèle
434
+ model_type = self._detect_model_type()
435
+ framework = model_type.split('-')[0] if '-' in model_type else 'unknown'
436
+
437
+ self._logger.info(f"Initialisation de l'explainer SHAP pour: {model_type}")
438
+
439
+ # Préparation des paramètres communs
440
+ kwargs = {}
441
+
442
+ # Appliquer les optimisations avancées selon la configuration
443
+ if hasattr(shap, 'utils') and hasattr(shap.utils, 'set_parallelism'):
444
+ try:
445
+ shap.utils.set_parallelism(self._config.n_jobs)
446
+ self._logger.debug(f"Parallélisme SHAP configuré avec {self._config.n_jobs} threads")
447
+ except Exception as e:
448
+ self._logger.debug(f"Impossible de configurer le parallélisme SHAP: {str(e)}")
449
+
450
+ # Configuration spécifique aux modèles d'arbres
451
+ if self._config.shap_type == 'tree' or (self._config.shap_type == 'auto' and
452
+ (self._is_tree_model() or any(x in model_type.lower() for x in ['tree', 'forest', 'boost', 'xgb', 'lgbm', 'catboost']))):
453
+
454
+ # Optimisations pour les modèles d'arbres
455
+ kwargs = {
456
+ 'model_output': 'raw', # ou 'probability', 'margin', 'logit'
457
+ 'feature_perturbation': self._config.feature_perturbation
458
+ }
459
+
460
+ # Gérer les interactions si nécessaire
461
+ if self._config.include_interactions:
462
+ kwargs['interactions'] = True
463
+ self._logger.debug("Calcul des interactions activé pour TreeExplainer")
464
+
465
+ # Détecter le framework XGBoost/LightGBM/Catboost pour appliquer les optimisations spécifiques
466
+ if 'xgboost' in framework:
467
+ self._logger.debug("Optimisations spécifiques pour XGBoost activées")
468
+ kwargs['data'] = self._get_background_data()
469
+ if self._config.approximate:
470
+ kwargs['approximate'] = True
471
+ if self._config.use_gpu:
472
+ # Vérifier si XGBoost est compilé avec GPU
473
+ try:
474
+ import xgboost
475
+ if hasattr(xgboost, 'gpu_predictor'):
476
+ kwargs['gpu_predictor'] = True
477
+ self._logger.debug("Support GPU XGBoost activé")
478
+ except (ImportError, AttributeError):
479
+ pass
480
+
481
+ # Création de l'explainer avec tous les paramètres optimisés
482
+ try:
483
+ self._logger.info("Initialisation de TreeExplainer avec paramètres optimisés")
484
+ self._shap_explainer = shap.TreeExplainer(self._model, **kwargs)
485
+ return
486
+ except Exception as e:
487
+ self._logger.warning(f"Erreur lors de l'initialisation de TreeExplainer: {str(e)}. Repli...")
488
+
489
+ # Configuration pour les réseaux de neurones profonds
490
+ elif self._config.shap_type == 'deep' or (self._config.shap_type == 'auto' and
491
+ (self._is_deep_model() or any(x in model_type.lower() for x in ['tensorflow', 'keras', 'torch', 'neural']))):
492
+
493
+ try:
494
+ background_data = self._get_background_data()
495
+
496
+ # Optimisations pour réseaux de neurones
497
+ if framework == 'tensorflow':
498
+ # Vérifier si on peut utiliser GradientExplainer qui est plus efficace pour TF
499
+ if hasattr(shap, 'GradientExplainer'):
500
+ self._logger.info("Initialisation de GradientExplainer pour TensorFlow")
501
+ self._shap_explainer = shap.GradientExplainer(self._model, background_data)
502
+ return
503
+
504
+ # Déterminer le bon type d'explainer pour réseaux profonds
505
+ self._logger.info("Initialisation de DeepExplainer")
506
+ if self._config.use_gpu:
507
+ with self._set_gpu_memory_growth():
508
+ self._shap_explainer = shap.DeepExplainer(self._model, background_data)
509
+ else:
510
+ self._shap_explainer = shap.DeepExplainer(self._model, background_data)
511
+ return
512
+ except Exception as e:
513
+ self._logger.warning(f"Erreur lors de l'initialisation de DeepExplainer: {str(e)}. Repli...")
514
+
515
+ # Configuration pour les modèles différentiables
516
+ elif self._config.shap_type == 'gradient' or (self._config.shap_type == 'auto' and
517
+ self._is_differentiable_model()):
518
+
519
+ try:
520
+ background_data = self._get_background_data()
521
+ self._logger.info("Initialisation de GradientExplainer")
522
+ self._shap_explainer = shap.GradientExplainer(self._model, background_data)
523
+ return
524
+ except Exception as e:
525
+ self._logger.warning(f"Erreur lors de l'initialisation de GradientExplainer: {str(e)}. Repli...")
526
+
527
+ # Configuration par défaut: KernelSHAP (universel mais plus lent)
528
+ try:
529
+ background_data = self._get_background_data()
530
+
531
+ # Optimisations pour KernelExplainer
532
+ kwargs = {
533
+ 'link': self._config.link,
534
+ }
535
+
536
+ if hasattr(self._model, 'predict_proba'):
537
+ kwargs['output_names'] = 'probability'
538
+
539
+ if self._config.approximate:
540
+ # Mode approximation pour grands jeux de données
541
+ kwargs['nsamples'] = self._config.n_samples
542
+ # Plus l'approximation est fine, plus précis mais plus lent
543
+ if self._config.n_samples <= 50:
544
+ self._logger.warning("Petit nombre d'échantillons, précision possiblement limitée")
545
+
546
+ if self._config.clustering and hasattr(shap, 'kmeans'):
547
+ # Clustering pour réduire les calculs
548
+ with Timer() as cluster_timer:
549
+ n_clusters = min(50, len(background_data) // 10) if len(background_data) > 500 else None
550
+ if n_clusters:
551
+ try:
552
+ background_data = shap.kmeans(background_data, n_clusters)
553
+ self._logger.debug(f"Clustering appliqué: {n_clusters} clusters en {cluster_timer.duration:.2f}s")
554
+ except Exception as e:
555
+ self._logger.warning(f"Erreur lors du clustering: {str(e)}")
556
+
557
+ # Implémentation de maskers si disponible dans la version de SHAP
558
+ masker = None
559
+ if self._config.masker_type and hasattr(shap, 'maskers'):
560
+ if self._config.masker_type == 'tabular' and hasattr(shap.maskers, 'Tabular'):
561
+ masker = shap.maskers.Tabular(background_data)
562
+ elif self._config.masker_type == 'text' and hasattr(shap.maskers, 'Text'):
563
+ masker = shap.maskers.Text()
564
+ elif self._config.masker_type == 'image' and hasattr(shap.maskers, 'Image'):
565
+ masker = shap.maskers.Image()
566
+
567
+ self._logger.info("Initialisation de KernelExplainer (explainer générique)")
568
+
569
+ if masker and hasattr(shap, 'Explainer'):
570
+ # Utiliser le nouvel API SHAP si disponible
571
+ self._shap_explainer = shap.Explainer(self._model_predict_wrapper, masker, **kwargs)
572
+ else:
573
+ # API standard KernelExplainer
574
+ self._shap_explainer = shap.KernelExplainer(
575
+ self._model_predict_wrapper,
576
+ background_data,
577
+ **kwargs
578
+ )
579
+
580
+ except Exception as e:
581
+ self._logger.error(f"Erreur critique lors de l'initialisation de l'explainer SHAP: {str(e)}")
582
+ raise RuntimeError(f"Initialisation de l'explainer SHAP impossible: {str(e)}")
583
+
584
+ # Log des performances de l'initialisation
585
+ memory_usage = mem_tracker.peak_usage_mb
586
+ self._logger.info(
587
+ f"Explainer SHAP initialisé en {timer.duration:.2f}s avec {memory_usage:.1f}MB "
588
+ f"(Type: {self._config.shap_type if self._config.shap_type != 'auto' else 'auto-detect'})"
589
+ )
590
+
591
+ def _maybe_use_gpu_context(self):
592
+ """
593
+ Contexte de gestion des ressources GPU pour l'explication.
594
+
595
+ Permet d'optimiser automatiquement l'utilisation des ressources GPU
596
+ lors du calcul des valeurs SHAP, particulièrement pour les grands jeux de données
597
+ et les modèles complexes.
598
+
599
+ Returns:
600
+ Un gestionnaire de contexte qui configure l'environnement GPU optimalement
601
+ """
602
+ import contextlib
603
+
604
+ @contextlib.contextmanager
605
+ def _gpu_context():
606
+ if not self._config.use_gpu:
607
+ yield
608
+ return
609
+
610
+ try:
611
+ # TensorFlow GPU configuration
612
+ try:
613
+ import tensorflow as tf
614
+ if hasattr(tf, 'config') and hasattr(tf.config, 'experimental'):
615
+ with self._set_gpu_memory_growth():
616
+ yield
617
+ return
618
+ except (ImportError, AttributeError):
619
+ pass
620
+
621
+ # PyTorch GPU configuration
622
+ try:
623
+ import torch
624
+ if torch.cuda.is_available():
625
+ self._logger.debug(f"Utilisation de PyTorch avec GPU: {torch.cuda.get_device_name(0)}")
626
+ # Optionally set additional PyTorch GPU configurations here
627
+ pass
628
+ except (ImportError, AttributeError):
629
+ pass
630
+
631
+ # No specific GPU config needed/available
632
+ yield
633
+ except Exception as e:
634
+ self._logger.warning(f"Erreur lors de la configuration GPU: {str(e)}")
635
+ yield
636
+
637
+ return _gpu_context()
638
+
639
+ def _set_gpu_memory_growth(self):
640
+ """Configure la croissance mémoire GPU dynamique pour TensorFlow.
641
+
642
+ Cette méthode contextuelle permet une utilisation optimale de la mémoire GPU
643
+ en configurant une allocation dynamique, évitant ainsi les erreurs OOM et
644
+ permettant une meilleure répartition des ressources entre plusieurs processus.
645
+ """
646
+ import contextlib
647
+
648
+ @contextlib.contextmanager
649
+ def _set_tf_memory_growth():
650
+ try:
651
+ import tensorflow as tf
652
+ if hasattr(tf, 'config') and hasattr(tf.config, 'experimental'):
653
+ gpus = tf.config.experimental.list_physical_devices('GPU')
654
+ if gpus:
655
+ for gpu in gpus:
656
+ tf.config.experimental.set_memory_growth(gpu, True)
657
+ self._logger.debug(f"{len(gpus)} GPU(s) configuré(s) avec memory growth")
658
+ except Exception as e:
659
+ self._logger.debug(f"Impossible de configurer la mémoire GPU: {str(e)}")
660
+ yield
661
+
662
+ return _set_tf_memory_growth()
663
+
664
+ def _detect_model_type(self) -> str:
665
+ """
666
+ Détecte le type de modèle pour choisir l'explainer SHAP optimal.
667
+
668
+ Cette méthode avancée identifie avec précision la nature du modèle à travers
669
+ une analyse multiniveau (framework, architecture, caractéristiques), permettant
670
+ de sélectionner l'implémentation SHAP la plus performante et précise pour ce modèle.
671
+
672
+ Returns:
673
+ str: Type de modèle détecté avec son framework ('sklearn-RandomForestClassifier', etc.)
674
+ """
675
+ # Analyser le framework du modèle
676
+ model_module = self._get_model_module()
677
+ framework = 'unknown'
678
+ for fw_name, identifiers in self._FRAMEWORK_IDENTIFIERS.items():
679
+ if any(ident in model_module.lower() for ident in identifiers):
680
+ framework = fw_name
681
+ break
682
+
683
+ # Récupérer le nom spécifique du modèle avec gestion avancée des cas complexes
684
+ model_name = type(self._model).__name__
685
+
686
+ # Cas 1: Pipeline scikit-learn - récupérer le modèle final
687
+ if self._is_pipeline():
688
+ try:
689
+ # Extraire le dernier estimateur du pipeline
690
+ if hasattr(self._model, 'steps') and self._model.steps:
691
+ last_step = self._model.steps[-1][1]
692
+ model_name = type(last_step).__name__
693
+ # Vérifier le module du dernier estimateur pour plus de précision
694
+ last_module = last_step.__module__
695
+ for fw_name, identifiers in self._FRAMEWORK_IDENTIFIERS.items():
696
+ if any(ident in last_module.lower() for ident in identifiers):
697
+ framework = fw_name
698
+ break
699
+ except (IndexError, AttributeError) as e:
700
+ self._logger.debug(f"Impossible d'analyser le pipeline: {str(e)}")
701
+
702
+ # Cas 2: Méta-estimateurs et ensembles - analyser les estimateurs de base
703
+ elif self._is_ensemble():
704
+ try:
705
+ base_estimator = None
706
+ # Essayer différentes structures d'ensembles connues
707
+ if hasattr(self._model, 'estimators_') and self._model.estimators_:
708
+ base_estimator = self._model.estimators_[0]
709
+ elif hasattr(self._model, 'estimators') and self._model.estimators:
710
+ base_estimator = self._model.estimators[0]
711
+ elif hasattr(self._model, 'base_estimator'):
712
+ base_estimator = self._model.base_estimator
713
+
714
+ if base_estimator:
715
+ model_name = f"Ensemble({type(base_estimator).__name__})"
716
+ except (IndexError, AttributeError) as e:
717
+ self._logger.debug(f"Impossible d'analyser l'ensemble: {str(e)}")
718
+
719
+ # Cas 3: Modèles à noyaux - identifier le type de noyau
720
+ elif any(kernel_type in model_name.lower() for kernel_type in ["svc", "svr", "gaussianprocess"]):
721
+ kernel_name = "rbf" # noyau par défaut
722
+ if hasattr(self._model, "kernel") and isinstance(self._model.kernel, str):
723
+ kernel_name = self._model.kernel
724
+ elif hasattr(self._model, "get_params"):
725
+ params = self._model.get_params()
726
+ if "kernel" in params and isinstance(params["kernel"], str):
727
+ kernel_name = params["kernel"]
728
+ model_name = f"{model_name}({kernel_name})"
729
+
730
+ # Cas 4: Modèles de deep learning - analyser l'architecture
731
+ elif framework in ["tensorflow", "pytorch", "mxnet"]:
732
+ # Ajouter des informations sur la profondeur ou le type d'architecture
733
+ if framework == "tensorflow" and hasattr(self._model, "layers"):
734
+ try:
735
+ n_layers = len(self._model.layers)
736
+ layer_types = [l.__class__.__name__ for l in self._model.layers][:3] # Premiers types de couches
737
+ model_name = f"{model_name}(layers={n_layers}, types={','.join(layer_types)}...)"
738
+ except:
739
+ pass
740
+ elif framework == "pytorch" and hasattr(self._model, "_modules"):
741
+ try:
742
+ n_modules = len(list(self._model._modules.items()))
743
+ model_name = f"{model_name}(modules={n_modules})"
744
+ except:
745
+ pass
746
+
747
+ # Cas 5: Détection avancée pour modèles spécifiques nécessitant un traitement particulier
748
+ if framework == "xgboost" and "booster" in model_name.lower():
749
+ # XGBoost nécessite une détection spécifique pour les types d'objectif
750
+ if hasattr(self._model, "objective"):
751
+ model_name = f"{model_name}({self._model.objective})"
752
+
753
+ # Enregistrer et retourner le résultat final avec format standard
754
+ detected_type = f"{framework}-{model_name}"
755
+ self._logger.info(f"Type de modèle détecté: {detected_type}")
756
+ return detected_type
757
+
758
+ def _get_model_module(self) -> str:
759
+ """Obtient le module du modèle pour aider à la détection du framework."""
760
+ try:
761
+ return str(self._model.__module__)
762
+ except (AttributeError, TypeError):
763
+ # Essayer d'obtenir le module via le type
764
+ return str(type(self._model).__module__)
765
+
766
+ def _is_pipeline(self) -> bool:
767
+ """Vérifie si le modèle est un pipeline ou une chaîne de traitement."""
768
+ # Vérifier scikit-learn Pipeline et ColumnTransformer
769
+ if hasattr(self._model, "steps") and callable(getattr(self._model, "fit", None)):
770
+ return True
771
+ # Vérifier les pipelines PyTorch (nn.Sequential)
772
+ if hasattr(self._model, "forward") and hasattr(self._model, "_modules"):
773
+ return True
774
+ # Vérifier les pipelines Keras (Sequential)
775
+ if hasattr(self._model, "layers") and hasattr(self._model, "add"):
776
+ return True
777
+ return False
778
+
779
+ def _is_ensemble(self) -> bool:
780
+ """Vérifie si le modèle est un ensemble ou un méta-estimateur."""
781
+ # Ensembles scikit-learn standards
782
+ if hasattr(self._model, "estimators_") or hasattr(self._model, "estimators"):
783
+ return True
784
+ # Autres méta-estimateurs
785
+ if hasattr(self._model, "base_estimator") or hasattr(self._model, "base_estimator_"):
786
+ return True
787
+ # Méta-estimateurs de vote
788
+ if hasattr(self._model, "estimators") and isinstance(getattr(self._model, "estimators", None), list):
789
+ return True
790
+ return False
791
+
792
+ def _is_tree_model(self) -> bool:
793
+ """Vérifie si le modèle est un modèle basé sur des arbres."""
794
+ tree_modules = ['sklearn.ensemble', 'xgboost', 'lightgbm', 'catboost']
795
+ model_module = self._model.__class__.__module__
796
+ return any(module in model_module for module in tree_modules)
797
+
798
+ def _is_deep_model(self) -> bool:
799
+ """Vérifie si le modèle est un réseau de neurones profond."""
800
+ deep_modules = ['keras', 'tensorflow', 'torch']
801
+ model_module = self._model.__class__.__module__
802
+ return any(module in model_module for module in deep_modules)
803
+
804
+ def _is_differentiable_model(self) -> bool:
805
+ """Vérifie si le modèle est différentiable (pour GradientExplainer)."""
806
+ model_type = self._detect_model_type()
807
+ return any(framework in model_type for framework in ['tensorflow', 'pytorch', 'torch'])
808
+
809
+ def _model_predict_wrapper(self, X):
810
+ """
811
+ Wrapper unifié pour obtenir des prédictions standardisées de différents types de modèles.
812
+
813
+ Cette méthode gère intelligemment les spécificités des différents frameworks de ML
814
+ et normalise les formats de sortie pour faciliter les calculs de métriques de qualité.
815
+
816
+ Args:
817
+ X: Données d'entrée (DataFrame, ndarray, etc.)
818
+
819
+ Returns:
820
+ np.ndarray: Prédictions normalisées
821
+ """
822
+ model_type = self._detect_model_type()
823
+
824
+ # Optimisation: utiliser le GPU si configuré
825
+ with self._maybe_use_gpu_context():
826
+ try:
827
+ # Convertir en format approprié si nécessaire
828
+ X_processed = X
829
+ if hasattr(self._model, 'predict') and not ('tensorflow' in model_type or 'pytorch' in model_type):
830
+ # Modèles scikit-learn, XGBoost, etc.
831
+ if hasattr(self._model, 'predict_proba'):
832
+ # Classifier avec probabilités
833
+ predictions = self._model.predict_proba(X_processed)
834
+ else:
835
+ # Régression ou autre
836
+ predictions = self._model.predict(X_processed)
837
+ # Conversion 1D -> 2D si nécessaire
838
+ if predictions.ndim == 1:
839
+ predictions = predictions.reshape(-1, 1)
840
+ elif 'tensorflow' in model_type:
841
+ # Modèles TensorFlow
842
+ import tensorflow as tf
843
+ # Conversion en tenseur TF si nécessaire
844
+ if not isinstance(X_processed, tf.Tensor) and not isinstance(X_processed, tf.Variable):
845
+ X_processed = tf.convert_to_tensor(X_processed, dtype=tf.float32)
846
+ predictions = self._model(X_processed).numpy()
847
+ elif 'pytorch' in model_type or 'torch' in model_type:
848
+ # Modèles PyTorch
849
+ import torch
850
+ # Conversion en tenseur PyTorch si nécessaire
851
+ if not isinstance(X_processed, torch.Tensor):
852
+ X_processed = torch.tensor(X_processed.values if hasattr(X_processed, 'values') else X_processed,
853
+ dtype=torch.float32)
854
+ # Désactiver le calcul de gradient pour l'inférence
855
+ with torch.no_grad():
856
+ predictions = self._model(X_processed).cpu().numpy()
857
+ else:
858
+ # Méthode générique pour les autres cas
859
+ if hasattr(self._model, '__call__'):
860
+ predictions = self._model(X_processed)
861
+ # Convertir en numpy si nécessaire
862
+ if not isinstance(predictions, np.ndarray):
863
+ predictions = np.array(predictions)
864
+ else:
865
+ raise ValueError(f"Type de modèle non supporté pour les prédictions: {model_type}")
866
+
867
+ return predictions
868
+
869
+ except Exception as e:
870
+ self._logger.error(f"Erreur lors de la prédiction avec le modèle: {str(e)}")
871
+ raise RuntimeError(f"Erreur lors de la prédiction: {str(e)}")
872
+
873
+ def _get_background_data(self):
874
+ """
875
+ Récupère les données d'arrière-plan pour les explainers qui en ont besoin.
876
+
877
+ Returns:
878
+ numpy.ndarray: Données d'arrière-plan
879
+ """
880
+ if self._background_data is not None:
881
+ return self._background_data
882
+
883
+ # Données synthétiques si aucune donnée n'est fournie
884
+ # C'est une solution de repli, l'idéal est de fournir de vraies données
885
+ self._logger.warning("Aucune donnée d'arrière-plan fournie. "
886
+ "Génération de données synthétiques, ce qui peut affecter la qualité des explications.")
887
+
888
+ # Tenter de déduire la forme d'entrée du modèle
889
+ input_shape = self._infer_model_input_shape()
890
+ if input_shape:
891
+ # Générer des données aléatoires normalisées
892
+ return np.random.normal(0, 0.1, size=(self._n_samples, *input_shape))
893
+
894
+ # Si impossible de déduire la forme, erreur
895
+ raise ValueError("Impossible de générer des données d'arrière-plan. "
896
+ "Veuillez fournir des données via le paramètre 'background_data'.")
897
+
898
+ def _infer_model_input_shape(self):
899
+ """
900
+ Tente de déduire la forme des entrées du modèle.
901
+
902
+ Returns:
903
+ tuple ou None: Forme déduite ou None si impossible
904
+ """
905
+ model_type = self._detect_model_type()
906
+
907
+ if model_type == 'tensorflow':
908
+ try:
909
+ # Pour les modèles Keras
910
+ return self._model.input_shape[1:]
911
+ except (AttributeError, IndexError):
912
+ pass
913
+
914
+ # Pour les modèles sklearn, xgboost, etc.
915
+ try:
916
+ if hasattr(self._model, 'n_features_in_'):
917
+ return (self._model.n_features_in_,)
918
+ except AttributeError:
919
+ pass
920
+
921
+ # Impossible de déduire
922
+ return None
923
+
924
+ def _model_predict_wrapper(self, x):
925
+ """
926
+ Wrapper pour la fonction de prédiction du modèle, adapté pour SHAP.
927
+
928
+ Args:
929
+ x: Données d'entrée
930
+
931
+ Returns:
932
+ numpy.ndarray: Prédictions du modèle
933
+ """
934
+ try:
935
+ model_type = self._detect_model_type()
936
+
937
+ if model_type in ['sklearn-ensemble', 'sklearn-linear', 'xgboost', 'lightgbm', 'catboost']:
938
+ if hasattr(self._model, 'predict_proba'):
939
+ return self._model.predict_proba(x)
940
+ else:
941
+ return self._model.predict(x)
942
+ elif model_type in ['tensorflow', 'pytorch']:
943
+ # Conversion en format attendu par le modèle
944
+ if isinstance(x, pd.DataFrame):
945
+ x = x.values
946
+
947
+ # Appel au modèle
948
+ result = self._model(x)
949
+
950
+ # Conversion du résultat si nécessaire (pour PyTorch)
951
+ if hasattr(result, 'detach') and hasattr(result.detach(), 'numpy'):
952
+ return result.detach().numpy()
953
+ return result
954
+ else:
955
+ # Cas générique
956
+ if hasattr(self._model, 'predict'):
957
+ return self._model.predict(x)
958
+ else:
959
+ return self._model(x)
960
+ except Exception as e:
961
+ self._logger.error(f"Erreur dans le wrapper de prédiction: {str(e)}")
962
+ raise RuntimeError(f"Échec de la prédiction du modèle: {str(e)}")
963
+
964
+ def explain(self, X, y=None, **kwargs):
965
+ """
966
+ Génère des explications SHAP optimisées pour un ensemble de données avec métriques de qualité.
967
+
968
+ Cette implémentation avancée prend en charge l'explicabilité à grande échelle avec
969
+ des optimisations de performance, des métriques de qualité d'explication, des contrôles
970
+ de conformité réglementaire et une adaptation dynamique au niveau d'audience.
971
+
972
+ Args:
973
+ X: Données d'entrée à expliquer (DataFrame, numpy array, ou liste)
974
+ y: Valeurs cibles réelles (optionnel), utilisées pour évaluer la fidélité
975
+ **kwargs: Paramètres avancés
976
+ output_index: Indice de sortie pour les modèles multi-sorties
977
+ audience_level: Niveau d'audience (AudienceLevel.TECHNICAL, BUSINESS, PUBLIC)
978
+ summarize: Résumer les résultats pour l'ensemble du dataset
979
+ sample_size: Pour grands datasets, taille d'échantillon représentatif à utiliser
980
+ compute_quality_metrics: Calculer les métriques de qualité d'explication (True par défaut)
981
+ verify_compliance: Vérifier la conformité réglementaire de l'explication
982
+ regulations: Liste des réglementations à vérifier (ex: ['RGPD', 'AI_ACT', 'HIPAA'])
983
+ return_raw_shap: Inclure les valeurs SHAP brutes dans le résultat (False par défaut)
984
+ batch_size: Nombre d'instances à traiter simultanément (pour grandes dimensions)
985
+ include_predictions: Inclure les prédictions du modèle avec l'explication
986
+ generate_narratives: Générer des descriptions textuelles des explications
987
+ language: Langue pour les narratives générées ('fr' ou 'en')
988
+
989
+ Returns:
990
+ ExplanationResult: Résultat enrichi avec métriques de qualité et informations de conformité
991
+
992
+ Raises:
993
+ ValueError: Si le format des données est incompatible
994
+ RuntimeError: Si l'explication échoue pour des raisons techniques
995
+ TimeoutError: Si le calcul dépasse le timeout configuré
996
+ """
997
+ # Validation et prétraitement des données
998
+ if self._shap_explainer is None:
999
+ raise ValueError("L'explainer SHAP n'a pas été correctement initialisé.")
1000
+
1001
+ # Extraction des paramètres avancés avec valeurs par défaut
1002
+ output_index = kwargs.get('output_index', None)
1003
+ audience_level = kwargs.get('audience_level', AudienceLevel.TECHNICAL)
1004
+ summarize = kwargs.get('summarize', False)
1005
+ sample_size = kwargs.get('sample_size', None)
1006
+ compute_quality = kwargs.get('compute_quality_metrics', True)
1007
+ verify_compliance = kwargs.get('verify_compliance', self._config.verify_compliance)
1008
+ regulations = kwargs.get('regulations', self._config.compliance_regulations)
1009
+ return_raw_shap = kwargs.get('return_raw_shap', False)
1010
+ batch_size = kwargs.get('batch_size', self._config.batch_size)
1011
+ include_predictions = kwargs.get('include_predictions', True)
1012
+ generate_narratives = kwargs.get('generate_narratives', False)
1013
+ language = kwargs.get('language', 'fr')
1014
+
1015
+ # Gestion des grands jeux de données avec échantillonnage intelligent
1016
+ if sample_size and len(X) > sample_size:
1017
+ self._logger.info(f"Échantillonnage de {sample_size} instances parmi {len(X)} pour performance")
1018
+ if hasattr(X, 'sample'):
1019
+ # Échantillonnage stratifié si y est disponible
1020
+ if y is not None and hasattr(pd, 'DataFrame'):
1021
+ try:
1022
+ # Tentative d'échantillonnage stratifié
1023
+ temp_df = pd.DataFrame(X)
1024
+ temp_df['_target'] = y
1025
+ stratified = temp_df.groupby('_target', group_keys=False).apply(
1026
+ lambda x: x.sample(min(len(x), int(sample_size * len(x) / len(temp_df))))
1027
+ )
1028
+ indices = stratified.index
1029
+ X_sample = X.iloc[indices] if hasattr(X, 'iloc') else X[indices]
1030
+ y_sample = y[indices] if y is not None else None
1031
+ self._logger.debug("Échantillonnage stratifié appliqué")
1032
+ except Exception as e:
1033
+ self._logger.debug(f"Échec de l'échantillonnage stratifié: {str(e)}. Retour à aléatoire.")
1034
+ X_sample = X.sample(sample_size) if hasattr(X, 'sample') else X[np.random.choice(len(X), sample_size, replace=False)]
1035
+ y_sample = y[X_sample.index] if y is not None and hasattr(y, '__getitem__') else None
1036
+ else:
1037
+ # Échantillonnage aléatoire
1038
+ X_sample = X.sample(sample_size)
1039
+ y_sample = y[X_sample.index] if y is not None and hasattr(y, '__getitem__') else None
1040
+ else:
1041
+ # Échantillonnage numpy
1042
+ indices = np.random.choice(len(X), sample_size, replace=False)
1043
+ X_sample = X[indices]
1044
+ y_sample = y[indices] if y is not None and hasattr(y, '__getitem__') else None
1045
+
1046
+ # Utilisation des échantillons à la place des données complètes
1047
+ X, y = X_sample, y_sample
1048
+
1049
+ # Préparation du calcul des valeurs SHAP
1050
+ with Timer() as compute_timer, MemoryTracker() as compute_memory:
1051
+ # Utilisation du cache si activé
1052
+ cache_key = None
1053
+ if self._config.use_cache:
1054
+ try:
1055
+ import hashlib
1056
+ import pickle
1057
+
1058
+ # Génération d'une clé de cache unique pour ces données
1059
+ data_hash = hashlib.md5(pickle.dumps((X, output_index))).hexdigest()
1060
+ model_hash = self._model_hash if hasattr(self, '_model_hash') else hashlib.md5(
1061
+ pickle.dumps(self._extract_model_signature())).hexdigest()
1062
+ cache_key = f"shap_{model_hash}_{data_hash}_{self._config.n_samples}"
1063
+
1064
+ # Tentative de récupération depuis le cache
1065
+ if hasattr(self, '_explanation_cache') and cache_key in self._explanation_cache:
1066
+ self._logger.info("Explication récupérée depuis le cache")
1067
+ shap_values, expected_value, execution_time = self._explanation_cache[cache_key]
1068
+ cached = True
1069
+ except Exception as e:
1070
+ self._logger.debug(f"Erreur lors de l'accès au cache: {str(e)}")
1071
+ cache_key = None
1072
+
1073
+ # Calcul des valeurs SHAP si non trouvées dans le cache
1074
+ if not cache_key or not hasattr(self, '_explanation_cache') or cache_key not in self._explanation_cache:
1075
+ self._logger.info("Calcul des valeurs SHAP en cours...")
1076
+
1077
+ # Traitement par lots pour grands jeux de données
1078
+ if batch_size and len(X) > batch_size:
1079
+ self._logger.info(f"Traitement par lots de {batch_size} instances")
1080
+
1081
+ # Division en lots
1082
+ num_batches = (len(X) + batch_size - 1) // batch_size
1083
+ results = []
1084
+ expected_values = []
1085
+
1086
+ # Paramètres de parallélisation
1087
+ parallel = self._config.n_jobs > 1
1088
+
1089
+ if parallel:
1090
+ from concurrent.futures import ProcessPoolExecutor
1091
+
1092
+ # Fonction pour calcul parallèle des valeurs SHAP par lot
1093
+ def process_batch(batch_idx):
1094
+ start_idx = batch_idx * batch_size
1095
+ end_idx = min(start_idx + batch_size, len(X))
1096
+ batch_X = X[start_idx:end_idx]
1097
+ with self._maybe_use_gpu_context():
1098
+ batch_result = self._shap_explainer.shap_values(
1099
+ batch_X, l1_reg=self._config.l1_reg, output_index=output_index
1100
+ )
1101
+ return batch_result
1102
+
1103
+ try:
1104
+ self._logger.debug(f"Exécution parallèle avec {self._config.n_jobs} workers")
1105
+ with ProcessPoolExecutor(max_workers=self._config.n_jobs) as executor:
1106
+ batch_results = list(executor.map(process_batch, range(num_batches)))
1107
+
1108
+ # Fusion des résultats
1109
+ if isinstance(batch_results[0], list):
1110
+ # Multi-output: list of arrays per class
1111
+ shap_values = [np.vstack([batch[i] for batch in batch_results])
1112
+ for i in range(len(batch_results[0]))]
1113
+ else:
1114
+ # Single output: array per instance
1115
+ shap_values = np.vstack(batch_results)
1116
+ except Exception as e:
1117
+ self._logger.warning(f"Erreur lors du calcul parallèle: {str(e)}. Passage en mode séquentiel.")
1118
+ parallel = False
1119
+
1120
+ # Traitement séquentiel si parallélisation impossible
1121
+ if not parallel:
1122
+ batch_results = []
1123
+ for batch_idx in range(num_batches):
1124
+ self._logger.debug(f"Traitement du lot {batch_idx+1}/{num_batches}")
1125
+ start_idx = batch_idx * batch_size
1126
+ end_idx = min(start_idx + batch_size, len(X))
1127
+ batch_X = X[start_idx:end_idx]
1128
+
1129
+ with self._maybe_use_gpu_context():
1130
+ batch_result = self._shap_explainer.shap_values(
1131
+ batch_X, l1_reg=self._config.l1_reg, output_index=output_index
1132
+ )
1133
+ batch_results.append(batch_result)
1134
+
1135
+ # Fusion des résultats
1136
+ if isinstance(batch_results[0], list):
1137
+ # Multi-output: list of arrays per class
1138
+ shap_values = [np.vstack([batch[i] for batch in batch_results])
1139
+ for i in range(len(batch_results[0]))]
1140
+ else:
1141
+ # Single output: array per instance
1142
+ shap_values = np.vstack(batch_results)
1143
+
1144
+ # Récupération de la valeur attendue
1145
+ if hasattr(self._shap_explainer, 'expected_value'):
1146
+ expected_value = self._shap_explainer.expected_value
1147
+ else:
1148
+ # Calcul manuel si non disponible
1149
+ try:
1150
+ if isinstance(shap_values, list):
1151
+ expected_value = [np.mean(self._model_predict_wrapper(background_data))
1152
+ for _ in range(len(shap_values))]
1153
+ else:
1154
+ expected_value = np.mean(self._model_predict_wrapper(background_data))
1155
+ except Exception as e:
1156
+ self._logger.warning(f"Erreur lors du calcul de expected_value: {str(e)}")
1157
+ expected_value = 0
1158
+ else:
1159
+ # Traitement standard sans lots
1160
+ with self._maybe_use_gpu_context():
1161
+ shap_values = self._shap_explainer.shap_values(
1162
+ X, l1_reg=self._config.l1_reg, output_index=output_index
1163
+ )
1164
+ expected_value = self._shap_explainer.expected_value if hasattr(self._shap_explainer, 'expected_value') else 0
1165
+
1166
+ # Mise en cache des résultats
1167
+ if cache_key:
1168
+ if not hasattr(self, '_explanation_cache'):
1169
+ from functools import lru_cache
1170
+ self._explanation_cache = lru_cache(maxsize=self._config.cache_size)(lambda x: x)({})
1171
+ self._explanation_cache[cache_key] = (shap_values, expected_value, compute_timer.duration)
1172
+
1173
+ # Métriques de qualité d'explication
1174
+ quality_metrics = {}
1175
+ if compute_quality:
1176
+ try:
1177
+ # Calcul des métriques de qualité
1178
+ quality_metrics = self._compute_explanation_quality(X, y, shap_values, expected_value)
1179
+ except Exception as e:
1180
+ self._logger.warning(f"Erreur lors du calcul des métriques de qualité: {str(e)}")
1181
+
1182
+ # Vérification de la conformité réglementaire
1183
+ compliance_results = None
1184
+ if verify_compliance and hasattr(self, '_compliance_checker'):
1185
+ try:
1186
+ compliance_results = self._compliance_checker.check_explanation(
1187
+ model=self._model,
1188
+ data=X,
1189
+ shap_values=shap_values,
1190
+ explainer=self,
1191
+ regulations=regulations
1192
+ )
1193
+ except Exception as e:
1194
+ self._logger.warning(f"Erreur lors de la vérification de conformité: {str(e)}")
1195
+
1196
+ # Génération des narratives explicatives
1197
+ narrative = None
1198
+ if generate_narratives:
1199
+ try:
1200
+ narrative = self._generate_explanation_narrative(
1201
+ shap_values, X, audience_level=audience_level, language=language
1202
+ )
1203
+ except Exception as e:
1204
+ self._logger.warning(f"Erreur lors de la génération de narratives: {str(e)}")
1205
+
1206
+ # Extraction des métadonnées complètes
1207
+ metadata = self._extract_metadata()
1208
+ metadata.update({
1209
+ 'computation_time_seconds': compute_timer.duration,
1210
+ 'memory_usage_mb': compute_memory.peak_usage_mb,
1211
+ 'sample_size': len(X),
1212
+ 'quality_metrics': quality_metrics,
1213
+ 'audience_level': audience_level,
1214
+ 'computation_mode': 'parallel' if self._config.n_jobs > 1 else 'sequential',
1215
+ 'batch_processing': bool(batch_size and len(X) > batch_size),
1216
+ 'cache_hit': bool(cache_key and hasattr(self, '_explanation_cache') and cache_key in self._explanation_cache)
1217
+ })
1218
+
1219
+ # Création du résultat d'explication
1220
+ result = self._create_explanation_result(
1221
+ shap_values=shap_values,
1222
+ expected_value=expected_value,
1223
+ X=X,
1224
+ y=y if include_predictions else None,
1225
+ metadata=metadata,
1226
+ compliance=compliance_results,
1227
+ narrative=narrative,
1228
+ raw_shap=shap_values if return_raw_shap else None
1229
+ )
1230
+
1231
+ # Conversion des données en format approprié
1232
+ if isinstance(X, pd.DataFrame):
1233
+ feature_names = X.columns.tolist()
1234
+ X_values = X.values
1235
+ else:
1236
+ X_values = X
1237
+ feature_names = kwargs.get('feature_names',
1238
+ [f"feature_{i}" for i in range(X_values.shape[1])])
1239
+
1240
+ # Limiter le nombre d'échantillons pour performance si nécessaire
1241
+ if X_values.shape[0] > max_samples:
1242
+ self._logger.warning(f"Échantillonnage de {max_samples} instances sur {X_values.shape[0]} pour performance.")
1243
+ indices = np.random.choice(X_values.shape[0], max_samples, replace=False)
1244
+ X_sample = X_values[indices]
1245
+ else:
1246
+ X_sample = X_values
1247
+
1248
+ # Tracer l'action
1249
+ self.add_audit_record("explain", {
1250
+ "n_samples": X_sample.shape[0],
1251
+ "n_features": X_sample.shape[1],
1252
+ "audience_level": audience_level.value if isinstance(audience_level, AudienceLevel) else audience_level,
1253
+ "include_interaction_values": include_interaction_values,
1254
+ "summarize": summarize
1255
+ })
1256
+
1257
+ try:
1258
+ # Calculer les valeurs SHAP
1259
+ shap_values = self._shap_explainer.shap_values(X_sample)
1260
+
1261
+ # Gérer les différents formats de sortie selon le type d'explainer SHAP
1262
+ if isinstance(shap_values, list):
1263
+ # Cas multi-classe: une liste de tableaux
1264
+ if output_indices is not None:
1265
+ if isinstance(output_indices, int):
1266
+ output_indices = [output_indices]
1267
+ shap_values = [shap_values[i] for i in output_indices]
1268
+
1269
+ # Calculer les valeurs d'interaction si demandé
1270
+ interaction_values = None
1271
+ if include_interaction_values and hasattr(self._shap_explainer, 'shap_interaction_values'):
1272
+ try:
1273
+ interaction_values = self._shap_explainer.shap_interaction_values(X_sample)
1274
+ except Exception as e:
1275
+ self._logger.warning(f"Impossible de calculer les valeurs d'interaction: {str(e)}")
1276
+
1277
+ # Préparer les importances de caractéristiques
1278
+ feature_importances = []
1279
+
1280
+ # Cas où on résume les résultats pour l'ensemble du dataset
1281
+ if summarize:
1282
+ if isinstance(shap_values, list):
1283
+ # Moyenne des valeurs absolues pour chaque classe
1284
+ global_importances = np.zeros(X_sample.shape[1])
1285
+ for sv in shap_values:
1286
+ global_importances += np.mean(np.abs(sv), axis=0)
1287
+ global_importances /= len(shap_values)
1288
+ else:
1289
+ # Moyenne des valeurs absolues
1290
+ global_importances = np.mean(np.abs(shap_values), axis=0)
1291
+
1292
+ # Créer les objets FeatureImportance
1293
+ for i, (name, importance) in enumerate(zip(feature_names, global_importances)):
1294
+ feature_importances.append(FeatureImportance(
1295
+ feature_name=name,
1296
+ importance=float(importance),
1297
+ # Ajouter des statistiques si disponibles
1298
+ std_dev=float(np.std(np.abs(shap_values[0][:, i])) if isinstance(shap_values, list)
1299
+ else np.std(np.abs(shap_values[:, i])))
1300
+ ))
1301
+
1302
+ # Extraire les métadonnées du modèle
1303
+ if not self._metadata:
1304
+ self._extract_metadata()
1305
+
1306
+ # Créer le résultat d'explication
1307
+ result = ExplanationResult(
1308
+ method=ExplainabilityMethod.SHAP,
1309
+ model_metadata=self._metadata,
1310
+ feature_importances=feature_importances,
1311
+ raw_explanation={
1312
+ "shap_values": shap_values,
1313
+ "interaction_values": interaction_values,
1314
+ "feature_names": feature_names,
1315
+ "data": X_sample
1316
+ },
1317
+ audience_level=audience_level
1318
+ )
1319
+
1320
+ return result
1321
+
1322
+ except Exception as e:
1323
+ self._logger.error(f"Erreur lors du calcul des valeurs SHAP: {str(e)}")
1324
+ raise RuntimeError(f"Échec de l'explication SHAP: {str(e)}")
1325
+
1326
+ def explain_instance(self, instance, **kwargs) -> ExplanationResult:
1327
+ """
1328
+ Explique une instance spécifique avec explications SHAP optimisées et enrichies.
1329
+
1330
+ Cette méthode implémente une explication avancée d'instance individuelle avec:
1331
+ - Optimisations de performance (GPU, cache)
1332
+ - Métriques de qualité d'explication
1333
+ - Conformité réglementaire
1334
+ - Générations de narratives explicatives adaptées à l'audience
1335
+ - Métadonnées complètes et traces d'audit
1336
+
1337
+ Args:
1338
+ instance: Instance à expliquer (array, liste, dict ou pandas.Series)
1339
+ **kwargs: Paramètres additionnels
1340
+ - output_index: Indice de la sortie à expliquer pour les modèles multi-sorties
1341
+ - audience_level: Niveau d'audience (TECHNICAL, BUSINESS, PUBLIC)
1342
+ - language: Langue des narratives ('fr', 'en')
1343
+ - include_interaction_values: Calculer les valeurs d'interaction entre caractéristiques
1344
+ - check_compliance: Vérifier la conformité réglementaire
1345
+ - compliance_regs: Liste des réglementations à vérifier
1346
+ - compute_metrics: Calculer des métriques de qualité d'explication
1347
+ - return_raw_shap: Renvoyer les valeurs SHAP brutes (potentiellement volumineuses)
1348
+ - generate_narratives: Générer des descriptions textuelles des explications
1349
+ - include_predictions: Inclure les prédictions du modèle dans le résultat
1350
+ - feature_names: Noms des caractéristiques (si non disponibles dans les données)
1351
+
1352
+ Returns:
1353
+ ExplanationResult: Résultat standardisé et enrichi de l'explication
1354
+
1355
+ Raises:
1356
+ ValueError: Si le format d'instance n'est pas supporté
1357
+ RuntimeError: Si l'explication échoue pour des raisons techniques
1358
+ """
1359
+ # Convertir l'instance en format approprié
1360
+ if isinstance(instance, dict):
1361
+ # Convertir dict en DataFrame
1362
+ instance_df = pd.DataFrame([instance])
1363
+ feature_names = list(instance.keys())
1364
+ elif isinstance(instance, pd.Series):
1365
+ instance_df = pd.DataFrame([instance])
1366
+ feature_names = instance.index.tolist()
1367
+ elif isinstance(instance, (list, np.ndarray)):
1368
+ instance_array = np.array(instance).reshape(1, -1)
1369
+ instance_df = pd.DataFrame(instance_array)
1370
+ feature_names = kwargs.get('feature_names',
1371
+ [f"feature_{i}" for i in range(instance_array.shape[1])])
1372
+ else:
1373
+ raise ValueError("Format d'instance non supporté. Utilisez un dict, pandas.Series, liste ou numpy.ndarray.")
1374
+
1375
+ # Extraction des paramètres avancés
1376
+ output_index = kwargs.get('output_index', None)
1377
+ audience_level = kwargs.get('audience_level', AudienceLevel.TECHNICAL)
1378
+ language = kwargs.get('language', 'fr')
1379
+ include_interaction_values = kwargs.get('include_interaction_values', False)
1380
+ check_compliance = kwargs.get('check_compliance', self._config.compliance_mode)
1381
+ compliance_regs = kwargs.get('compliance_regs', self._config.compliance_regs)
1382
+ compute_metrics = kwargs.get('compute_metrics', self._config.compute_metrics)
1383
+ return_raw_shap = kwargs.get('return_raw_shap', False)
1384
+ generate_narratives = kwargs.get('generate_narratives', True)
1385
+ include_predictions = kwargs.get('include_predictions', True)
1386
+
1387
+ # Préparation du cache et des métriques de performance
1388
+ compute_timer = Timer()
1389
+ compute_memory = MemoryTracker()
1390
+ cache_key = None
1391
+
1392
+ # Création d'une clé de cache unique pour cette instance
1393
+ if hasattr(self, '_explanation_cache'):
1394
+ try:
1395
+ # Génère une clé de hachage unique pour cette instance
1396
+ instance_hash = hashlib.md5(pickle.dumps(instance_df)).hexdigest()
1397
+ params_hash = hashlib.md5(str({
1398
+ 'output_index': output_index,
1399
+ 'audience_level': audience_level,
1400
+ 'include_interaction_values': include_interaction_values
1401
+ }).encode()).hexdigest()
1402
+ cache_key = f"instance_{instance_hash}_{params_hash}"
1403
+
1404
+ # Vérifie si l'explication est déjà en cache
1405
+ if cache_key in self._explanation_cache:
1406
+ self._logger.info("Explication récupérée du cache")
1407
+ cached_result = self._explanation_cache[cache_key]
1408
+ # Mise à jour des métadonnées pour refléter l'utilisation du cache
1409
+ if isinstance(cached_result, ExplanationResult) and hasattr(cached_result, 'metadata'):
1410
+ cached_result.metadata['cache_hit'] = True
1411
+ cached_result.metadata['computation_time_seconds'] = 0.0
1412
+ return cached_result
1413
+ except Exception as e:
1414
+ self._logger.warning(f"Erreur lors de l'accès au cache: {str(e)}")
1415
+ cache_key = None
1416
+
1417
+ # Tracer l'action avec paramètres enrichis
1418
+ self.add_audit_record("explain_instance", {
1419
+ "n_features": len(feature_names),
1420
+ "audience_level": audience_level.value if isinstance(audience_level, AudienceLevel) else audience_level,
1421
+ "include_interaction_values": include_interaction_values,
1422
+ "check_compliance": check_compliance,
1423
+ "compute_metrics": compute_metrics,
1424
+ "language": language,
1425
+ "use_gpu": self._config.use_gpu
1426
+ })
1427
+
1428
+ compute_timer.start()
1429
+ compute_memory.start_tracking()
1430
+
1431
+ try:
1432
+ # Récupération de la valeur attendue (baseline) de l'explainer
1433
+ expected_value = self._shap_explainer.expected_value
1434
+ if isinstance(expected_value, np.ndarray):
1435
+ expected_value = expected_value.tolist()
1436
+ elif isinstance(expected_value, list) and all(isinstance(x, np.ndarray) for x in expected_value):
1437
+ expected_value = [x.tolist() for x in expected_value]
1438
+
1439
+ # Utilisation du contexte GPU si configuré
1440
+ with self._maybe_use_gpu_context():
1441
+ # Calculer les valeurs SHAP avec optimisations
1442
+ shap_values = self._shap_explainer.shap_values(instance_df)
1443
+
1444
+ # Gérer les différents formats de sortie selon le type d'explainer SHAP
1445
+ if isinstance(shap_values, list):
1446
+ # Cas multi-classe: une liste de tableaux
1447
+ if output_index is not None:
1448
+ shap_values = shap_values[output_index]
1449
+ else:
1450
+ # Prendre la classe avec la probabilité maximale
1451
+ predictions = self._model_predict_wrapper(instance_df)
1452
+ if predictions.ndim > 1 and predictions.shape[1] > 1:
1453
+ output_index = np.argmax(predictions[0])
1454
+ shap_values = shap_values[output_index]
1455
+ else:
1456
+ # Cas binaire, prendre la classe positive
1457
+ shap_values = shap_values[1] if len(shap_values) > 1 else shap_values[0]
1458
+
1459
+ # Calcul des valeurs d'interaction si demandé
1460
+ interaction_values = None
1461
+ if include_interaction_values and hasattr(self._shap_explainer, 'shap_interaction_values'):
1462
+ try:
1463
+ with self._maybe_use_gpu_context():
1464
+ interaction_values = self._shap_explainer.shap_interaction_values(instance_df)
1465
+ if isinstance(interaction_values, list) and output_index is not None:
1466
+ interaction_values = interaction_values[output_index]
1467
+ except Exception as e:
1468
+ self._logger.warning(f"Impossible de calculer les valeurs d'interaction: {str(e)}")
1469
+
1470
+ # Génération des prédictions du modèle si demandé
1471
+ y_pred = None
1472
+ if include_predictions:
1473
+ try:
1474
+ y_pred = self._model_predict_wrapper(instance_df)
1475
+ except Exception as e:
1476
+ self._logger.warning(f"Impossible de générer les prédictions: {str(e)}")
1477
+
1478
+ # Métriques de qualité d'explication
1479
+ quality_metrics = {}
1480
+ if compute_metrics:
1481
+ try:
1482
+ # Pour une instance unique, nous calculons des métriques spécifiques à l'instance
1483
+ quality_metrics = {
1484
+ 'local_fidelity': self._compute_local_fidelity(instance_df, shap_values, expected_value),
1485
+ 'feature_sparsity': self._gini_index(np.abs(shap_values)),
1486
+ }
1487
+
1488
+ # Si des prédictions ont été faites, ajout de métriques supplémentaires
1489
+ if y_pred is not None:
1490
+ quality_metrics['prediction_impact'] = self._compute_prediction_impact(shap_values, expected_value, y_pred)
1491
+ except Exception as e:
1492
+ self._logger.warning(f"Erreur lors du calcul des métriques de qualité: {str(e)}")
1493
+
1494
+ # Vérification de conformité
1495
+ compliance_results = None
1496
+ if check_compliance and hasattr(self, '_compliance_checker'):
1497
+ try:
1498
+ compliance_results = self._compliance_checker.check_explanation(
1499
+ shap_values=shap_values,
1500
+ data=instance_df,
1501
+ model=self._model,
1502
+ regulations=compliance_regs
1503
+ )
1504
+ except Exception as e:
1505
+ self._logger.warning(f"Erreur lors de la vérification de conformité: {str(e)}")
1506
+
1507
+ # Génération des narratives explicatives
1508
+ narrative = None
1509
+ if generate_narratives:
1510
+ try:
1511
+ narrative = self._generate_explanation_narrative(
1512
+ shap_values, instance_df, audience_level=audience_level, language=language
1513
+ )
1514
+ except Exception as e:
1515
+ self._logger.warning(f"Erreur lors de la génération de narratives: {str(e)}")
1516
+
1517
+ # Arrêt du minuteur et du suivi mémoire
1518
+ compute_timer.stop()
1519
+ compute_memory.stop_tracking()
1520
+
1521
+ # Extraction des métadonnées complètes
1522
+ metadata = self._extract_metadata()
1523
+ metadata.update({
1524
+ 'computation_time_seconds': compute_timer.duration,
1525
+ 'memory_usage_mb': compute_memory.peak_usage_mb,
1526
+ 'quality_metrics': quality_metrics,
1527
+ 'audience_level': audience_level,
1528
+ 'computation_mode': 'gpu' if self._config.use_gpu else 'cpu',
1529
+ 'cache_hit': False,
1530
+ 'is_single_instance': True
1531
+ })
1532
+
1533
+ # Créer le résultat d'explication enrichi en utilisant notre méthode avancée
1534
+ result = self._create_explanation_result(
1535
+ shap_values=shap_values,
1536
+ expected_value=expected_value,
1537
+ X=instance_df,
1538
+ y=y_pred if include_predictions else None,
1539
+ metadata=metadata,
1540
+ compliance=compliance_results,
1541
+ narrative=narrative,
1542
+ raw_shap=shap_values if return_raw_shap else None,
1543
+ interaction_values=interaction_values if include_interaction_values else None,
1544
+ feature_names=feature_names,
1545
+ output_index=output_index
1546
+ )
1547
+
1548
+ # Mise en cache du résultat si possible
1549
+ if cache_key and hasattr(self, '_explanation_cache'):
1550
+ try:
1551
+ self._explanation_cache[cache_key] = result
1552
+ except Exception as e:
1553
+ self._logger.warning(f"Erreur lors de la mise en cache du résultat: {str(e)}")
1554
+
1555
+ return result
1556
+
1557
+ except Exception as e:
1558
+ self._logger.error(f"Erreur lors du calcul des valeurs SHAP pour l'instance: {str(e)}")
1559
+ raise RuntimeError(f"Échec de l'explication SHAP pour l'instance: {str(e)}")
1560
+
1561
+ def _compute_local_fidelity(self, X, shap_values, expected_value):
1562
+ """
1563
+ Calcule la fidélité locale de l'explication SHAP pour une instance.
1564
+
1565
+ La fidélité locale mesure comment les attributions SHAP expliquent
1566
+ effectivement la différence entre la prédiction du modèle et la valeur de référence.
1567
+
1568
+ Args:
1569
+ X: Instance à expliquer (DataFrame ou ndarray)
1570
+ shap_values: Valeurs SHAP calculées
1571
+ expected_value: Valeur attendue (baseline)
1572
+
1573
+ Returns:
1574
+ float: Score de fidélité locale (0-1)
1575
+ """
1576
+ try:
1577
+ # Prédiction du modèle pour l'instance
1578
+ prediction = self._model_predict_wrapper(X)
1579
+
1580
+ # Format des données selon le type de sortie
1581
+ if hasattr(prediction, 'shape') and len(prediction.shape) > 1 and prediction.shape[1] > 1:
1582
+ # Classification multi-classes
1583
+ if isinstance(shap_values, list):
1584
+ # Vérifier la somme des valeurs SHAP pour chaque classe
1585
+ class_fidelities = []
1586
+ for i, sv in enumerate(shap_values):
1587
+ if sv.ndim > 1:
1588
+ sv_sum = sv[0].sum()
1589
+ else:
1590
+ sv_sum = sv.sum()
1591
+
1592
+ ev = expected_value[i] if isinstance(expected_value, list) else expected_value
1593
+ pred = prediction[0, i] if prediction.ndim > 1 else prediction[i]
1594
+
1595
+ # Différence entre la prédiction réelle et la prédiction explicable
1596
+ fidelity = 1.0 - min(1.0, abs(pred - (ev + sv_sum)) / max(0.01, abs(pred)))
1597
+ class_fidelities.append(fidelity)
1598
+
1599
+ # Moyenne des fidélités par classe
1600
+ return np.mean(class_fidelities)
1601
+ else:
1602
+ # Cas binaire avec shap_values pour classe positive
1603
+ if shap_values.ndim > 1:
1604
+ sv_sum = shap_values[0].sum()
1605
+ else:
1606
+ sv_sum = shap_values.sum()
1607
+
1608
+ ev = expected_value[1] if isinstance(expected_value, list) else expected_value
1609
+ pred = prediction[0, 1] if prediction.ndim > 1 else prediction[1]
1610
+
1611
+ return 1.0 - min(1.0, abs(pred - (ev + sv_sum)) / max(0.01, abs(pred)))
1612
+ else:
1613
+ # Régression ou classification binaire (sortie simple)
1614
+ if shap_values.ndim > 1:
1615
+ sv_sum = shap_values[0].sum()
1616
+ else:
1617
+ sv_sum = shap_values.sum()
1618
+
1619
+ ev = expected_value[0] if isinstance(expected_value, list) else expected_value
1620
+ pred = prediction[0] if hasattr(prediction, '__len__') else prediction
1621
+
1622
+ return 1.0 - min(1.0, abs(pred - (ev + sv_sum)) / max(0.01, abs(pred)))
1623
+ except Exception as e:
1624
+ self._logger.warning(f"Erreur lors du calcul de la fidélité locale: {str(e)}")
1625
+ return None
1626
+
1627
+ def _compute_prediction_impact(self, shap_values, expected_value, prediction):
1628
+ """
1629
+ Évalue l'impact des attributions sur la prédiction du modèle.
1630
+
1631
+ Cette méthode détermine dans quelle mesure les attributions SHAP
1632
+ influencent substantiellement la prédiction finale par rapport à la valeur de référence.
1633
+
1634
+ Args:
1635
+ shap_values: Valeurs SHAP calculées
1636
+ expected_value: Valeur attendue (baseline)
1637
+ prediction: Prédiction du modèle
1638
+
1639
+ Returns:
1640
+ float: Ratio d'impact (0-1) indiquant l'importance relative des attributions
1641
+ """
1642
+ try:
1643
+ # Gérer les formats de données diverses
1644
+ if isinstance(shap_values, list):
1645
+ # Classification multi-classes
1646
+ impacts = []
1647
+ for i, sv in enumerate(shap_values):
1648
+ sv_sum = np.abs(sv).sum() if sv.ndim == 1 else np.abs(sv[0]).sum()
1649
+ pred_val = prediction[0, i] if prediction.ndim > 1 else prediction[i]
1650
+ baseline = expected_value[i] if isinstance(expected_value, list) else expected_value
1651
+
1652
+ # Impact normalisé: contrib des attributions / écart total à la baseline
1653
+ impacts.append(sv_sum / max(0.001, abs(pred_val - baseline) + sv_sum))
1654
+ return np.mean(impacts)
1655
+ else:
1656
+ # Régression ou classification binaire
1657
+ sv_sum = np.abs(shap_values).sum() if shap_values.ndim == 1 else np.abs(shap_values[0]).sum()
1658
+ pred_val = prediction[0] if hasattr(prediction, '__len__') else prediction
1659
+ baseline = expected_value[0] if isinstance(expected_value, list) else expected_value
1660
+
1661
+ return sv_sum / max(0.001, abs(pred_val - baseline) + sv_sum)
1662
+ except Exception as e:
1663
+ self._logger.warning(f"Erreur lors du calcul de l'impact prédictif: {str(e)}")
1664
+ return None
1665
+
1666
+ def _compute_explanation_quality(self, X, y, shap_values, expected_value):
1667
+ """
1668
+ Calcule des métriques avancées de qualité pour évaluer la fiabilité des explications.
1669
+
1670
+ Cette méthode implémente un ensemble de métriques pour quantifier divers aspects
1671
+ de la qualité d'explication tels que:
1672
+ - Fidélité: Comment les valeurs SHAP prédisent correctement les sorties du modèle
1673
+ - Stabilité: Cohérence des explications sur des variations minimales des données
1674
+ - Sparsité: Concentration des attributions sur un sous-ensemble de caractéristiques
1675
+
1676
+ Args:
1677
+ X: Données d'entrée
1678
+ y: Valeurs cibles réelles ou prédites
1679
+ shap_values: Valeurs SHAP calculées
1680
+ expected_value: Valeur attendue du modèle
1681
+
1682
+ Returns:
1683
+ dict: Dictionnaire de métriques de qualité avec leurs valeurs
1684
+ """
1685
+ metrics = {}
1686
+
1687
+ try:
1688
+ # Conversion en numpy pour calculs homogènes
1689
+ X_np = X.values if hasattr(X, 'values') else np.array(X)
1690
+
1691
+ # 1. Métrique de fidélité (corrélation prédiction-explication)
1692
+ if isinstance(shap_values, list):
1693
+ # Multi-class case
1694
+ predictions = self._model_predict_wrapper(X)
1695
+ if predictions.ndim > 1 and predictions.shape[1] > 1:
1696
+ # Prendre la classe maximale pour chaque prédiction
1697
+ pred_class = np.argmax(predictions, axis=1)
1698
+ # Extraire les valeurs SHAP correspondantes à la classe prédite
1699
+ class_shap = np.array([shap_values[pred_class[i]][i].sum() for i in range(len(X_np))])
1700
+
1701
+ # Calculer la corrélation
1702
+ if hasattr(y, 'values'):
1703
+ y_np = y.values
1704
+ else:
1705
+ y_np = np.array(y) if y is not None else pred_class
1706
+
1707
+ fidelity_score = np.corrcoef(class_shap, predictions.max(axis=1))[0, 1]
1708
+ metrics['fidelity_correlation'] = float(fidelity_score)
1709
+ else:
1710
+ # Regression/binary case
1711
+ predictions = self._model_predict_wrapper(X)
1712
+ if predictions.ndim > 1 and predictions.shape[1] > 1:
1713
+ # Binary with probability output
1714
+ predictions = predictions[:, 1] # Take positive class probability
1715
+
1716
+ # Sum des valeurs SHAP + expected value devrait être proche de la prédiction
1717
+ if isinstance(shap_values, np.ndarray):
1718
+ shap_sum = shap_values.sum(axis=1)
1719
+ if isinstance(expected_value, (list, np.ndarray)):
1720
+ expected_scalar = expected_value[0] if len(expected_value) > 0 else 0
1721
+ else:
1722
+ expected_scalar = expected_value
1723
+
1724
+ shap_predictions = shap_sum + expected_scalar
1725
+ fidelity_score = np.corrcoef(shap_predictions.flatten(), predictions.flatten())[0, 1]
1726
+ metrics['fidelity_correlation'] = float(fidelity_score)
1727
+
1728
+ # RMSE entre prédictions et reconstruction SHAP
1729
+ fidelity_rmse = np.sqrt(np.mean((shap_predictions.flatten() - predictions.flatten()) ** 2))
1730
+ metrics['fidelity_rmse'] = float(fidelity_rmse)
1731
+
1732
+ # 2. Métrique de stabilité (variance des explications)
1733
+ if isinstance(shap_values, list):
1734
+ avg_variance = np.mean([np.var(sv, axis=0).mean() for sv in shap_values])
1735
+ else:
1736
+ avg_variance = np.var(shap_values, axis=0).mean()
1737
+ metrics['stability_variance'] = float(avg_variance)
1738
+
1739
+ # 3. Métrique de sparsité (concentration des attributions)
1740
+ if isinstance(shap_values, list):
1741
+ avg_gini = np.mean([self._gini_index(np.abs(sv).mean(axis=0)) for sv in shap_values])
1742
+ else:
1743
+ feature_importance = np.abs(shap_values).mean(axis=0)
1744
+ avg_gini = self._gini_index(feature_importance)
1745
+ metrics['sparsity_gini'] = float(avg_gini)
1746
+
1747
+ # 4. Proportion d'attributions nulles ou négligeables
1748
+ threshold = 0.01 * (shap_values[0].max() if isinstance(shap_values, list) else shap_values.max())
1749
+ if isinstance(shap_values, list):
1750
+ avg_sparsity = np.mean([np.mean(np.abs(sv) < threshold) for sv in shap_values])
1751
+ else:
1752
+ avg_sparsity = np.mean(np.abs(shap_values) < threshold)
1753
+ metrics['feature_sparsity'] = float(avg_sparsity)
1754
+
1755
+ except Exception as e:
1756
+ self._logger.warning(f"Erreur lors du calcul des métriques de qualité: {str(e)}")
1757
+
1758
+ return metrics
1759
+
1760
+ def _generate_explanation_narrative(self, shap_values, X, audience_level=AudienceLevel.TECHNICAL, language='fr'):
1761
+ """
1762
+ Génère des descriptions textuelles des explications SHAP adaptées au niveau d'audience.
1763
+
1764
+ Cette méthode traduit les valeurs SHAP complexes en narratives explicatives claires
1765
+ qui s'adaptent automatiquement au niveau de technicité requis par l'audience.
1766
+
1767
+ Args:
1768
+ shap_values: Les valeurs SHAP calculées
1769
+ X: Données expliquées
1770
+ audience_level: Niveau d'audience ('TECHNICAL', 'BUSINESS', 'PUBLIC')
1771
+ language: Langue des narratives ('fr' ou 'en')
1772
+
1773
+ Returns:
1774
+ dict: Narratives explicatives structurées par niveau et fonction
1775
+ """
1776
+ narratives = {}
1777
+ feature_names = X.columns.tolist() if hasattr(X, 'columns') else [f'feature_{i}' for i in range(X.shape[1])]
1778
+
1779
+ try:
1780
+ # Calcul des importances globales moyennes
1781
+ if isinstance(shap_values, list):
1782
+ # Multi-class
1783
+ global_importance = np.mean([np.abs(sv).mean(axis=0) for sv in shap_values], axis=0)
1784
+ classes = len(shap_values)
1785
+ else:
1786
+ # Binary/Regression
1787
+ global_importance = np.abs(shap_values).mean(axis=0)
1788
+ classes = 1
1789
+
1790
+ # Tri des caractéristiques par importance
1791
+ sorted_idx = np.argsort(-global_importance)
1792
+ top_features = [feature_names[idx] for idx in sorted_idx[:5]] # Top 5 features
1793
+ top_importance = global_importance[sorted_idx[:5]]
1794
+ top_importance_norm = top_importance / top_importance.sum() * 100 # Normalisation en %
1795
+
1796
+ # Génération des narratives selon le niveau d'audience
1797
+ if language == 'fr':
1798
+ if audience_level == AudienceLevel.TECHNICAL:
1799
+ # Version technique détaillée
1800
+ narratives['summary'] = f"L'analyse SHAP a identifié {len(feature_names)} variables explicatives pour ce modèle. "
1801
+ narratives['main_drivers'] = f"Les principaux facteurs contributifs sont {', '.join(top_features[:3])}, "
1802
+ narratives['main_drivers'] += f"avec des importances relatives de {', '.join([f'{v:.1f}%' for v in top_importance_norm[:3]])}."
1803
+
1804
+ # Détails techniques
1805
+ narratives['technical_details'] = f"L'indice de Gini des attributions SHAP est de {self._gini_index(global_importance):.3f}, "
1806
+ narratives['technical_details'] += "indiquant "
1807
+ if self._gini_index(global_importance) > 0.7:
1808
+ narratives['technical_details'] += "une forte concentration des effets sur un petit nombre de variables."
1809
+ elif self._gini_index(global_importance) > 0.4:
1810
+ narratives['technical_details'] += "une distribution moyennement concentrée des effets sur les variables."
1811
+ else:
1812
+ narratives['technical_details'] += "une distribution relativement équilibrée des effets sur l'ensemble des variables."
1813
+
1814
+ # Interactions (si disponible)
1815
+ if hasattr(self, '_last_interaction_values') and self._last_interaction_values is not None:
1816
+ narratives['interactions'] = "Des effets d'interaction significatifs ont été détectés entre certaines variables."
1817
+
1818
+ elif audience_level == AudienceLevel.BUSINESS:
1819
+ # Version intermédiaire orientée métier
1820
+ narratives['summary'] = f"Ce modèle utilise {len(feature_names)} variables pour établir ses prédictions."
1821
+ narratives['main_drivers'] = f"Les 3 facteurs les plus influents sont {', '.join(top_features[:3])}."
1822
+ narratives['business_impact'] = "Ces facteurs représentent "
1823
+ total_pct = sum(top_importance_norm[:3])
1824
+ narratives['business_impact'] += f"ensemble {total_pct:.1f}% de l'impact total sur les décisions du modèle."
1825
+
1826
+ # Recommandation métier
1827
+ if total_pct > 70:
1828
+ narratives['recommendation'] = "Recommandation: Concentrez votre attention sur ces facteurs clés qui dominent la décision."
1829
+ else:
1830
+ narratives['recommendation'] = "Recommandation: Tenez compte de l'ensemble des facteurs qui contribuent de manière significative aux décisions."
1831
+
1832
+ else: # PUBLIC
1833
+ # Version simplifiée grand public
1834
+ narratives['summary'] = "Voici une explication simplifiée des résultats de ce modèle."
1835
+ narratives['main_factors'] = f"Les principaux éléments qui ont influencé ce résultat sont {', '.join(top_features[:3])}."
1836
+ if len(top_features) > 3:
1837
+ narratives['additional_info'] = f"D'autres facteurs comme {', '.join(top_features[3:5])} ont également joué un rôle, mais moins important."
1838
+ else: # English
1839
+ if audience_level == AudienceLevel.TECHNICAL:
1840
+ # Technical detailed version
1841
+ narratives['summary'] = f"SHAP analysis identified {len(feature_names)} explanatory variables for this model. "
1842
+ narratives['main_drivers'] = f"The main contributing factors are {', '.join(top_features[:3])}, "
1843
+ narratives['main_drivers'] += f"with relative importances of {', '.join([f'{v:.1f}%' for v in top_importance_norm[:3]])}."
1844
+
1845
+ # Technical details
1846
+ narratives['technical_details'] = f"The Gini index of SHAP attributions is {self._gini_index(global_importance):.3f}, "
1847
+ narratives['technical_details'] += "indicating "
1848
+ if self._gini_index(global_importance) > 0.7:
1849
+ narratives['technical_details'] += "a high concentration of effects on a small number of variables."
1850
+ elif self._gini_index(global_importance) > 0.4:
1851
+ narratives['technical_details'] += "a moderately concentrated distribution of effects across variables."
1852
+ else:
1853
+ narratives['technical_details'] += "a relatively balanced distribution of effects across all variables."
1854
+
1855
+ # Interactions (if available)
1856
+ if hasattr(self, '_last_interaction_values') and self._last_interaction_values is not None:
1857
+ narratives['interactions'] = "Significant interaction effects were detected between certain variables."
1858
+
1859
+ elif audience_level == AudienceLevel.BUSINESS:
1860
+ # Intermediate business-oriented version
1861
+ narratives['summary'] = f"This model uses {len(feature_names)} variables to make its predictions."
1862
+ narratives['main_drivers'] = f"The 3 most influential factors are {', '.join(top_features[:3])}."
1863
+ narratives['business_impact'] = "These factors together represent "
1864
+ total_pct = sum(top_importance_norm[:3])
1865
+ narratives['business_impact'] += f"{total_pct:.1f}% of the total impact on model decisions."
1866
+
1867
+ # Business recommendation
1868
+ if total_pct > 70:
1869
+ narratives['recommendation'] = "Recommendation: Focus your attention on these key factors that dominate the decision."
1870
+ else:
1871
+ narratives['recommendation'] = "Recommendation: Consider all factors that contribute significantly to decisions."
1872
+
1873
+ else: # PUBLIC
1874
+ # Simplified public version
1875
+ narratives['summary'] = "Here is a simplified explanation of this model's results."
1876
+ narratives['main_factors'] = f"The main elements that influenced this result are {', '.join(top_features[:3])}."
1877
+ if len(top_features) > 3:
1878
+ narratives['additional_info'] = f"Other factors like {', '.join(top_features[3:5])} also played a role, but less important."
1879
+
1880
+ # Ajout d'informations spécifiques à l'instance
1881
+ # Valeurs des features importantes pour cette prédiction spécifique
1882
+ instance_values = {}
1883
+ for i, feature in enumerate(top_features[:3]):
1884
+ idx = feature_names.index(feature)
1885
+ value = X.iloc[0, idx] if hasattr(X, 'iloc') else X[0, idx]
1886
+ instance_values[feature] = value
1887
+
1888
+ if language == 'fr':
1889
+ narratives['instance_specific'] = "Pour cette prédiction spécifique, les valeurs clés sont: "
1890
+ else: # English
1891
+ narratives['instance_specific'] = "For this specific prediction, the key values are: "
1892
+
1893
+ instance_details = [f"{feature}: {value}" for feature, value in instance_values.items()]
1894
+ narratives['instance_specific'] += ", ".join(instance_details)
1895
+
1896
+ except Exception as e:
1897
+ self._logger.warning(f"Erreur lors de la génération des narratives: {str(e)}")
1898
+ if language == 'fr':
1899
+ narratives['error'] = "Impossible de générer une explication narrative complète."
1900
+ else: # English
1901
+ narratives['error'] = "Unable to generate a complete narrative explanation."
1902
+
1903
+ return narratives
1904
+
1905
+ def _gini_index(self, array):
1906
+ """
1907
+ Calcule l'indice de Gini pour mesurer l'inégalité des attributions.
1908
+ Un Gini proche de 1 indique une forte concentration (attributions inégales),
1909
+ proche de 0 indique des attributions uniformes.
1910
+ """
1911
+ # Assurer que l'array est 1D et non négatif
1912
+ if array.ndim > 1:
1913
+ array = np.abs(array).mean(axis=0)
1914
+ else:
1915
+ array = np.abs(array)
1916
+
1917
+ # Trier les valeurs
1918
+ array = np.sort(array)
1919
+ n = array.size
1920
+ index = np.arange(1, n + 1)
1921
+
1922
+ # Calculer l'indice de Gini
1923
+ return (np.sum((2 * index - n - 1) * array)) / (n * np.sum(array))
1924
+
1925
+ def _generate_explanation_narrative(self, shap_values, X, audience_level='TECHNICAL', language='fr'):
1926
+ """
1927
+ Génère des descriptions textuelles des explications SHAP adaptées au niveau d'audience.
1928
+
1929
+ Cette méthode traduit les valeurs SHAP complexes en narratives explicatives claires
1930
+ qui s'adaptent automatiquement au niveau de technicité requis par l'audience.
1931
+
1932
+ Args:
1933
+ shap_values: Les valeurs SHAP calculées
1934
+ X: Données expliquées
1935
+ audience_level: Niveau d'audience ('TECHNICAL', 'BUSINESS', 'PUBLIC')
1936
+ language: Langue des narratives ('fr' ou 'en')
1937
+
1938
+ Returns:
1939
+ dict: Narratives explicatives structurées par niveau et fonction
1940
+ """
1941
+ narratives = {}
1942
+ feature_names = X.columns.tolist() if hasattr(X, 'columns') else [f'feature_{i}' for i in range(X.shape[1])]
1943
+
1944
+ try:
1945
+ # Calcul des importances globales moyennes
1946
+ if isinstance(shap_values, list):
1947
+ # Multi-class
1948
+ global_importance = np.mean([np.abs(sv).mean(axis=0) for sv in shap_values], axis=0)
1949
+ classes = len(shap_values)
1950
+ else:
1951
+ # Binary/Regression
1952
+ global_importance = np.abs(shap_values).mean(axis=0)
1953
+ classes = 1
1954
+
1955
+ # Tri des caractéristiques par importance
1956
+ sorted_idx = np.argsort(-global_importance)
1957
+ top_features = [feature_names[idx] for idx in sorted_idx[:5]] # Top 5 features
1958
+ top_importance = global_importance[sorted_idx[:5]]
1959
+ top_importance_norm = top_importance / top_importance.sum() * 100 # Normalisation en %
1960
+
1961
+ # Génération des narratives selon le niveau d'audience
1962
+ if language == 'fr':
1963
+ # Français
1964
+ if audience_level == 'TECHNICAL':
1965
+ # Narrative technique détaillée
1966
+ gini_value = self._gini_index(global_importance)
1967
+ concentration_type = "forte concentration" if gini_value > 0.6 else "répartition équilibrée"
1968
+ model_type_str = 'multi-classes' if classes > 1 else 'binaire/régression'
1969
+ summary = (
1970
+ f"L'analyse SHAP a identifié {len(feature_names)} caractéristiques dont les "
1971
+ f"principales sont {', '.join(top_features[:3])} représentant "
1972
+ f"{top_importance_norm[:3].sum():.1f}% "
1973
+ f"Le modèle {model_type_str} "
1974
+ f"présente une distribution d'attributions avec un indice de Gini "
1975
+ f"de {gini_value:.3f}, indiquant une "
1976
+ f"{concentration_type} "
1977
+ f"de l'influence prédictive."
1978
+ )
1979
+ elif audience_level == 'BUSINESS':
1980
+ # Narrative simplifiée pour business
1981
+ summary = (
1982
+ f"Les facteurs principaux influençant les prédictions sont "
1983
+ f"{', '.join(top_features[:3])} avec un impact respectif de "
1984
+ f"{', '.join([f'{v:.1f}%' for v in top_importance_norm[:3]])}. "
1985
+ f"Le modèle s'appuie {'largement sur un petit nombre de facteurs clés' if self._gini_index(global_importance) > 0.6 else 'sur un ensemble diversifié de facteurs'} "
1986
+ f"pour établir ses prédictions."
1987
+ )
1988
+ else: # 'PUBLIC'
1989
+ # Narrative très simplifiée pour le grand public
1990
+ summary = (
1991
+ f"Les éléments les plus importants dans cette décision sont "
1992
+ f"{', '.join(top_features[:3])}, "
1993
+ f"qui représentent ensemble {top_importance_norm[:3].sum():.0f}% "
1994
+ f"de l'influence sur le résultat."
1995
+ )
1996
+ else:
1997
+ # Anglais
1998
+ if audience_level == 'TECHNICAL':
1999
+ gini_value = self._gini_index(global_importance)
2000
+ model_type_str = "multi-class" if classes > 1 else "binary/regression"
2001
+ concentration_str = "high concentration" if gini_value > 0.6 else "balanced distribution"
2002
+ summary = (
2003
+ f"SHAP analysis identified {len(feature_names)} features with "
2004
+ f"the top drivers being {', '.join(top_features[:3])} representing "
2005
+ f"{top_importance_norm[:3].sum():.1f}% of explanatory impact. "
2006
+ f"The {model_type_str} model "
2007
+ f"displays an attribution distribution with Gini index "
2008
+ f"of {gini_value:.3f}, indicating "
2009
+ f"{concentration_str} "
2010
+ f"of predictive influence."
2011
+ )
2012
+ elif audience_level == 'BUSINESS':
2013
+ gini_value = self._gini_index(global_importance)
2014
+ model_strategy = "relies heavily on a small number of key factors" if gini_value > 0.6 else "leverages a diverse set of factors"
2015
+ summary = (
2016
+ f"The main factors influencing predictions are "
2017
+ f"{', '.join(top_features[:3])} with respective impacts of "
2018
+ f"{', '.join([f'{v:.1f}%' for v in top_importance_norm[:3]])}. "
2019
+ f"The model {model_strategy} "
2020
+ f"to establish its predictions."
2021
+ )
2022
+ else: # 'PUBLIC'
2023
+ summary = (
2024
+ f"The most important elements in this decision are "
2025
+ f"{', '.join(top_features[:3])}, "
2026
+ f"which together represent {top_importance_norm[:3].sum():.0f}% "
2027
+ f"of the influence on the outcome."
2028
+ )
2029
+
2030
+ narratives['summary'] = summary
2031
+
2032
+ # Ajouter d'autres narratives spécialisées selon le besoin
2033
+ # Par exemple: analyses contrefactuelles, alertes sur des biais potentiels, etc.
2034
+
2035
+ except Exception as e:
2036
+ self._logger.warning(f"Erreur lors de la génération des narratives: {str(e)}")
2037
+ # Narrative de secours en cas d'échec
2038
+ narratives['summary'] = f"Analyse SHAP effectuée sur {len(feature_names)} caractéristiques."
2039
+
2040
+ return narratives
2041
+
2042
+ def _create_explanation_result(self, shap_values, expected_value, X, metadata, y=None,
2043
+ compliance=None, narrative=None, raw_shap=None, **kwargs):
2044
+ """
2045
+ Crée un objet ExplanationResult enrichi avec métriques de qualité et conformité.
2046
+
2047
+ Cette implémentation avancée génère un résultat d'explication complet avec données
2048
+ structurées pour tous les aspects de l'explicabilité: attributions, métriques de qualité,
2049
+ conformité réglementaire, narratives adaptées à différentes audiences, et métadonnées
2050
+ exhaustives pour la traçabilité et l'audit.
2051
+
2052
+ Args:
2053
+ shap_values: Valeurs SHAP calculées (par instance et par caractéristique)
2054
+ expected_value: Valeur attendue (baseline) du modèle
2055
+ X: Données d'entrée expliquées
2056
+ metadata: Métadonnées de l'explication et du processus
2057
+ y: Valeurs réelles ou prédites (optionnel)
2058
+ compliance: Résultats de conformité réglementaire (optionnel)
2059
+ narrative: Descriptions textuelles des explications (optionnel)
2060
+ raw_shap: Valeurs SHAP brutes pour analyses avancées (optionnel)
2061
+ **kwargs: Paramètres additionnels
2062
+ feature_names: Noms des caractéristiques
2063
+ class_names: Noms des classes pour classification
2064
+ interaction_values: Valeurs d'interaction entre caractéristiques
2065
+ output_indices: Indices des sorties expliquées
2066
+
2067
+ Returns:
2068
+ ExplanationResult: Résultat complet et enrichi de l'explication
2069
+ """
2070
+ # Détermine les noms des caractéristiques
2071
+ feature_names = kwargs.get('feature_names')
2072
+ if feature_names is None:
2073
+ if hasattr(X, 'columns'):
2074
+ feature_names = X.columns.tolist()
2075
+ else:
2076
+ feature_names = [f"feature_{i}" for i in range(X.shape[1])]
2077
+
2078
+ # Détermine les noms des classes
2079
+ class_names = kwargs.get('class_names')
2080
+ if class_names is None:
2081
+ if isinstance(shap_values, list):
2082
+ class_names = [f"class_{i}" for i in range(len(shap_values))]
2083
+
2084
+ # Calcul de l'importance globale des caractéristiques
2085
+ if isinstance(shap_values, list):
2086
+ # Multi-class
2087
+ importance_values = np.mean([np.abs(sv).mean(axis=0) for sv in shap_values], axis=0)
2088
+ else:
2089
+ # Binary/Regression
2090
+ importance_values = np.abs(shap_values).mean(axis=0)
2091
+
2092
+ # Création des objets FeatureImportance
2093
+ feature_importances = []
2094
+ for i, (name, value) in enumerate(zip(feature_names, importance_values)):
2095
+ feature_importances.append(FeatureImportance(
2096
+ feature_name=name,
2097
+ importance_value=float(value),
2098
+ importance_rank=i + 1,
2099
+ direction="positive" if value >= 0 else "negative"
2100
+ ))
2101
+
2102
+ # Tri par importance décroissante
2103
+ feature_importances.sort(key=lambda x: x.importance_value, reverse=True)
2104
+
2105
+ # Prépare les métriques de qualité
2106
+ quality_metrics = metadata.get('quality_metrics', {})
2107
+ explanation_quality = None
2108
+ if quality_metrics:
2109
+ explanation_quality = ExplanationQuality(
2110
+ fidelity=quality_metrics.get('fidelity_correlation', None),
2111
+ stability=1.0 - min(1.0, quality_metrics.get('stability_variance', 0)),
2112
+ sparsity=quality_metrics.get('feature_sparsity', None),
2113
+ consistency=None # À implémenter si disponible
2114
+ )
2115
+
2116
+ # Création de l'objet ExplanationResult
2117
+ result = ExplanationResult(
2118
+ explanation_method=ExplainabilityMethod.SHAP,
2119
+ feature_importances=feature_importances,
2120
+ instance_explanations=self._create_instance_explanations(shap_values, X, expected_value),
2121
+ global_explanation={
2122
+ 'expected_value': expected_value,
2123
+ 'feature_importance_mean': dict(zip(feature_names, importance_values.tolist())),
2124
+ 'feature_importance_std': dict(zip(feature_names, np.std([np.abs(sv) for sv in shap_values], axis=0).tolist()))
2125
+ if isinstance(shap_values, list) else dict(zip(feature_names, np.std(np.abs(shap_values), axis=0).tolist()))
2126
+ },
2127
+ metadata=metadata,
2128
+ quality=explanation_quality,
2129
+ target_values=y.tolist() if y is not None and hasattr(y, 'tolist') else y,
2130
+ raw_data={
2131
+ 'raw_shap_values': raw_shap,
2132
+ 'compliance_results': compliance,
2133
+ 'narrative': narrative
2134
+ } if raw_shap is not None or compliance is not None or narrative is not None else None,
2135
+ visualizations=None # Les visualisations seront générées séparément
2136
+ )
2137
+
2138
+ return result
2139
+
2140
+ def _create_instance_explanations(self, shap_values, X, expected_value):
2141
+ """
2142
+ Crée des explications détaillées pour chaque instance expliquée.
2143
+
2144
+ Args:
2145
+ shap_values: Valeurs SHAP calculées
2146
+ X: Données d'entrée
2147
+ expected_value: Valeur de base (expected value)
2148
+
2149
+ Returns:
2150
+ list: Liste des explications par instance
2151
+ """
2152
+ instance_explanations = []
2153
+ feature_names = X.columns.tolist() if hasattr(X, 'columns') else [f"feature_{i}" for i in range(X.shape[1])]
2154
+
2155
+ # Traitement selon le type de sortie (classification ou régression)
2156
+ if isinstance(shap_values, list):
2157
+ # Classification multi-classes
2158
+ for instance_idx in range(shap_values[0].shape[0]):
2159
+ instance_exp = {}
2160
+ for class_idx, sv in enumerate(shap_values):
2161
+ class_exp = {
2162
+ 'base_value': float(expected_value[class_idx]) if isinstance(expected_value, (list, np.ndarray)) else float(expected_value),
2163
+ 'output_value': float(expected_value[class_idx] + sv[instance_idx].sum())
2164
+ if isinstance(expected_value, (list, np.ndarray)) else float(expected_value + sv[instance_idx].sum()),
2165
+ 'features': {}
2166
+ }
2167
+
2168
+ # Détails des contributions par caractéristique
2169
+ for j, fname in enumerate(feature_names):
2170
+ class_exp['features'][fname] = {
2171
+ 'contribution': float(sv[instance_idx, j]),
2172
+ 'value': float(X.iloc[instance_idx, j]) if hasattr(X, 'iloc') else float(X[instance_idx, j])
2173
+ }
2174
+
2175
+ instance_exp[f"class_{class_idx}"] = class_exp
2176
+ instance_explanations.append(instance_exp)
2177
+ else:
2178
+ # Régression ou classification binaire
2179
+ for instance_idx in range(shap_values.shape[0]):
2180
+ instance_exp = {
2181
+ 'base_value': float(expected_value) if isinstance(expected_value, (int, float)) else float(expected_value[0]),
2182
+ 'output_value': float(expected_value + shap_values[instance_idx].sum())
2183
+ if isinstance(expected_value, (int, float)) else float(expected_value[0] + shap_values[instance_idx].sum()),
2184
+ 'features': {}
2185
+ }
2186
+
2187
+ # Détails des contributions par caractéristique
2188
+ for j, fname in enumerate(feature_names):
2189
+ instance_exp['features'][fname] = {
2190
+ 'contribution': float(shap_values[instance_idx, j]),
2191
+ 'value': float(X.iloc[instance_idx, j]) if hasattr(X, 'iloc') else float(X[instance_idx, j])
2192
+ }
2193
+
2194
+ instance_explanations.append(instance_exp)
2195
+
2196
+ return instance_explanations
2197
+
2198
+ def _extract_metadata(self):
2199
+ """
2200
+ Extrait les métadonnées de l'explainer et du modèle.
2201
+
2202
+ Returns:
2203
+ dict: Métadonnées structurées
2204
+ """
2205
+ model_type = self._detect_model_type()
2206
+ framework = model_type.split('-')[0] if '-' in model_type else model_type
2207
+
2208
+ # Déterminer le type de modèle (classification ou régression)
2209
+ is_classifier = False
2210
+ if hasattr(self._model, 'predict_proba'):
2211
+ is_classifier = True
2212
+ elif hasattr(self._model, '_estimator_type') and self._model._estimator_type == 'classifier':
2213
+ is_classifier = True
2214
+ elif model_type in ['tensorflow', 'pytorch']:
2215
+ # Pour les modèles deep learning, vérifier la forme de sortie
2216
+ try:
2217
+ # Utiliser les données d'arrière-plan pour une inférence
2218
+ bg_data = self._get_background_data()
2219
+ sample = bg_data[0:1]
2220
+ preds = self._model_predict_wrapper(sample)
2221
+
2222
+ # Si la sortie a plusieurs dimensions et la dernière dimension > 1, c'est probablement une classification
2223
+ if preds.ndim > 1 and preds.shape[-1] > 1:
2224
+ is_classifier = True
2225
+ except:
2226
+ pass
2227
+
2228
+ # Créer les métadonnées
2229
+ self._metadata = ModelMetadata(
2230
+ model_type="classification" if is_classifier else "regression",
2231
+ framework=framework,
2232
+ input_shape=self._infer_model_input_shape(),
2233
+ output_shape=None, # À compléter si nécessaire
2234
+ feature_names=getattr(self, '_feature_names', None),
2235
+ target_names=None, # À compléter si disponible
2236
+ model_params={},
2237
+ model_version="1.0.0"
2238
+ )