gtlab 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gtlab-0.1.0/LICENSE +21 -0
- gtlab-0.1.0/PKG-INFO +137 -0
- gtlab-0.1.0/README.md +97 -0
- gtlab-0.1.0/pyproject.toml +54 -0
- gtlab-0.1.0/setup.cfg +4 -0
- gtlab-0.1.0/src/gtlab/__init__.py +39 -0
- gtlab-0.1.0/src/gtlab/_memo.py +31 -0
- gtlab-0.1.0/src/gtlab/core/__init__.py +17 -0
- gtlab-0.1.0/src/gtlab/core/bayesian.py +335 -0
- gtlab-0.1.0/src/gtlab/core/correlated.py +120 -0
- gtlab-0.1.0/src/gtlab/core/extensive_form.py +133 -0
- gtlab-0.1.0/src/gtlab/core/normal_form.py +176 -0
- gtlab-0.1.0/src/gtlab/core/stochastic.py +95 -0
- gtlab-0.1.0/src/gtlab/core/zero_sum.py +119 -0
- gtlab-0.1.0/src/gtlab/games/__init__.py +27 -0
- gtlab-0.1.0/src/gtlab/games/applied.py +36 -0
- gtlab-0.1.0/src/gtlab/games/classic.py +44 -0
- gtlab-0.1.0/src/gtlab/solvers/__init__.py +33 -0
- gtlab-0.1.0/src/gtlab/solvers/best_response.py +51 -0
- gtlab-0.1.0/src/gtlab/solvers/correlated.py +105 -0
- gtlab-0.1.0/src/gtlab/solvers/dominance.py +83 -0
- gtlab-0.1.0/src/gtlab/solvers/learning.py +95 -0
- gtlab-0.1.0/src/gtlab/solvers/linprog.py +78 -0
- gtlab-0.1.0/src/gtlab/solvers/nash.py +82 -0
- gtlab-0.1.0/src/gtlab/solvers/pareto.py +39 -0
- gtlab-0.1.0/src/gtlab/solvers/value_iteration.py +66 -0
- gtlab-0.1.0/src/gtlab/solvers/welfare.py +48 -0
- gtlab-0.1.0/src/gtlab/viz/__init__.py +12 -0
- gtlab-0.1.0/src/gtlab/viz/format.py +65 -0
- gtlab-0.1.0/src/gtlab/viz/html.py +171 -0
- gtlab-0.1.0/src/gtlab/viz/plots.py +103 -0
- gtlab-0.1.0/src/gtlab/viz/theme.py +101 -0
- gtlab-0.1.0/src/gtlab.egg-info/PKG-INFO +137 -0
- gtlab-0.1.0/src/gtlab.egg-info/SOURCES.txt +40 -0
- gtlab-0.1.0/src/gtlab.egg-info/dependency_links.txt +1 -0
- gtlab-0.1.0/src/gtlab.egg-info/requires.txt +17 -0
- gtlab-0.1.0/src/gtlab.egg-info/top_level.txt +1 -0
- gtlab-0.1.0/tests/test_bayesian.py +60 -0
- gtlab-0.1.0/tests/test_display.py +60 -0
- gtlab-0.1.0/tests/test_games.py +36 -0
- gtlab-0.1.0/tests/test_perf.py +114 -0
- gtlab-0.1.0/tests/test_solvers.py +72 -0
gtlab-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tamás Takács
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
gtlab-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gtlab
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Teaching toolkit for the ELTE Game Theory course: solvers, game library, and Jupyter-friendly visualizations.
|
|
5
|
+
Author-email: Tamás Takács <tamastheactual@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tamastheactual/gtlab
|
|
8
|
+
Project-URL: Repository, https://github.com/tamastheactual/gtlab
|
|
9
|
+
Project-URL: Issues, https://github.com/tamastheactual/gtlab/issues
|
|
10
|
+
Keywords: game-theory,nash-equilibrium,teaching,elte,education
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
22
|
+
Classifier: Topic :: Education
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: numpy>=1.21
|
|
27
|
+
Requires-Dist: scipy>=1.7
|
|
28
|
+
Requires-Dist: matplotlib>=3.4
|
|
29
|
+
Requires-Dist: pandas>=1.3
|
|
30
|
+
Provides-Extra: nash
|
|
31
|
+
Requires-Dist: nashpy>=0.0.35; extra == "nash"
|
|
32
|
+
Provides-Extra: welfare
|
|
33
|
+
Requires-Dist: pulp>=2.6; extra == "welfare"
|
|
34
|
+
Provides-Extra: full
|
|
35
|
+
Requires-Dist: nashpy>=0.0.35; extra == "full"
|
|
36
|
+
Requires-Dist: pulp>=2.6; extra == "full"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# gtlab — Game Theory Lab
|
|
42
|
+
|
|
43
|
+
A teaching toolkit for the ELTE Game Theory course. It consolidates the solvers,
|
|
44
|
+
example games, and Jupyter visualizations that previously lived (duplicated) in
|
|
45
|
+
six separate Colab notebooks into one installable, extensible package.
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install -e ".[full]" # full = nashpy + pulp extras (recommended in Colab)
|
|
51
|
+
pip install -e . # core only (numpy / scipy / matplotlib / pandas)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import gtlab
|
|
58
|
+
gtlab.apply_rc() # shared matplotlib styling (call once)
|
|
59
|
+
|
|
60
|
+
from gtlab.games import prisoners_dilemma
|
|
61
|
+
prisoners_dilemma().solve() # annotated bimatrix in a Jupyter cell
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Build your own game:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import numpy as np
|
|
68
|
+
from gtlab import NormalFormGame
|
|
69
|
+
|
|
70
|
+
g = NormalFormGame(
|
|
71
|
+
A=np.array([[3, 0], [5, 1]]),
|
|
72
|
+
B=np.array([[3, 5], [0, 1]]),
|
|
73
|
+
row_actions=["Cooperate", "Defect"],
|
|
74
|
+
col_actions=["Cooperate", "Defect"],
|
|
75
|
+
name="My Game",
|
|
76
|
+
)
|
|
77
|
+
g.explain()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Architecture
|
|
81
|
+
|
|
82
|
+
The design separates the three concerns that were tangled together in the
|
|
83
|
+
notebooks:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
gtlab/
|
|
87
|
+
├── core/ game classes — hold data, expose a thin API
|
|
88
|
+
│ ├── normal_form.py NormalFormGame
|
|
89
|
+
│ ├── zero_sum.py ZeroSumGame
|
|
90
|
+
│ ├── correlated.py CorrelatedGame
|
|
91
|
+
│ ├── stochastic.py StochasticGame
|
|
92
|
+
│ ├── extensive_form.py ExtensiveFormGame
|
|
93
|
+
│ └── bayesian.py PostedPrice, FirstPriceAuction, SecondPriceAuction
|
|
94
|
+
├── solvers/ pure algorithms — numpy in, numpy/dict out, no display
|
|
95
|
+
│ ├── best_response.py nash.py dominance.py
|
|
96
|
+
│ ├── pareto.py linprog.py value_iteration.py
|
|
97
|
+
│ ├── welfare.py learning.py correlated.py
|
|
98
|
+
├── viz/ display layer — ONE theme, formatters, HTML, plots
|
|
99
|
+
│ ├── theme.py format.py html.py plots.py
|
|
100
|
+
└── games/ ready-made example games + REGISTRY
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Rule of thumb: **math goes in `solvers/`, rendering goes in `viz/`, and the
|
|
104
|
+
classes in `core/` just wire them together.** A theme tweak is one edit in
|
|
105
|
+
`viz/theme.py`; a new algorithm is one file in `solvers/`; a new example is one
|
|
106
|
+
factory in `games/`.
|
|
107
|
+
|
|
108
|
+
## Extending
|
|
109
|
+
|
|
110
|
+
| To add… | …do this |
|
|
111
|
+
|---|---|
|
|
112
|
+
| a new example game | write a factory in `games/`, add it to `REGISTRY` |
|
|
113
|
+
| a new algorithm | add a pure function in `solvers/`, export it |
|
|
114
|
+
| a new game type | add a class in `core/` that calls `solvers` + `viz` |
|
|
115
|
+
| restyle output | edit `viz/theme.py` (colors / CSS / rcParams) |
|
|
116
|
+
|
|
117
|
+
## Tests
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pip install -e ".[dev]"
|
|
121
|
+
pytest
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The solver layer has golden-value tests so refactors stay behavior-preserving.
|
|
125
|
+
|
|
126
|
+
## Notebook migration
|
|
127
|
+
|
|
128
|
+
Each lecture notebook drops its ~2,000-line engine class and imports from
|
|
129
|
+
`gtlab` instead, keeping only narrative and example-specific calls. The methods
|
|
130
|
+
the notebooks relied on (`.summary()`, `.solve()`, `.explain()`, `.plot_*()`)
|
|
131
|
+
are preserved on the core classes.
|
|
132
|
+
```python
|
|
133
|
+
# old: 1,800 lines of NormalFormGame defined inline
|
|
134
|
+
# new:
|
|
135
|
+
from gtlab.games import prisoners_dilemma
|
|
136
|
+
prisoners_dilemma().solve(show_br=True, show_ne=True)
|
|
137
|
+
```
|
gtlab-0.1.0/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# gtlab — Game Theory Lab
|
|
2
|
+
|
|
3
|
+
A teaching toolkit for the ELTE Game Theory course. It consolidates the solvers,
|
|
4
|
+
example games, and Jupyter visualizations that previously lived (duplicated) in
|
|
5
|
+
six separate Colab notebooks into one installable, extensible package.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install -e ".[full]" # full = nashpy + pulp extras (recommended in Colab)
|
|
11
|
+
pip install -e . # core only (numpy / scipy / matplotlib / pandas)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import gtlab
|
|
18
|
+
gtlab.apply_rc() # shared matplotlib styling (call once)
|
|
19
|
+
|
|
20
|
+
from gtlab.games import prisoners_dilemma
|
|
21
|
+
prisoners_dilemma().solve() # annotated bimatrix in a Jupyter cell
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Build your own game:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import numpy as np
|
|
28
|
+
from gtlab import NormalFormGame
|
|
29
|
+
|
|
30
|
+
g = NormalFormGame(
|
|
31
|
+
A=np.array([[3, 0], [5, 1]]),
|
|
32
|
+
B=np.array([[3, 5], [0, 1]]),
|
|
33
|
+
row_actions=["Cooperate", "Defect"],
|
|
34
|
+
col_actions=["Cooperate", "Defect"],
|
|
35
|
+
name="My Game",
|
|
36
|
+
)
|
|
37
|
+
g.explain()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Architecture
|
|
41
|
+
|
|
42
|
+
The design separates the three concerns that were tangled together in the
|
|
43
|
+
notebooks:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
gtlab/
|
|
47
|
+
├── core/ game classes — hold data, expose a thin API
|
|
48
|
+
│ ├── normal_form.py NormalFormGame
|
|
49
|
+
│ ├── zero_sum.py ZeroSumGame
|
|
50
|
+
│ ├── correlated.py CorrelatedGame
|
|
51
|
+
│ ├── stochastic.py StochasticGame
|
|
52
|
+
│ ├── extensive_form.py ExtensiveFormGame
|
|
53
|
+
│ └── bayesian.py PostedPrice, FirstPriceAuction, SecondPriceAuction
|
|
54
|
+
├── solvers/ pure algorithms — numpy in, numpy/dict out, no display
|
|
55
|
+
│ ├── best_response.py nash.py dominance.py
|
|
56
|
+
│ ├── pareto.py linprog.py value_iteration.py
|
|
57
|
+
│ ├── welfare.py learning.py correlated.py
|
|
58
|
+
├── viz/ display layer — ONE theme, formatters, HTML, plots
|
|
59
|
+
│ ├── theme.py format.py html.py plots.py
|
|
60
|
+
└── games/ ready-made example games + REGISTRY
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Rule of thumb: **math goes in `solvers/`, rendering goes in `viz/`, and the
|
|
64
|
+
classes in `core/` just wire them together.** A theme tweak is one edit in
|
|
65
|
+
`viz/theme.py`; a new algorithm is one file in `solvers/`; a new example is one
|
|
66
|
+
factory in `games/`.
|
|
67
|
+
|
|
68
|
+
## Extending
|
|
69
|
+
|
|
70
|
+
| To add… | …do this |
|
|
71
|
+
|---|---|
|
|
72
|
+
| a new example game | write a factory in `games/`, add it to `REGISTRY` |
|
|
73
|
+
| a new algorithm | add a pure function in `solvers/`, export it |
|
|
74
|
+
| a new game type | add a class in `core/` that calls `solvers` + `viz` |
|
|
75
|
+
| restyle output | edit `viz/theme.py` (colors / CSS / rcParams) |
|
|
76
|
+
|
|
77
|
+
## Tests
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install -e ".[dev]"
|
|
81
|
+
pytest
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The solver layer has golden-value tests so refactors stay behavior-preserving.
|
|
85
|
+
|
|
86
|
+
## Notebook migration
|
|
87
|
+
|
|
88
|
+
Each lecture notebook drops its ~2,000-line engine class and imports from
|
|
89
|
+
`gtlab` instead, keeping only narrative and example-specific calls. The methods
|
|
90
|
+
the notebooks relied on (`.summary()`, `.solve()`, `.explain()`, `.plot_*()`)
|
|
91
|
+
are preserved on the core classes.
|
|
92
|
+
```python
|
|
93
|
+
# old: 1,800 lines of NormalFormGame defined inline
|
|
94
|
+
# new:
|
|
95
|
+
from gtlab.games import prisoners_dilemma
|
|
96
|
+
prisoners_dilemma().solve(show_br=True, show_ne=True)
|
|
97
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gtlab"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Teaching toolkit for the ELTE Game Theory course: solvers, game library, and Jupyter-friendly visualizations."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Tamás Takács", email = "tamastheactual@gmail.com" }]
|
|
13
|
+
keywords = ["game-theory", "nash-equilibrium", "teaching", "elte", "education"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Education",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
26
|
+
"Topic :: Education",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
dependencies = [
|
|
30
|
+
"numpy>=1.21",
|
|
31
|
+
"scipy>=1.7",
|
|
32
|
+
"matplotlib>=3.4",
|
|
33
|
+
"pandas>=1.3",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
# Mixed-Nash via support enumeration in normal/extensive-form games.
|
|
38
|
+
nash = ["nashpy>=0.0.35"]
|
|
39
|
+
# Linear-programming welfare objectives (utilitarian / egalitarian).
|
|
40
|
+
welfare = ["pulp>=2.6"]
|
|
41
|
+
# Everything, for running the full notebook suite.
|
|
42
|
+
full = ["nashpy>=0.0.35", "pulp>=2.6"]
|
|
43
|
+
dev = ["pytest>=7.0"]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://github.com/tamastheactual/gtlab"
|
|
47
|
+
Repository = "https://github.com/tamastheactual/gtlab"
|
|
48
|
+
Issues = "https://github.com/tamastheactual/gtlab/issues"
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
testpaths = ["tests"]
|
gtlab-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""gtlab — Game Theory Lab for the ELTE Game Theory course.
|
|
2
|
+
|
|
3
|
+
Quick start::
|
|
4
|
+
|
|
5
|
+
import gtlab
|
|
6
|
+
gtlab.apply_rc() # consistent plot styling (once)
|
|
7
|
+
|
|
8
|
+
from gtlab.games import prisoners_dilemma
|
|
9
|
+
prisoners_dilemma().solve() # annotated bimatrix in Jupyter
|
|
10
|
+
|
|
11
|
+
Build your own::
|
|
12
|
+
|
|
13
|
+
from gtlab import NormalFormGame
|
|
14
|
+
import numpy as np
|
|
15
|
+
g = NormalFormGame(np.array([[3, 0], [5, 1]]), np.array([[3, 5], [0, 1]]))
|
|
16
|
+
g.explain()
|
|
17
|
+
|
|
18
|
+
Layers:
|
|
19
|
+
* ``gtlab.core`` — game classes (data + thin API)
|
|
20
|
+
* ``gtlab.solvers`` — pure algorithms (best response, Nash, value iteration, …)
|
|
21
|
+
* ``gtlab.viz`` — formatting, HTML, plots, theme
|
|
22
|
+
* ``gtlab.games`` — ready-made example games
|
|
23
|
+
"""
|
|
24
|
+
from . import games, solvers, viz
|
|
25
|
+
from .core import (CorrelatedGame, ExtensiveFormGame, FirstPriceAuction,
|
|
26
|
+
Mechanism, NormalFormGame, PostedPrice, Procurement,
|
|
27
|
+
PublicProject, SecondPriceAuction, SpenceSignaling,
|
|
28
|
+
StochasticGame, VCGAssignment, ZeroSumGame)
|
|
29
|
+
from .viz import apply_rc
|
|
30
|
+
|
|
31
|
+
__version__ = "0.1.0"
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"NormalFormGame", "ZeroSumGame", "CorrelatedGame", "StochasticGame",
|
|
35
|
+
"ExtensiveFormGame", "Mechanism", "PostedPrice", "FirstPriceAuction",
|
|
36
|
+
"SecondPriceAuction", "SpenceSignaling", "VCGAssignment", "PublicProject",
|
|
37
|
+
"Procurement",
|
|
38
|
+
"solvers", "viz", "games", "apply_rc", "__version__",
|
|
39
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""A tiny per-instance memoization decorator for game-class analysis methods.
|
|
2
|
+
|
|
3
|
+
Game classes are dataclasses wrapping numpy arrays, so they are unhashable and
|
|
4
|
+
``functools.lru_cache`` does not apply. The payoff data is treated as immutable
|
|
5
|
+
after construction, so caching results on the instance is safe and lets repeated
|
|
6
|
+
display calls (and ``compare_via``) reuse expensive solves instead of redoing
|
|
7
|
+
them. Mutating a payoff matrix in place after the first call is unsupported
|
|
8
|
+
(call :func:`clear_cache` if you must).
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import functools
|
|
13
|
+
from typing import Callable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def cached_method(func: Callable) -> Callable:
|
|
17
|
+
"""Memoize a method's result on the instance, keyed by ``(name, args)``."""
|
|
18
|
+
@functools.wraps(func)
|
|
19
|
+
def wrapper(self, *args, **kwargs):
|
|
20
|
+
cache = self.__dict__.setdefault("_cache", {})
|
|
21
|
+
key = (func.__name__, args, tuple(sorted(kwargs.items())))
|
|
22
|
+
if key not in cache:
|
|
23
|
+
cache[key] = func(self, *args, **kwargs)
|
|
24
|
+
return cache[key]
|
|
25
|
+
|
|
26
|
+
return wrapper
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def clear_cache(instance) -> None:
|
|
30
|
+
"""Drop all memoized results on ``instance`` (use after mutating payoffs)."""
|
|
31
|
+
instance.__dict__.pop("_cache", None)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Core game classes — each holds data and delegates math/display to the
|
|
2
|
+
shared :mod:`gtlab.solvers` and :mod:`gtlab.viz` layers."""
|
|
3
|
+
from .bayesian import (FirstPriceAuction, Mechanism, PostedPrice, Procurement,
|
|
4
|
+
PublicProject, SecondPriceAuction, SpenceSignaling,
|
|
5
|
+
VCGAssignment)
|
|
6
|
+
from .correlated import CorrelatedGame
|
|
7
|
+
from .extensive_form import ExtensiveFormGame
|
|
8
|
+
from .normal_form import NormalFormGame
|
|
9
|
+
from .stochastic import StochasticGame
|
|
10
|
+
from .zero_sum import ZeroSumGame
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"NormalFormGame", "ZeroSumGame", "CorrelatedGame", "StochasticGame",
|
|
14
|
+
"ExtensiveFormGame", "Mechanism", "PostedPrice", "FirstPriceAuction",
|
|
15
|
+
"SecondPriceAuction", "SpenceSignaling", "VCGAssignment", "PublicProject",
|
|
16
|
+
"Procurement",
|
|
17
|
+
]
|