[
  {
    "path": ".coveragerc",
    "content": "[report]\nexclude_lines =\n    def create_integrand_vectors\n    def create_standard_deviation_vector\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "\nname: CI\n\non:\n\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n  workflow_dispatch:\n\njobs:\n    build:\n      runs-on: ubuntu-latest\n      steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-python@v1\n        with:\n          python-version: 3.8.17\n      - uses: s-weigand/setup-conda@v1\n        with:\n          activate-conda: true\n          python-version: 3.8\n      - run: conda --version\n      - run: which python\n      - name: Run installation.\n        run: |\n         conda install -y scipy\n         pip install codecov\n         pip install pytest\n         python setup.py install\n      - name: Install main package\n        run: |\n          pip install -e .[test]\n      - name: Run test-suite\n        run: |\n          python -m pytest\n      - name: Generate coverage report\n        if: success()\n        run: |\n          pip install coverage\n          coverage run -m pytest\n      - name: Upload coverage report to codecov\n        uses: codecov/codecov-action@v1\n        if: success()\n        with:\n          file: coverage.xml\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Benedek Rozemberczki\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[pypi-image]: https://badge.fury.io/py/shapley.svg\n[pypi-url]: https://pypi.python.org/pypi/shapley\n[size-image]: https://img.shields.io/github/repo-size/benedekrozemberczki/shapley.svg\n[size-url]: https://github.com/benedekrozemberczki/shapley/archive/master.zip\n[build-image]: https://github.com/benedekrozemberczki/shapley/workflows/CI/badge.svg\n[build-url]: https://github.com/benedekrozemberczki/shapley/actions?query=workflow%3ACI\n[docs-image]: https://readthedocs.org/projects/shapley/badge/?version=latest\n[docs-url]: https://shapley.readthedocs.io/en/latest/?badge=latest\n[coverage-image]: https://codecov.io/gh/benedekrozemberczki/shapley/branch/master/graph/badge.svg\n[coverage-url]: https://codecov.io/github/benedekrozemberczki/shapley?branch=master\n[arxiv-image]: https://img.shields.io/badge/ArXiv-2101.02153-orange.svg\n[arxiv-url]: https://arxiv.org/abs/2101.02153\n\n<p align=\"center\">\n  <img width=\"90%\" src=\"https://github.com/benedekrozemberczki/shapley/raw/master/shapley.jpg?sanitize=true\" />\n</p>\n\n[![PyPI Version][pypi-image]][pypi-url]\n[![Docs Status][docs-image]][docs-url]\n[![Repo size][size-image]][size-url]\n[![Code Coverage][coverage-image]][coverage-url]\n[![Build Status][build-image]][build-url]\n[![Arxiv][arxiv-image]][arxiv-url]\n\n**[Documentation](https://shapley.readthedocs.io)** | **[External Resources](https://shapley.readthedocs.io/en/latest/notes/resources.html)**\n| **[Research Paper](https://arxiv.org/abs/2101.02153)**\n\n*Shapley* is a Python library for evaluating binary classifiers in a machine learning ensemble.\n\nThe library consists of various methods to compute (approximate) the Shapley value of players (models) in weighted voting games (ensemble games) - a class of transferable utility cooperative games. We covered the exact enumeration based computation and various widely know approximation methods from economics and computer science research papers. There are also functionalities to identify the heterogeneity of the player pool based on the [Shapley entropy](https://arxiv.org/abs/2101.02153). In addition, the framework comes with a [detailed documentation](https://shapley.readthedocs.io/en/latest/), an intuitive [tutorial](https://shapley.readthedocs.io/en/latest/notes/introduction.html), 100% test coverage, and illustrative toy [examples](https://github.com/benedekrozemberczki/shapley/tree/master/examples).\n\n-------------------------------------------------------\n\n**Citing**\n\nIf you find *Shapley* useful in your research please consider adding the following citation:\n\n```bibtex\n@inproceedings{rozemberczki2021shapley,\n      title = {{The Shapley Value of Classifiers in Ensemble Games}}, \n      author = {Benedek Rozemberczki and Rik Sarkar},\n      year = {2021},\n      booktitle={Proceedings of the 30th ACM International Conference on Information and Knowledge Management},\n      pages = {1558–1567},\n}\n```\n\n--------------------------------------------------------------\n\n**A simple example**\n\nShapley makes solving voting games quite easy - see the accompanying [tutorial](https://shapley.readthedocs.io/en/latest/notes/introduction.html#applications). For example, this is all it takes to solve a weighted voting game with defined on the fly with permutation sampling:\n\n```python\nimport numpy as np\nfrom shapley import PermutationSampler\n\nW = np.random.uniform(0, 1, (1, 7))\nW = W/W.sum()\nq = 0.5\n\nsolver = PermutationSampler()\nsolver.solve_game(W, q)\nshapley_values = solver.get_solution()\n```\n----------------------------------------------------------------------------------\n\n**Methods Included**\n\nIn detail, the following methods can be used.\n\n\n* **[Expected Marginal Contribution Approximation](https://shapley.readthedocs.io/en/latest/modules/root.html#shapley.solvers.expected_marginal_contributions.ExpectedMarginalContributions)** from Fatima *et al.*: [A Linear Approximation Method for the Shapley Value](https://www.sciencedirect.com/science/article/pii/S0004370208000696)\n\n* **[Multilinear Extension](https://shapley.readthedocs.io/en/latest/modules/root.html#shapley.solvers.multilinear_extension.MultilinearExtension)** from Owen: [Multilinear Extensions of Games](https://www.jstor.org/stable/2661445?seq=1#metadata_info_tab_contents)\n\n* **[Monte Carlo Permutation Sampling](https://shapley.readthedocs.io/en/latest/modules/root.html#shapley.solvers.permutation_sampler.PermutationSampler)** from Maleki *et al.*: [Bounding the Estimation Error of Sampling-based Shapley Value Approximation](https://arxiv.org/abs/1306.4265)\n\n* **[Exact Enumeration](https://shapley.readthedocs.io/en/latest/modules/root.html#shapley.solvers.exact_enumeration.ExactEnumeration)** from Shapley: [A Value for N-Person Games](https://www.rand.org/pubs/papers/P0295.html)\n\n--------------------------------------------------------------------------------\n\n\nHead over to our [documentation](https://shapley.readthedocs.io) to find out more about installation, creation of datasets and a full list of implemented methods and available datasets.\nFor a quick start, check out the [examples](https://github.com/benedekrozemberczki/shapley/tree/master/examples) in the `examples/` directory.\n\nIf you notice anything unexpected, please open an [issue](https://benedekrozemberczki/shapley/issues). If you are missing a specific method, feel free to open a [feature request](https://github.com/benedekrozemberczki/shapley/issues).\n\n--------------------------------------------------------------------------------\n\n**Installation**\n\n```\n$ pip install shapley\n```\n\n**Running tests**\n\n```\n$ python setup.py test\n```\n----------------------------------------------------------------------------------\n\n**Running examples**\n\n```\n$ cd examples\n$ python permutation_sampler_example.py\n```\n\n----------------------------------------------------------------------------------\n\n**License**\n\n- [MIT License](https://github.com/benedekrozemberczki/shapley/blob/master/LICENSE)\n"
  },
  {
    "path": "docs/Makefile",
    "content": "SPHINXBUILD   = sphinx-build\nSPHINXPROJ    = shapley\nSOURCEDIR     = source\nBUILDDIR      = build\n\n.PHONY: help Makefile\n\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\"\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n\n<html>\n<head>\n  <title>Redirect</title>\n  <meta http-equiv=\"refresh\" content=\"0; url=https://karateclub.readthedocs.io\" />\n</head>\n</html>\n"
  },
  {
    "path": "docs/requirements_1.txt",
    "content": "numpy\nsphinx==4.0.2\nsphinx_rtd_theme==0.5.2\nscipy\nsix\nnbsphinx\nnbsphinx_link\n"
  },
  {
    "path": "docs/source/_figures/build.sh",
    "content": "#!/bin/sh\n\nfor filename in *.tex; do\n  basename=$(basename $filename .tex)\n  pdflatex \"$basename.tex\"\n  pdf2svg \"$basename.pdf\" \"$basename.svg\"\ndone\n"
  },
  {
    "path": "docs/source/_static/css/custom.css",
    "content": ".wy-side-nav-search .wy-dropdown > a img.logo, .wy-side-nav-search > a img.logo {\n  height: 150px;\n}\n\n.wy-side-nav-search {\n  background: rgb(243,244,247);\n}\n\n.wy-side-nav-search > div.version {\n  color: black;\n}\n\n.wy-nav-content-wrap {\n  background: inherit;\n}\n\n.wy-side-nav-search input[type=\"text\"] {\n  border: none;\n  box-shadow: none;\n  background: white;\n  border-radius: 0;\n  font-size: 100%;\n}\n\n.wy-menu-vertical li.current a,\n.wy-menu-vertical li.toctree-l1.current > a {\n  border: none;\n}\n\n.ethical-rtd > div.ethical-sidebar,\n.ethical-rtd > div.ethical-footer {\n  display: none !important;\n}\n\nh1 {\n  text-transform: uppercase;\n  font-family: inherit;\n  font-weight: 200;\n}\n\nh2,\n.rst-content .toctree-wrapper p.caption {\n  font-family: inherit;\n  font-weight: 200;\n}\n\n.rst-content a:visited {\n  color: #3091d1;\n}\n\n/* Begin code */\n.rst-content pre.literal-block,\n.rst-content div[class^=\"highlight\"] {\n  border: none;\n}\n\n.rst-content pre.literal-block,\n.rst-content div[class^=\"highlight\"] pre,\n.rst-content .linenodiv pre {\n  font-size: 80%;\n}\n\n.highlight {\n  background: #f6f8fa;\n  border-radius: 6px;\n}\n\n.highlight .kn,\n.highlight .k {\n  color: #d73a49;\n}\n\n.highlight .nn {\n  color: inherit;\n  font-weight: inherit;\n}\n\n.highlight .nc {\n  color: #e36209;\n  font-weight: inherit;\n}\n\n.highlight .fm,\n.highlight .nd,\n.highlight .nf,\n.highlight .nb {\n  color: #6f42c1;\n}\n\n.highlight .bp,\n.highlight .n {\n  color: inherit;\n}\n\n.highlight .kc,\n.highlight .s1,\n.highlight .s2,\n.highlight .mi,\n.highlight .mf,\n.highlight .bp,\n.highlight .bn,\n.highlight .ow {\n  color: #005cc5;\n  font-weight: inherit;\n}\n\n.highlight .c1 {\n  color: #6a737d;\n}\n\n.rst-content code.xref {\n  padding: .2em .4em;\n  background: rgba(27,31,35,.05);\n  border-radius: 6px;\n  border: none;\n}\n/* End code */\n\n.rst-content dl:not(.docutils) dt,\n.rst-content dl:not(.docutils) dl dt {\n  background: rgb(243,244,247);\n}\n\n.rst-content dl:not(.docutils) dt.field-odd {\n  text-transform: uppercase;\n  background: inherit;\n  border: none;\n  padding: 6px 0;\n}\n\n.rst-content dl:not(.docutils) .property:first-child .pre {\n  text-transform: uppercase;\n  font-style: normal;\n}\n\nem.sig-param span.n:first-child, em.sig-param span.n:nth-child(2) {\n  color: black;\n  font-style: normal;\n}\n\nem.sig-param span.n:nth-child(3),\nem.sig-param span.n:nth-child(3) a {\n  color: inherit;\n  font-weight: normal;\n  font-style: normal;\n}\n\nem.sig-param span.default_value {\n  font-family: SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",Courier,monospace;\n  font-style: normal;\n  font-size: 90%;\n}\n\n.sig-paren {\n  padding: 0 4px;\n}\n\n.wy-table-responsive table td,\n.wy-table-responsive table th {\n  white-space: normal;\n}\n\n.wy-table-bordered-all,\n.rst-content table.docutils {\n  border: none;\n}\n\n.wy-table-bordered-all td,\n.rst-content table.docutils td {\n  border: none;\n}\n\n.wy-table-odd td,\n.wy-table-striped tr:nth-child(2n-1) td,\n.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td {\n  background: rgb(243,244,247);\n}\n\n.wy-table td,\n.rst-content table.docutils td,\n.rst-content table.field-list td,\n.wy-table th,\n.rst-content table.docutils th,\n.rst-content table.field-list th {\n  padding: 14px;\n}\n\ntable.colwidths-given tr td p,\ntable.colwidths-given tr th p {\n  text-align: center;\n}\n\ntable.colwidths-given tr td:first-child p,\ntable.colwidths-given tr th:first-child p {\n  text-align: left;\n}\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "import datetime\nimport sphinx_rtd_theme\nimport doctest\nimport sys\nimport os\n\nsys.path.insert(0, os.path.abspath('../../'))\n\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.doctest',\n    'sphinx.ext.intersphinx',\n    'sphinx.ext.mathjax',\n    'sphinx.ext.napoleon',\n    'sphinx.ext.viewcode',\n    'sphinx.ext.githubpages',\n]\n\nautodoc_default_options = {\n    'member-order': 'bysource',\n    'special-members': '__init__',\n    'undoc-members': True,\n}\n\n\nautodoc_mock_imports = ['numba']\n\nsource_suffix = '.rst'\nmaster_doc = 'index'\n\nauthor = 'Benedek Rozemberczki'\nproject = 'shapley'\ncopyright = '{}, {}'.format(datetime.datetime.now().year, author)\n\n\nhtml_theme = 'sphinx_rtd_theme'\nhtml_theme_path = [sphinx_rtd_theme.get_html_theme_path()]\n\ndoctest_default_flags = doctest.NORMALIZE_WHITESPACE\nintersphinx_mapping = {'python': ('https://docs.python.org/', None)}\n\nhtml_theme_options = {\n    'collapse_navigation': False,\n    'display_version': True,\n    'logo_only': True,\n}\n\nhtml_logo = '_static/img/shapley_logo_text.jpg'\nhtml_static_path = ['_static']\nhtml_context = {'css_files': ['_static/css/custom.css']}\n\nadd_module_names = False\n\n\ndef setup(app):\n    def skip(app, what, name, obj, skip, options):\n        members = [\n            '__init__',\n            '__repr__',\n            '__weakref__',\n            '__dict__',\n            '__module__',\n        ]\n        return True if name in members else skip\n\n    app.connect('autodoc-skip-member', skip)\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": ":github_url: https://github.com/benedekrozemberczki/shapley\n\nShapley Documentation\n===============================\n\n*Shapley* is a Python library for evaluating binary classifiers in a machine learning ensemble. The library consists of various methods to compute (approximate) the Shapley value of players (models) in weighted voting games (ensemble games) - a class of transferable utility cooperative games. We covered the exact enumeration based computation and various widely know approximation methods from economics and computer science research papers. There are also functionalities to identify the heterogeneity of the player pool based on the `Shapley entropy <https://arxiv.org/abs/2101.02153>`_. In addition, the framework comes with a `detailed documentation <https://shapley.readthedocs.io/en/latest/>`_, an intuitive `tutorial <https://shapley.readthedocs.io/en/latest/notes/introduction.html>`_, 100% test coverage and illustrative toy `examples <https://github.com/benedekrozemberczki/shapley/tree/master/examples>`_.\n\n.. code-block:: latex\n\n    >@inproceedings{rozemberczki2021shapley,\n                    title = {{The Shapley Value of Classifiers in Ensemble Games}}, \n                    author = {Benedek Rozemberczki and Rik Sarkar},\n                    year = {2021},\n                    booktitle={Proceedings of the 30th ACM International Conference on Information and Knowledge Management},\n                    pages = {1558–1567},\n    }\n\n\n.. toctree::\n   :glob:\n   :maxdepth: 2\n   :caption: Notes\n\n   notes/installation\n   notes/introduction\n   notes/examples\n   notes/resources\n\n.. toctree::\n   :glob:\n   :maxdepth: 2\n   :caption: Package Reference\n\n   modules/root\n"
  },
  {
    "path": "docs/source/modules/root.rst",
    "content": "Shapley \n==================\n\n.. contents:: Contents\n    :local:\n\nExact Solution\n--------------------\n\n.. automodule:: shapley.solvers.exact_enumeration\n    :members:\n    :inherited-members:\n\nApproximate Solutions \n--------------------\n\n.. automodule:: shapley.solvers.permutation_sampler\n    :members:\n    :inherited-members:\n\n.. automodule:: shapley.solvers.multilinear_extension\n    :members:\n    :inherited-members:\n\n.. automodule:: shapley.solvers.expected_marginal_contributions\n    :members:\n    :inherited-members:\n    :exclude-members: create_integrand_vectors, create_standard_deviation_vector\n"
  },
  {
    "path": "docs/source/notes/installation.rst",
    "content": "Installation\n============\n\nInstall the package by using pip:\n\n    .. code-block:: none\n\n        $ pip install shapley\n\nUpgrade your outdated Shapley version by using:\n\n    .. code-block:: none\n\n        $ pip install shapley --upgrade\n\n\nTo check your current package version just simply run:\n\n    .. code-block:: none\n\n        $ pip freeze | grep shapley\n"
  },
  {
    "path": "docs/source/notes/introduction.rst",
    "content": "Introduction by example\n=======================\n\n*Shapley* is a Python library for evaluating binary classifiers in a machine learning ensemble. The library consists of various methods to compute (approximate) the Shapley value of players (models) in weighted voting games (ensemble games) - a class of transferable utility cooperative games. We covered the exact enumeration based computation and various widely know approximation methods from economics and computer science research papers. There are also functionalities to identify the heterogeneity of the player pool based on the `Shapley entropy <https://arxiv.org/abs/2101.02153>`_. In addition, the framework comes with a `detailed documentation <https://shapley.readthedocs.io/en/latest/>`_, an intuitive `tutorial <https://shapley.readthedocs.io/en/latest/notes/introduction.html>`_, 100% test coverage and illustrative toy `examples <https://github.com/benedekrozemberczki/shapley/tree/master/examples>`_.\n\n\n--------------------------------------------------------------------------------\n\n**Citing**\n\nIf you find *Shapley* useful in your research, please consider citing the following paper:\n\n.. code-block:: latex\n\n    >@inproceedings{rozemberczki2021shapley,\n                    title = {{The Shapley Value of Classifiers in Ensemble Games}}, \n                    author = {Benedek Rozemberczki and Rik Sarkar},\n                    year = {2021},\n                    booktitle={Proceedings of the 30th ACM International Conference on Information and Knowledge Management},\n                    pages = {1558–1567},\n    }\n\n\nOverview\n=======================\n--------------------------------------------------------------------------------\n\nWe provide a brief overview pf the fundamental concepts and features of Shapley through the following simple examples:\n\n.. contents::\n    :local:\n\nStandardized dataset ingestion\n------------------------------\n\nShapley assumes that the weights in the game(s) are provided by a ``Numpy float array``. This array is 2 dimensional, the first dimension corresponds to the games and the second one corresponds to the players. The quota for the games is set universally by a ``float`` value. We assume that all of the games can be won by a certain coalition. \n\nStandardized output generation\n------------------------------\nThe Shapley values are returned as a ``Numpy float array`` which has 2 dimensions - rows represent the games and columns are players. A given value in the ``Numpy array`` is the Shapley value of a player in a game. \n\nAPI driven design\n-----------------\n\nShapley uses an API driven design which involves a  ``SolutionConcept`` meta class. All of the Shapley value calculation techniques inherit from this class the methods used to verify the input and output. Methods used to estimate the average Shapley values and the Shapley entropy are also inherited from this meta class. Shapley value calculation ``solvers`` are created by parametrizing the appropriate constructor. Just like in the example below where we define a ``Monte Carlo Permutation Sampler``.\n\n.. code-block:: python\n\n    from shapley import PermutationSampler\n\n    solver = PermutationSampler()\n\nWe define a quota value and a weight matrix so we can solve a single game with permutation sampling. It is worth noting that this game is extremely large - it has 70 players.\n\n.. code-block:: python\n    \n    import numpy as np\n\n    W = np.random.uniform(0, 1, (1, 70))\n    W = W/W.sum()\n    q = 0.5\n\nWe solve the game by using the ``solve_game`` method which uses the weights and the quota as input. The approximate Shapley values are returned by the ``get_solution`` method.\n\n.. code-block:: python\n    \n    solver.solve_game(W, q)\n    shapley_values = solver.get_solution()\n\n"
  },
  {
    "path": "docs/source/notes/resources.rst",
    "content": "External resources\n==================\n\nShapley Value Approximations\n----------------------------\n\n* Shaheen Fatimaa, Michael Wooldridge, Nicholas Jennings: **A Linear Approximation Method for the Shapley Value** `Paper <https://www.sciencedirect.com/science/article/pii/S0004370208000696>`_\n\n* Guillermo Owen: **Multilinear Extensions of Games** `Paper <https://www.jstor.org/stable/2661445?seq=1#metadata_info_tab_contents>`_\n\n* Sasan Maleki, Long Tran-Thanh, Greg Hines, Talal Rahwan, Alex Rogers: **Bounding the Estimation Error of Sampling-based Shapley Value Approximation** `Paper <https://arxiv.org/abs/1306.4265>`_\n\n* Lloyd Shapley: **A Value for N-Person Games** `Paper <https://www.rand.org/pubs/papers/P0295.html>`_\n"
  },
  {
    "path": "examples/exact_calculation_example.py",
    "content": "\"\"\"Exact Enumeration Example\"\"\"\n\nimport numpy as np\nfrom shapley import ExactEnumeration\n\nW = np.random.uniform(0, 1, (1, 7))\nW = W/W.sum()\nq = 0.5\n\nsolver = ExactEnumeration()\nsolver.solve_game(W, q)\nshapley_values = solver.get_solution()\n\nprint(shapley_values)\n"
  },
  {
    "path": "examples/exact_marginal_contributions_example.py",
    "content": "\"\"\"Expected Marginal Contributions Example\"\"\"\n\nimport numpy as np\nfrom shapley import ExpectedMarginalContributions\n\nW = np.random.uniform(0, 1, (1, 20))\nW = W/W.sum()\nq = 0.35\n\nsolver = ExpectedMarginalContributions()\nsolver.solve_game(W, q)\nshapley_values = solver.get_solution()\n\nprint(shapley_values)\n"
  },
  {
    "path": "examples/multilinear_extension_example.py",
    "content": "\"\"\"Multilinear Extension Example\"\"\"\n\nimport numpy as np\nfrom shapley import MultilinearExtension\n\nW = np.random.uniform(0, 1, (20, 20))\nW = W/W.sum(1)\nq = 0.35\n\nsolver = MultilinearExtension()\nsolver.solve_game(W, q)\nshapley_values = solver.get_solution()\n\nprint(shapley_values)\n"
  },
  {
    "path": "examples/permutation_sampler_example.py",
    "content": "\"\"\"Permutation Sampler Example\"\"\"\n\nimport numpy as np\nfrom shapley import PermutationSampler\n\nW = np.random.uniform(0, 1, (1, 7))\nW = W/W.sum()\nq = 0.5\n\nsolver = PermutationSampler()\nsolver.solve_game(W, q)\nshapley_values = solver.get_solution()\n\nprint(shapley_values)\n"
  },
  {
    "path": "readthedocs.yml",
    "content": "version: 2\n\nbuild:\n   image: latest\n\npython:\n   version: 3.7\n   system_packages: true\n   install:\n      - requirements: docs/requirements_1.txt\n      - method: setuptools\n        path: .\n\nformats: []\n"
  },
  {
    "path": "setup.py",
    "content": "import os\nfrom setuptools import find_packages, setup\n\non_rtd = os.environ.get(\"READTHEDOCS\") == \"True\"\n\ninstall_requires = [\"numpy\", \"scipy\", \"six\"]\n\nif not on_rtd:\n    install_requires.append(\"numba\")\n\nsetup_requires = [\"pytest-runner\"]\n\n\ntests_require = [\"pytest\", \"pytest-cov\", \"mock\", \"unittest\"]\n\n\n\nkeywords = [\n    \"shapley\",\n    \"random-forest\",\n    \"data-science\",\n    \"shap\",\n    \"fairness\",\n    \"ensemble\",\n    \"expert-system\",\n    \"explanability\",\n    \"voting-classifier\",\n    \"classifier\",\n    \"machine-learning\",\n    \"deep-learning\",\n    \"deeplearning\",\n    \"game-theory\",\n    \"cooperative-game\"\n]\n\n\nsetup(\n    name=\"shapley\",\n    packages=find_packages(),\n    version=\"1.0.3\",\n    license=\"MIT\",\n    description=\"A general purpose library to quantify the value of classifiers in an ensemble.\",\n    author=\"Benedek Rozemberczki\",\n    author_email=\"benedek.rozemberczki@gmail.com\",\n    url=\"https://github.com/benedekrozemberczki/shapley\",\n    download_url=\"https://github.com/benedekrozemberczki/shapley/archive/v_10003.tar.gz\",\n    keywords=keywords,\n    install_requires=install_requires,\n    setup_requires=setup_requires,\n    tests_require=tests_require,\n    classifiers=[\n        \"Development Status :: 3 - Alpha\",\n        \"Intended Audience :: Developers\",\n        \"Topic :: Software Development :: Build Tools\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3.7\",\n    ],\n)\n"
  },
  {
    "path": "shapley/__init__.py",
    "content": "from shapley.solvers import *\nfrom shapley.solution_concept import SolutionConcept\nfrom shapley.version import __version__  # noqa:F401,F403\n\n__all__ = [\n    \"shapley\",\n]\n"
  },
  {
    "path": "shapley/solution_concept.py",
    "content": "\"\"\"Solution Concept base class.\"\"\"\n\n\nimport numpy as np\nfrom abc import ABCMeta, abstractmethod\n\n\nclass SolutionConcept(metaclass=ABCMeta):\n    \"\"\"Solution Concept base class with constructor and public methods.\"\"\"\n\n    @abstractmethod\n    def get_solution(self) -> np.ndarray:\n        r\"\"\"Returning the solution.\n\n        Return Types:\n            Phi (Numpy array): Approximate Shapley matrix of players in the game(s) with size :math:`n \\times m`.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def solve_game(self, W: np.ndarray, q: float):\n        r\"\"\"Solving the weigted voting game(s).\n\n        Args:\n            W (Numpy array): An :math:`n \\times m` matrix of voting weights for the :math:`n` games with :math:`m` players.\n            q (float): Quota in the games.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def setup(self, W: np.ndarray):\n        \"\"\"Creating an empty Shapley value matrix and a player pool.\"\"\"\n        pass\n\n    def _check_quota(self, q: float):\n        \"\"\"Checking for negative quota.\"\"\"\n        assert 0.0 <= q\n\n    def _verify_result_shape(self, W: np.ndarray, Phi: np.ndarray):\n        \"\"\"Checking the shape of the Shapley value matrix.\"\"\"\n        assert W.shape == Phi.shape\n\n    def _verify_distribution(self, Phi: np.ndarray):\n        \"\"\"Verify distribution hypothesis.\"\"\"\n        assert (np.sum(Phi) - Phi.shape[0]) < 0.0001\n\n    def _run_sanity_check(self, W: np.ndarray, Phi: np.ndarray):\n        \"\"\"Checking the basic assumptions about the Shapley values.\"\"\"\n        self._verify_result_shape(W, Phi)\n        self._verify_distribution(Phi)\n\n    def _set_average_shapley(self):\n        \"\"\"Calculating the average Shapley value scores.\"\"\"\n        self._Phi_tilde = np.mean(self._Phi, axis=0)\n\n    def _set_shapley_entropy(self):\n        \"\"\"Calculating the Shapley entropy score.\"\"\"\n        self._shapley_entropy = -np.sum(self._Phi_tilde * np.log(self._Phi_tilde))\n\n    def get_average_shapley(self) -> np.ndarray:\n        \"\"\"Getting the average Shapley value scores.\"\"\"\n        return self._Phi_tilde\n\n    def get_shapley_entropy(self) -> float:\n        \"\"\"Getting the Shapley entropy score.\"\"\"\n        return self._shapley_entropy\n"
  },
  {
    "path": "shapley/solvers/__init__.py",
    "content": "from .exact_enumeration import ExactEnumeration\nfrom .permutation_sampler import PermutationSampler\nfrom .multilinear_extension import MultilinearExtension\nfrom .expected_marginal_contributions import ExpectedMarginalContributions\n"
  },
  {
    "path": "shapley/solvers/exact_enumeration.py",
    "content": "\"\"\"Exact Enumeration Based Shapley Value.\"\"\"\n\nimport itertools\nimport numpy as np\nfrom shapley.solution_concept import SolutionConcept\n\n\nclass ExactEnumeration(SolutionConcept):\n    r\"\"\"Exact enumeration of all permutations and finding the pivotal voters. It\n    is designed with a generator of the permutations. For details see this paper:\n    `\"A Value for N-Person Games.\" <https://www.rand.org/pubs/papers/P0295.html>`_\n    \"\"\"\n\n    def setup(self, W: np.ndarray):\n        \"\"\"Creating an empty Shapley value matrix and a player pool.\"\"\"\n        self._Phi = np.zeros(W.shape)\n        self._indices = [i for i in range(W.shape[1])]\n        self.permutations = 0\n\n    def _run_permutations(self, W: np.ndarray, q: float):\n        \"\"\"Creating Monte Carlo permutations and finding the marginal voter.\"\"\"\n        for perm in itertools.permutations(self._indices):\n            self.permutations = self.permutations + 1\n            indices = list(perm)\n            W_perm = W[:, indices]\n            cum_sum = np.cumsum(W_perm, axis=1)\n            pivotal = np.array(indices)[np.argmax(cum_sum > q, axis=1)]\n            self._Phi[np.arange(W.shape[0]), pivotal] += 1.0\n        self._Phi = self._Phi / self.permutations\n\n    def solve_game(self, W: np.ndarray, q: float):\n        r\"\"\"Solving the weigted voting game(s).\n\n        Args:\n            W (Numpy array): An :math:`n \\times m` matrix of voting weights for the :math:`n` games with :math:`m` players.\n            q (float): Quota in the games.\n        \"\"\"\n        self._check_quota(q)\n        self.setup(W)\n        self._run_permutations(W, q)\n        self._run_sanity_check(W, self._Phi)\n        self._set_average_shapley()\n        self._set_shapley_entropy()\n\n    def get_solution(self) -> np.ndarray:\n        r\"\"\"Returning the solution.\n\n        Return Types:\n            Phi (Numpy array): Approximate Shapley matrix of players in the game(s) with size :math:`n \\times m`.\n        \"\"\"\n        return self._Phi\n"
  },
  {
    "path": "shapley/solvers/expected_marginal_contributions.py",
    "content": "\"\"\"Expected Marginal Contributions Based Shapley Value Approximation.\"\"\"\n\nimport numpy as np\nfrom numba import jit\nfrom scipy.stats import norm\nfrom shapley.solution_concept import SolutionConcept\n\n\n@jit\ndef create_integrand_vectors(player_count, q, w):\n    \"\"\"\n    Creating the vector of standard deviations and integrand limits.\n    :param player_count (int): Number of players in the game.\n    :param q (float): Quota to win the game.\n    :param w (float): Weight of the player in the game.\n\n    :return a_s: Vector of lower integrand limits.\n    :return b_s: Vector of upper integrand limits.\n    \"\"\"\n    a_s = (q - w) / np.linspace(1, player_count - 1, player_count - 1)\n    b_s = (q - 10**-20) / np.linspace(1, player_count - 1, player_count - 1)\n    return a_s, b_s\n\n\n@jit\ndef create_standard_deviation_vector(var, player_count):\n    \"\"\"\n    Creating the vector of standard deviations and integrand limits.\n    :param var (float): Variance of the weights in the game.\n    :param player_count (int): Number of players in the game.\n\n    :return sigma_s: Vector of standard deviations for the game.\n    \"\"\"\n    sigma_s = np.power(var / np.linspace(1, player_count - 1, player_count - 1), 0.5)\n    return sigma_s\n\n\nclass ExpectedMarginalContributions(SolutionConcept):\n    r\"\"\"The expected marginal contributions approximation of the Shapley value in a weighted\n    voting game using the technique proposed by Fatima. For details see this paper:\n    `\"A Linear Approximation Method for the Shapley Value.\"\n     <https://www.sciencedirect.com/science/article/pii/S0004370208000696>`_\n    \"\"\"\n\n    def __init__(self, epsilon: float = 10**-8):\n        self.epsilon = epsilon\n\n    def setup(self, W: np.ndarray):\n        \"\"\"Creating an empty Shapley value matrix.\"\"\"\n        self._Phi = np.zeros(W.shape)\n\n    def _approximate(self, W: np.ndarray, q: float):\n        \"\"\"Using the naive multilinear approximation method.\"\"\"\n        for data_point in range(W.shape[0]):\n            mu = float(np.mean(W[data_point, :]))\n            var = float(np.var(W[data_point, :]))\n            sigma_s = create_standard_deviation_vector(var, W.shape[1])\n            for player_index in range(W.shape[1]):\n                w = W[data_point, player_index]\n                a_s, b_s = create_integrand_vectors(W.shape[1], q, w)\n                shap_in_game = norm.cdf(b_s, loc=mu, scale=sigma_s) - norm.cdf(\n                    a_s, loc=mu, scale=sigma_s\n                )\n                self._Phi[data_point, player_index] += np.sum(shap_in_game)\n\n        self._Phi = self._Phi / np.sum(self._Phi, axis=1).reshape(-1, 1)\n\n    def solve_game(self, W: np.ndarray, q: float):\n        r\"\"\"Solving the weigted voting game(s).\n\n        Args:\n            W (Numpy array): An :math:`n \\times m` matrix of voting weights for the :math:`n` games with :math:`m` players.\n            q (float): Quota in the games.\n        \"\"\"\n        self._check_quota(q)\n        self.setup(W)\n        self._approximate(W, q)\n        self._run_sanity_check(W, self._Phi)\n        self._set_average_shapley()\n        self._set_shapley_entropy()\n\n    def get_solution(self) -> np.ndarray:\n        r\"\"\"Returning the solution.\n\n        Return Types:\n            Phi (Numpy array): Approximate Shapley matrix of players in the game(s) with size :math:`n \\times m`.\n        \"\"\"\n        return self._Phi\n"
  },
  {
    "path": "shapley/solvers/multilinear_extension.py",
    "content": "\"\"\"Multilinear Extension Based Shapley Value Approximation.\"\"\"\n\nimport random\nimport numpy as np\nfrom scipy.stats import norm\nfrom shapley.solution_concept import SolutionConcept\n\n\nclass MultilinearExtension(SolutionConcept):\n    r\"\"\"The multilinear extension approximation of the Shapley value in a weighted\n    voting game using the technique proposed by Owen. For details see this paper:\n    `\"Multilinear Extensions of Games.\"  <https://www.jstor.org/stable/2661445#metadata_info_tab_contents>`_\n    \"\"\"\n\n    def setup(self, W: np.ndarray):\n        \"\"\"Creating an empty Shapley value matrix.\"\"\"\n        self._Phi = np.zeros(W.shape)\n\n    def _approximate(self, W: np.ndarray, q: float):\n        \"\"\"Using the naive multilinear approximation method.\"\"\"\n        mu = np.tile(np.sum(W, axis=1).reshape(-1, 1), W.shape[1]) - W\n        std = np.tile(\n            np.sum(np.square(W), axis=1).reshape(-1, 1), W.shape[1]\n        ) - np.square(W)\n        upper = (np.zeros(W.shape) + q - mu) / std\n        lower = (np.zeros(W.shape) + q - W - mu) / std\n        self._Phi = norm.cdf(upper, 0, 1) - norm.cdf(lower, 0, 1)\n        self._Phi = self._Phi / np.sum(self._Phi, axis=1).reshape(-1, 1)\n\n    def solve_game(self, W: np.ndarray, q: float):\n        r\"\"\"Solving the weigted voting game(s).\n\n        Args:\n            W (Numpy array): An :math:`n \\times m` matrix of voting weights for the :math:`n` games with :math:`m` players.\n            q (float): Quota in the games.\n        \"\"\"\n        self._check_quota(q)\n        self.setup(W)\n        self._approximate(W, q)\n        self._run_sanity_check(W, self._Phi)\n        self._set_average_shapley()\n        self._set_shapley_entropy()\n\n    def get_solution(self) -> np.ndarray:\n        r\"\"\"Returning the solution.\n\n        Return Types:\n            Phi (Numpy array): Approximate Shapley matrix of players in the game(s) with size :math:`n \\times m`.\n        \"\"\"\n        return self._Phi\n"
  },
  {
    "path": "shapley/solvers/permutation_sampler.py",
    "content": "\"\"\"Permutation Sampling Based Shapley Value Approximation.\"\"\"\n\nimport random\nimport numpy as np\nfrom shapley.solution_concept import SolutionConcept\n\n\nclass PermutationSampler(SolutionConcept):\n    r\"\"\"Permutation sampler to solve a block of weighted voting games. The solver\n    samples random permutations of the players uniformly. In each permutation we\n    identify the pivotal voter. We use the empirical probability of becoming the\n    pivotal player as the Shapley value estimate. For details see this paper:\n    `\"Bounding the Estimation Error of Sampling-based Shapley Value Approximation.\"\n    <https://arxiv.org/abs/1306.4265>`_\n\n    Args:\n        permutations (int): Number of permutations. The default is 1000.\n    \"\"\"\n\n    def __init__(self, permutations: int = 1000):\n        self.permutations = permutations\n\n    def setup(self, W: np.ndarray):\n        \"\"\"Creating an empty Shapley value matrix and a player pool.\"\"\"\n        self._Phi = np.zeros(W.shape)\n        self._indices = [i for i in range(W.shape[1])]\n\n    def _run_permutations(self, W: np.ndarray, q: float):\n        \"\"\"Creating Monte Carlo permutations and finding the marginal voter.\"\"\"\n        for _ in range(self.permutations):\n            random.shuffle(self._indices)\n            W_perm = W[:, self._indices]\n            cum_sum = np.cumsum(W_perm, axis=1)\n            pivotal = np.array(self._indices)[np.argmax(cum_sum > q, axis=1)]\n            self._Phi[np.arange(W.shape[0]), pivotal] += 1.0\n        self._Phi = self._Phi / self.permutations\n\n    def solve_game(self, W: np.ndarray, q: float):\n        r\"\"\"Solving the weigted voting game(s).\n\n        Args:\n            W (Numpy array): An :math:`n \\times m` matrix of voting weights for the :math:`n` games with :math:`m` players.\n            q (float): Quota in the games.\n        \"\"\"\n        self._check_quota(q)\n        self.setup(W)\n        self._run_permutations(W, q)\n        self._run_sanity_check(W, self._Phi)\n        self._set_average_shapley()\n        self._set_shapley_entropy()\n\n    def get_solution(self) -> np.ndarray:\n        r\"\"\"Returning the solution.\n\n        Return Types:\n            Phi (Numpy array): Approximate Shapley matrix of players in the game(s) with size :math:`n \\times m`.\n        \"\"\"\n        return self._Phi\n"
  },
  {
    "path": "shapley/version.py",
    "content": "\"\"\"Contains the version of Shapley.\"\"\"\n\n__version__ = \"1.0.3\"\n"
  },
  {
    "path": "tests/unit/test_shapley.py",
    "content": "\"\"\"Generic shape and entropy tests.\"\"\"\n\nimport pytest\nimport unittest\n\nimport math\nimport numpy as np\nfrom shapley import ExactEnumeration\nfrom shapley import PermutationSampler\nfrom shapley import MultilinearExtension\nfrom shapley import ExpectedMarginalContributions\n\n\nclass TestShapley(unittest.TestCase):\n\n    def test_permutation_sampling(self):\n        \"\"\"\n        Testing the Permutation Sampler class.\n        \"\"\"\n        solver = PermutationSampler()\n\n        W = np.random.uniform(0, 1, (100, 100))\n        solver.solve_game(W, q=50)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n\n        solver = PermutationSampler(permutations=10000)\n\n        W = np.random.uniform(0, 1, (100, 50))\n        solver.solve_game(W, q=20)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n\n        solver = PermutationSampler(permutations=10000)\n\n        W = np.random.uniform(0, 1, (10, 13))\n        solver.solve_game(W, q=3)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n\n\n    def test_multilinear_extension(self):\n        \"\"\"\n        Testing the Multilinear Extension class.\n        \"\"\"\n\n        solver = MultilinearExtension()\n\n        W = np.random.uniform(0, 1, (100, 97))\n        solver.solve_game(W, q=0.25)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n\n        solver = MultilinearExtension()\n\n        W = np.random.uniform(0, 1, (100, 48))\n        solver.solve_game(W, q=0.25)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n\n        solver = MultilinearExtension()\n\n        W = np.random.uniform(0, 1, (10, 13))\n        solver.solve_game(W, q=0.13)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n\n    def test_exact_enumeration(self):\n        \"\"\"\n        Testing the Exact Enumeration class.\n        \"\"\"\n\n        solver = ExactEnumeration()\n\n        W = np.random.uniform(0, 1, (100, 7))\n        solver.solve_game(W, q=2.5)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > -0.001\n\n        solver = ExactEnumeration()\n\n        W = np.random.uniform(0, 1, (100, 6))\n        solver.solve_game(W, q=2)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > -0.001\n\n        solver = ExactEnumeration()\n\n        W = np.random.uniform(0, 1, (10, 5))\n        solver.solve_game(W, q=3)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > -0.001\n\n\n    def test_expected_marginal_contributions(self):\n        \"\"\"\n        Testing the Expected Marginal Contributions class.\n        \"\"\"\n\n        solver = ExpectedMarginalContributions()\n\n        W = np.random.uniform(0, 1, (100, 97))\n        solver.solve_game(W, q=15.5)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n\n        solver = ExpectedMarginalContributions()\n\n        W = np.random.uniform(0, 1, (100, 48))\n        solver.solve_game(W, q=12.5)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n\n        solver = ExpectedMarginalContributions(epsilon=10 ** -5)\n\n        W = np.random.uniform(0, 1, (10, 13))\n        solver.solve_game(W, q=3.0)\n        Phi = solver.get_solution()\n        Phi_tilde = solver.get_average_shapley()\n        entropy = solver.get_shapley_entropy()\n\n        assert Phi.shape == W.shape\n        assert Phi_tilde.shape == (W.shape[1],)\n        assert -math.log(1.0 / W.shape[1]) - entropy > 0\n"
  }
]