Skip to content

Commit 269ebb4

Browse files
authored
JOSS review (#97)
* Incorporate player copying (#83) * Alter tests to support copies of players. Player instances are now internal to a Game instance so the tests have to reflect that. Without designing some overly elaborate __eq__ and __hash__ methods, this seems sensible for now (other aspects of a Game instance require the Player class to be hashable). * Implement player copying. Player instances are copied at the instantiation of a Game subclass. This is done using `copy.deepcopy`. That way, multiple Game instances can be created from one set of player lists. * Add hypothesis to setup.py (#87) * Add test for inconsistent coverage. (#86) Occasionally, coverage falls below 100% because of an edge case in HR where no resident is removed from consideration for forgetting all of their preferences. This simple example test rectifies that. * Add hypothesis to `tests_require` in `setup.py` * Make Game a metaclass (#94) * Reimplement `test_game.py` for `abc.ABCMeta`. * Change `Game` to `BaseGame` as `ABCMeta` subclass. * Change inheritance in each game. * Format with black, blackbook and isort. * Format with black, blackbook and isort. * Edit paper for JOSS review (#93) * Add test for inconsistent coverage. (#86) Occasionally, coverage falls below 100% because of an edge case in HR where no resident is removed from consideration for forgetting all of their preferences. This simple example test rectifies that. * Fix flaky `Player.forget()` behaviour (#88) * Add `ValueError` to exceptions in SR(2) test. Since the first phase is not being completed, the preferences have not been reduced and the all-or-nothing cycles can contain repeats of pairs. This means players attempt to forget someone they've already forgotten. * Remove the try-except from `Player.forget()`. No longer necessary as the tests catch this edge case (that would not ever actually happen in SR). * Flesh out example paragraph. * Remove fluff around edu infrastructure. * Fix documentation for JOSS review (#85) * Add sphinxcontrib-bibtex to docs/env file. * Remove "Search Page" from "Indices and Tables" * Add notebook pointer prologue. * Clarify installation instructions for pyversion. * Move install intro to a note directive. * Update README (and supporting files) (#84) * Add CONTRIBUTING file to the top level. * Remove duplicate "Documentation" sect from README * Change "PR" to "pull request" in README * Add python_requires parameter to setup (#92)
1 parent fabb94f commit 269ebb4

File tree

23 files changed

+212
-217
lines changed

23 files changed

+212
-217
lines changed

CONTRIBUTING.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
If you are interested in contributing to the Matching library then first of all
2+
we would like to offer our thanks and say welcome!
3+
4+
Whether you're looking to provide a fix for a current `issue
5+
<https://github.com/daffidwilde/matching/issues>`_, report a bug or implement a
6+
new feature to the library, your contributions are always welcome. Pull requests
7+
are a good place to discuss contributions so please do not feel you must present
8+
a perfect product from the off.
9+
10+
To make a contribution via a pull request, follow these steps:
11+
12+
1. Go to the `GitHub repo <https://github.com/daffidwilde/matching>`_,
13+
make a fork, and clone the repo locally::
14+
15+
$ git clone git@github.com/<your-username>/matching.git
16+
17+
2. Assuming you have all the dependencies installed, the next step is to ensure
18+
that all the tests pass (this should not take very long at all)::
19+
20+
$ cd matching
21+
$ pytest tests
22+
23+
3. Make your changes and write tests to go with them -- ensuring they pass, too.
24+
25+
4. Push to your fork and open a pull request.
26+

INSTALLATION.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
Matching is written in Python 3, and relies only on `NumPy
2-
<http://www.numpy.org/>`_ for general use.
1+
.. note::
2+
Matching requires a Python version of 3.5 or above and relies only on `NumPy
3+
<http://www.numpy.org/>`_ for installation and general use.
34

45
The library is most easily installed using :code:`pip`::
56

67
$ python -m pip install matching
78

89
However, if you would like to install it from source then go ahead and clone the
9-
GitHub repo::
10+
GitHub repository before installing locally::
1011

1112
$ git clone https://github.com/daffidwilde/matching.git
1213
$ cd matching
13-
$ python setup.py install
14+
$ python -m pip install .

README.rst

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,9 @@ solved in less than one tenth of a second:
200200
>>> _ = game.solve() # 48.6 ms ± 963 µs per loop
201201

202202

203-
Documentation
204-
-------------
205-
206-
Full documentation is available here: `<https://matching.readthedocs.io>`_
207-
208203
Get in contact!
209204
---------------
210205

211206
I hope this package is useful, and feel free to contact me here (or on Twitter:
212207
`@daffidwilde <https://twitter.com/daffidwilde>`_) with any issues or
213-
recommendations. PRs always welcome!
208+
recommendations. Pull requests are always welcome!

docs/conf.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,20 @@
195195
"reference/source/modules.rst",
196196
]
197197

198-
# nbsphinx_prolog = """
199-
# Go there: https://github.com/daffidwilde/matching/blob/docs/{{ env.doc2path(env.docname, base=None) }}
200-
# ----
201-
# """
198+
nbsphinx_prolog = """
199+
200+
{% set docname = 'docs/' + env.doc2path(env.docname, base=None) %}
201+
202+
.. raw:: html
203+
204+
<div class="admonition note">
205+
<p>This page was generated from
206+
<a class="reference external"
207+
href="https://github.com/daffidwilde/matching/blob/master/{{ docname|e }}">{{ docname|e }}</a>.
208+
</p>
209+
</div>
210+
211+
"""
202212

203213
nbsphinx_epilog = """
204214
----

docs/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ dependencies:
1212
- matching
1313
- nbsphinx
1414
- sphinx_rtd_theme
15+
- sphinxcontrib-bibtex
Lines changed: 1 addition & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1 @@
1-
{
2-
"cells": [
3-
{
4-
"cell_type": "markdown",
5-
"metadata": {},
6-
"source": [
7-
"# Check the status of a matching\n",
8-
"\n",
9-
"Verifying the validity and stability of a matching is paramount in any matching game. In matching this is done by creating an instance of a game and using the `check_validity` and `check_stability` methods of the instance.\n",
10-
"\n",
11-
"Consider the following instance of SA."
12-
]
13-
},
14-
{
15-
"cell_type": "code",
16-
"execution_count": 1,
17-
"metadata": {},
18-
"outputs": [],
19-
"source": [
20-
"from matching.games import StudentAllocation\n",
21-
"\n",
22-
"student_preferences = {\n",
23-
" \"A\": [\"X1\", \"X2\"],\n",
24-
" \"B\": [\"Y2\", \"X1\"],\n",
25-
" \"C\": [\"X1\", \"Y1\"],\n",
26-
" \"D\": [\"Y2\", \"Y1\"],\n",
27-
"}\n",
28-
"\n",
29-
"supervisor_preferences = {\"X\": [\"C\", \"A\", \"B\"], \"Y\": [\"C\", \"D\", \"B\"]}\n",
30-
"\n",
31-
"project_supervisors = {\"X1\": \"X\", \"X2\": \"X\", \"Y1\": \"Y\", \"Y2\": \"Y\"}\n",
32-
"project_capacities = {project: 1 for project in project_supervisors}\n",
33-
"supervisor_capacities = {supervisor: 2 for supervisor in supervisor_preferences}\n",
34-
"\n",
35-
"\n",
36-
"game = StudentAllocation.create_from_dictionaries(\n",
37-
" student_preferences,\n",
38-
" supervisor_preferences,\n",
39-
" project_supervisors,\n",
40-
" project_capacities,\n",
41-
" supervisor_capacities,\n",
42-
")\n"
43-
]
44-
},
45-
{
46-
"cell_type": "markdown",
47-
"metadata": {},
48-
"source": [
49-
"An easy way to get a matching is just to solve the game. From there we can verify the status of the current matching."
50-
]
51-
},
52-
{
53-
"cell_type": "code",
54-
"execution_count": 2,
55-
"metadata": {},
56-
"outputs": [
57-
{
58-
"data": {
59-
"text/plain": [
60-
"(True, True)"
61-
]
62-
},
63-
"execution_count": 2,
64-
"metadata": {},
65-
"output_type": "execute_result"
66-
}
67-
],
68-
"source": [
69-
"game.solve()\n",
70-
"\n",
71-
"game.check_validity(), game.check_stability()"
72-
]
73-
}
74-
],
75-
"metadata": {
76-
"kernelspec": {
77-
"display_name": "Matching docs",
78-
"language": "python",
79-
"name": "matching-docs"
80-
},
81-
"language_info": {
82-
"codemirror_mode": {
83-
"name": "ipython",
84-
"version": 3
85-
},
86-
"file_extension": ".py",
87-
"mimetype": "text/x-python",
88-
"name": "python",
89-
"nbconvert_exporter": "python",
90-
"pygments_lexer": "ipython3",
91-
"version": "3.7.6"
92-
}
93-
},
94-
"nbformat": 4,
95-
"nbformat_minor": 4
96-
}
1+
{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Check the status of a matching\n", "\n", "Verifying the validity and stability of a matching is paramount in any matching game. In matching this is done by creating an instance of a game and using the `check_validity` and `check_stability` methods of the instance.\n", "\n", "Consider the following instance of SA."]}, {"cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": ["from matching.games import StudentAllocation\n", "\n", "student_preferences = {\n", " \"A\": [\"X1\", \"X2\"],\n", " \"B\": [\"Y2\", \"X1\"],\n", " \"C\": [\"X1\", \"Y1\"],\n", " \"D\": [\"Y2\", \"Y1\"],\n", "}\n", "\n", "supervisor_preferences = {\"X\": [\"C\", \"A\", \"B\"], \"Y\": [\"C\", \"D\", \"B\"]}\n", "\n", "project_supervisors = {\"X1\": \"X\", \"X2\": \"X\", \"Y1\": \"Y\", \"Y2\": \"Y\"}\n", "project_capacities = {project: 1 for project in project_supervisors}\n", "supervisor_capacities = {supervisor: 2 for supervisor in supervisor_preferences}\n", "\n", "\n", "game = StudentAllocation.create_from_dictionaries(\n", " student_preferences,\n", " supervisor_preferences,\n", " project_supervisors,\n", " project_capacities,\n", " supervisor_capacities,\n", ")\n"]}, {"cell_type": "markdown", "metadata": {}, "source": ["An easy way to get a matching is just to solve the game. From there we can verify the status of the current matching."]}, {"cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [{"data": {"text/plain": ["(True, True)"]}, "execution_count": 2, "metadata": {}, "output_type": "execute_result"}], "source": ["game.solve()\n", "\n", "game.check_validity(), game.check_stability()\n"]}], "metadata": {"kernelspec": {"display_name": "Matching docs", "language": "python", "name": "matching-docs"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6"}}, "nbformat": 4, "nbformat_minor": 4}

docs/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,3 @@ Indices and tables
3636

3737
* :ref:`genindex`
3838
* :ref:`modindex`
39-
* :ref:`search`

docs/reference/contributing.rst

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,4 @@
11
Contributing to the library
22
===========================
33

4-
If you are interested in contributing to the Matching library then first of all
5-
we would like to offer our thanks and say welcome!
6-
7-
Whether you're looking to provide a fix for a current `issue
8-
<https://github.com/daffidwilde/matching/issues>`_, report a bug or implement a
9-
new feature to the library, your contributions are always welcome. Pull requests
10-
(PRs) are a good place to discuss contributions so please do not feel you must
11-
present a perfect product from the off.
12-
13-
To make a contribution via a PR, follow these steps:
14-
15-
1. Go to the `GitHub repo <https://github.com/daffidwilde/matching>`_,
16-
make a fork, and clone the repo locally::
17-
18-
$ git clone git@github.com/<your-username>/matching.git
19-
20-
2. Assuming you have all the dependencies installed, the next step is to ensure
21-
that all the tests pass (this should not take very long at all)::
22-
23-
$ cd matching
24-
$ pytest tests
25-
26-
3. Make your changes and write tests to go with them -- ensuring they pass, too.
27-
28-
4. Push to your fork and open a pull request.
4+
.. include:: ../../CONTRIBUTING.rst

docs/tutorials/project_allocation/data.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,10 @@ def main():
188188
print("Seed set:", SEED)
189189

190190
supervisor_to_projects = create_supervisor_to_projects_map()
191-
supervisor_to_capacity, project_to_capacity = create_player_to_capacity_maps(
192-
supervisor_to_projects
193-
)
191+
(
192+
supervisor_to_capacity,
193+
project_to_capacity,
194+
) = create_player_to_capacity_maps(supervisor_to_projects)
194195
print("Supervisor and project dictionaries created...")
195196

196197
all_projects = list(get_all_projects(supervisor_to_projects))

paper.bib

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -114,18 +114,6 @@ @misc{numpy
114114
note = {[Online; accessed 2020-02-24]},
115115
}
116116

117-
@article{okun1995,
118-
title = {Social Norms and Random Matching Games},
119-
journal = {Games and Economic Behavior},
120-
volume = {9},
121-
number = {1},
122-
pages = {79 - 109},
123-
year = {1995},
124-
issn = {0899-8256},
125-
doi = {10.1006/game.1995.1006},
126-
author = {Okuno-Fujiwara, Masahiro and Postlewaite, Andrew},
127-
}
128-
129117
@article{roth1984,
130118
author = {Roth, Alvin},
131119
title = {The Evolution of the Labor Market for Medical Interns and

paper.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,10 @@ require the use of software to be solved in reasonable time.
8484

8585
# Statement of Need
8686

87-
Matching games have applications in a number of fields including social and
88-
market economics [@agar2017; @okun1995], wireless communication [@baya2016], and
87+
Matching games have applications in many fields where relationships between
88+
rational agents must be managed. Some example applications include: being able
89+
to inform on healthcare finance policy [@agar2017]; helping to reduce the
90+
complexity of automated wireless communication networks [@baya2016]; and
8991
education infrastructure [@chia2019]. Thus, having access to software
9092
implementations of algorithms that are able to solve such games is essential.
9193

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@
1919
keywords=["game-theory gale-shapley matching-games"],
2020
packages=find_packages("src"),
2121
package_dir={"": "src"},
22-
tests_require=["pytest", "numpy"],
22+
python_requires=">=3.5",
23+
tests_require=["pytest", "hypothesis", "numpy"],
2324
)

src/matching/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
""" Top-level imports only. """
22

3-
from .game import Game
3+
from .game import BaseGame
44
from .matching import Matching
55
from .player import Player
66
from .version import __version__

src/matching/game.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
""" The base game class for facilitating and solving matching games. """
22

3+
import abc
34

4-
class Game:
5-
""" A class to store information about, and facilitate the solving of, a
6-
matching game.
75

8-
**This is a base class and is not intended for use other than inheritance.**
6+
class BaseGame(metaclass=abc.ABCMeta):
7+
""" An abstract base class for facilitating various matching games.
98
109
Attributes
1110
----------
@@ -23,17 +22,14 @@ def __init__(self):
2322
self.matching = None
2423
self.blocking_pairs = None
2524

25+
@abc.abstractmethod
2626
def solve(self):
2727
""" Placeholder for solving the given matching game. """
2828

29-
raise NotImplementedError()
30-
29+
@abc.abstractmethod
3130
def check_stability(self):
3231
""" Placeholder for checking the stability of the current matching. """
3332

34-
raise NotImplementedError()
35-
33+
@abc.abstractmethod
3634
def check_validity(self):
3735
""" Placeholder for checking the validity of the current matching. """
38-
39-
raise NotImplementedError()

src/matching/games/hospital_resident.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
""" The HR solver and algorithm. """
22

3-
from matching import Game, Matching
3+
import copy
4+
5+
from matching import BaseGame, Matching
46
from matching import Player as Resident
57
from matching.players import Hospital
68

79
from .util import delete_pair, match_pair
810

911

10-
class HospitalResident(Game):
12+
class HospitalResident(BaseGame):
1113
""" A class for solving instances of the hospital-resident assignment
1214
problem (HR).
1315
@@ -40,8 +42,9 @@ class HospitalResident(Game):
4042
blocking pairs.
4143
"""
4244

43-
def __init__(self, residents=None, hospitals=None):
45+
def __init__(self, residents, hospitals):
4446

47+
residents, hospitals = copy.deepcopy([residents, hospitals])
4548
self.residents = residents
4649
self.hospitals = hospitals
4750

src/matching/games/stable_marriage.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
""" The SM solver and algorithm. """
22

3-
from matching import Game, Matching, Player
3+
import copy
4+
5+
from matching import BaseGame, Matching, Player
46

57
from .util import delete_pair, match_pair
68

79

8-
class StableMarriage(Game):
10+
class StableMarriage(BaseGame):
911
""" A class for solving instances of the stable marriage problem (SM).
1012
1113
Parameters
@@ -30,6 +32,7 @@ class StableMarriage(Game):
3032

3133
def __init__(self, suitors, reviewers):
3234

35+
suitors, reviewers = copy.deepcopy([suitors, reviewers])
3336
self.suitors = suitors
3437
self.reviewers = reviewers
3538

0 commit comments

Comments
 (0)