noregret 0.0.0.dev9__tar.gz → 0.0.0.dev10__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.
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/PKG-INFO +1 -1
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/__init__.py +6 -2
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/__init__.py +2 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/black_box.py +76 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/regret_minimizers/stochastic.py +2 -3
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/tests/test_games.py +42 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/tests/test_regret_minimization.py +6 -3
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/utilities.py +0 -14
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret.egg-info/PKG-INFO +1 -1
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/setup.py +1 -1
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/LICENSE +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/README.rst +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/assurance-game.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/battle-of-the-sexes.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/chicken.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/gift-exchange-game.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/matching-pennies.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/prisoners-dilemma.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/pure-coordination.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/rock-paper-scissors-plus.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/rock-paper-scissors.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/rock-paper-superscissors.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/stag-hunt.json +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/extensive_form.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/games.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/multilinear.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/normal_form.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/kernels.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/regret_minimizers/__init__.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/regret_minimizers/probability_simplices.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/regret_minimizers/regret_minimizers.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/regret_minimizers/sequence_form_polytopes.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/sequence_form_polytopes.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/solvers/__init__.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/solvers/linear_programming.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/solvers/regret_minimization.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/tests/__init__.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/tests/test_linear_programming.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/tests/test_sequence_form_polytopes.py +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret.egg-info/SOURCES.txt +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret.egg-info/dependency_links.txt +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret.egg-info/requires.txt +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret.egg-info/top_level.txt +0 -0
- {noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/setup.cfg +0 -0
|
@@ -17,6 +17,7 @@ from noregret.games import (
|
|
|
17
17
|
RockPaperScissors,
|
|
18
18
|
RockPaperScissorsPlus,
|
|
19
19
|
RockPaperSuperscissors,
|
|
20
|
+
Simulation,
|
|
20
21
|
StagHunt,
|
|
21
22
|
StrategyProfile,
|
|
22
23
|
to_extensive_form_game,
|
|
@@ -67,7 +68,7 @@ from noregret.solvers import (
|
|
|
67
68
|
stochastic_regret_minimization,
|
|
68
69
|
symmetric_regret_minimization,
|
|
69
70
|
)
|
|
70
|
-
from noregret.utilities import import_object,
|
|
71
|
+
from noregret.utilities import import_object, tuple_or_none
|
|
71
72
|
|
|
72
73
|
BM = BlumMansour
|
|
73
74
|
"""Alias for :class:`noregret.BlumMansour`."""
|
|
@@ -121,6 +122,8 @@ RM = RegretMatching
|
|
|
121
122
|
"""Alias for :class:`noregret.RegretMatching`."""
|
|
122
123
|
rm = regret_minimization
|
|
123
124
|
"""Alias for :func:`noregret.regret_minimization`."""
|
|
125
|
+
Sim = Simulation
|
|
126
|
+
"""Alias for :class:`noregret.Simulation`."""
|
|
124
127
|
stochastic_rm = stochastic_regret_minimization
|
|
125
128
|
"""Alias for :func:`noregret.stochastic_regret_minimization`."""
|
|
126
129
|
symmetric_rm = symmetric_regret_minimization
|
|
@@ -194,10 +197,11 @@ __all__ = (
|
|
|
194
197
|
'RockPaperScissors',
|
|
195
198
|
'RockPaperScissorsPlus',
|
|
196
199
|
'RockPaperSuperscissors',
|
|
197
|
-
'sample',
|
|
198
200
|
'SequenceFormPolytope',
|
|
199
201
|
'SequenceFormPolytopeRegretMinimizer',
|
|
200
202
|
'Serializable',
|
|
203
|
+
'Sim',
|
|
204
|
+
'Simulation',
|
|
201
205
|
'StagHunt',
|
|
202
206
|
'stochastic_regret_minimization',
|
|
203
207
|
'StochasticRegretMinimizer',
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from noregret.games.black_box import (
|
|
3
3
|
BlackBoxGame,
|
|
4
4
|
open_spiel_game,
|
|
5
|
+
Simulation,
|
|
5
6
|
StrategyProfile,
|
|
6
7
|
UniformStrategyProfile,
|
|
7
8
|
)
|
|
@@ -53,6 +54,7 @@ __all__ = (
|
|
|
53
54
|
'RockPaperScissors',
|
|
54
55
|
'RockPaperScissorsPlus',
|
|
55
56
|
'RockPaperSuperscissors',
|
|
57
|
+
'Simulation',
|
|
56
58
|
'StagHunt',
|
|
57
59
|
'StrategyProfile',
|
|
58
60
|
'to_extensive_form_game',
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from functools import partial
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
from ordered_set import OrderedSet
|
|
7
8
|
from pyspiel import exploitability, GameType, load_game
|
|
@@ -9,6 +10,39 @@ from pyspiel import exploitability, GameType, load_game
|
|
|
9
10
|
from noregret.kernels import Kernel
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
@dataclass
|
|
14
|
+
class Simulation:
|
|
15
|
+
"""Class for simulations."""
|
|
16
|
+
kernel: Kernel
|
|
17
|
+
"""Kernel."""
|
|
18
|
+
players: list[int]
|
|
19
|
+
"""Players."""
|
|
20
|
+
decision_points: list[str | None]
|
|
21
|
+
"""Decision points."""
|
|
22
|
+
actions: list[str]
|
|
23
|
+
"""Actions."""
|
|
24
|
+
utilities: Any
|
|
25
|
+
"""Utilities."""
|
|
26
|
+
|
|
27
|
+
def sequences(self, player=None):
|
|
28
|
+
"""Return sequences given an optional player.
|
|
29
|
+
|
|
30
|
+
:param player: Optional player.
|
|
31
|
+
:return: Sequences.
|
|
32
|
+
"""
|
|
33
|
+
for i, j, a in zip(self.players, self.decision_points, self.actions):
|
|
34
|
+
if i is not None and (player is None or i == player):
|
|
35
|
+
yield j, a
|
|
36
|
+
|
|
37
|
+
def utility(self, player):
|
|
38
|
+
"""Return the utility given a player.
|
|
39
|
+
|
|
40
|
+
:param player: Player.
|
|
41
|
+
:return: Utility.
|
|
42
|
+
"""
|
|
43
|
+
return self.utilities[player]
|
|
44
|
+
|
|
45
|
+
|
|
12
46
|
@dataclass
|
|
13
47
|
class BlackBoxGame(ABC):
|
|
14
48
|
"""Abstract base class for black box games."""
|
|
@@ -140,11 +174,53 @@ class BlackBoxGame(ABC):
|
|
|
140
174
|
return np.array(ps, dtype)
|
|
141
175
|
|
|
142
176
|
def exploitability(self, strategy_profile):
|
|
177
|
+
"""Return exploitability given a strategy profile.
|
|
178
|
+
|
|
179
|
+
:param strategy_profile: Strategy profile.
|
|
180
|
+
:return: Exploitability.
|
|
181
|
+
"""
|
|
143
182
|
if not self.is_two_player or not self.is_zero_sum:
|
|
144
183
|
raise ValueError('not 2p0s')
|
|
145
184
|
|
|
146
185
|
raise NotImplementedError
|
|
147
186
|
|
|
187
|
+
def simulate(self, strategy_profile):
|
|
188
|
+
"""Run a simulation given a strategy profile.
|
|
189
|
+
|
|
190
|
+
:param strategy_profile: Strategy profile.
|
|
191
|
+
:return: Simulation.
|
|
192
|
+
"""
|
|
193
|
+
np = self.kernel.numpy
|
|
194
|
+
is_ = []
|
|
195
|
+
js = []
|
|
196
|
+
as_ = []
|
|
197
|
+
h = self.root_node
|
|
198
|
+
|
|
199
|
+
while A := self.actions(h):
|
|
200
|
+
i = self.player(h)
|
|
201
|
+
|
|
202
|
+
if i is None:
|
|
203
|
+
j = None
|
|
204
|
+
ps = self.chance_probabilities(h)
|
|
205
|
+
else:
|
|
206
|
+
j = self.information_set(h)
|
|
207
|
+
ps = strategy_profile(h)
|
|
208
|
+
|
|
209
|
+
a = np.random.choice(A, p=ps).item()
|
|
210
|
+
h = self.apply(h, a)
|
|
211
|
+
|
|
212
|
+
is_.append(i)
|
|
213
|
+
js.append(j)
|
|
214
|
+
as_.append(a)
|
|
215
|
+
|
|
216
|
+
is_ = tuple(is_)
|
|
217
|
+
js = tuple(js)
|
|
218
|
+
as_ = tuple(as_)
|
|
219
|
+
us = self.utilities(h)
|
|
220
|
+
simulation = Simulation(self.kernel, is_, js, as_, us)
|
|
221
|
+
|
|
222
|
+
return simulation
|
|
223
|
+
|
|
148
224
|
|
|
149
225
|
@dataclass
|
|
150
226
|
class _OpenSpielBlackBoxGame(BlackBoxGame):
|
|
@@ -10,7 +10,6 @@ from noregret.regret_minimizers.probability_simplices import (
|
|
|
10
10
|
ProbabilitySimplexRegretMinimizer,
|
|
11
11
|
RegretMatching,
|
|
12
12
|
)
|
|
13
|
-
from noregret.utilities import sample
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
@dataclass
|
|
@@ -105,7 +104,7 @@ class StochasticRegretMinimizer(ABC):
|
|
|
105
104
|
us[j] = np.array(u_primes, dtype)
|
|
106
105
|
u += us[j] @ ps
|
|
107
106
|
else:
|
|
108
|
-
a =
|
|
107
|
+
a = np.random.choice(A, p=ps).item()
|
|
109
108
|
h_prime = self.game.apply(h, a)
|
|
110
109
|
u += self._external_sampling(i, us, h_prime)
|
|
111
110
|
|
|
@@ -135,7 +134,7 @@ class StochasticRegretMinimizer(ABC):
|
|
|
135
134
|
else:
|
|
136
135
|
ps = self._action_probabilities(h)
|
|
137
136
|
|
|
138
|
-
k =
|
|
137
|
+
k = np.random.choice(len(A), p=ps)
|
|
139
138
|
a = A[k]
|
|
140
139
|
h_prime = self.game.apply(h, a)
|
|
141
140
|
p_prime = ps[k] * p
|
|
@@ -6,6 +6,7 @@ import noregret as nr
|
|
|
6
6
|
|
|
7
7
|
class GameTestCaseMixin(ABC):
|
|
8
8
|
KER = None
|
|
9
|
+
GAMES = None
|
|
9
10
|
|
|
10
11
|
@abstractmethod
|
|
11
12
|
def uniform_strategy_profile(self, game):
|
|
@@ -145,12 +146,43 @@ class ExtensiveFormGameTestCase(GameTestCaseMixin, TestCase):
|
|
|
145
146
|
self.assertEqual(sfp.parent_sequences, sfp2.parent_sequences)
|
|
146
147
|
|
|
147
148
|
|
|
149
|
+
class SimulationTestCase(TestCase):
|
|
150
|
+
KER = nr.FPKer()
|
|
151
|
+
|
|
152
|
+
def test_sequences(self):
|
|
153
|
+
np = self.KER.numpy
|
|
154
|
+
dtype = self.KER.data_type
|
|
155
|
+
sim = nr.Sim(
|
|
156
|
+
self.KER,
|
|
157
|
+
(0, None, 0, 1),
|
|
158
|
+
('', None, 'ab', 'b'),
|
|
159
|
+
('a', 'b', 'c', 'd'),
|
|
160
|
+
np.array([1, -1], dtype),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
self.assertEqual(
|
|
164
|
+
tuple(sim.sequences()),
|
|
165
|
+
(('', 'a'), ('ab', 'c'), ('b', 'd')),
|
|
166
|
+
)
|
|
167
|
+
self.assertEqual(tuple(sim.sequences(0)), (('', 'a'), ('ab', 'c')))
|
|
168
|
+
self.assertEqual(tuple(sim.sequences(1)), (('b', 'd'),))
|
|
169
|
+
|
|
170
|
+
def test_utility(self):
|
|
171
|
+
np = self.KER.numpy
|
|
172
|
+
dtype = self.KER.data_type
|
|
173
|
+
sim = nr.Sim(self.KER, (), (), (), np.array([1, -1], dtype))
|
|
174
|
+
|
|
175
|
+
self.assertEqual(sim.utility(0), 1)
|
|
176
|
+
self.assertEqual(sim.utility(1), -1)
|
|
177
|
+
|
|
178
|
+
|
|
148
179
|
class BlackBoxGameTestCase(TestCase):
|
|
149
180
|
KER = nr.FPKer()
|
|
150
181
|
GAMES = (
|
|
151
182
|
nr.open_spiel_game(KER, 'kuhn_poker'),
|
|
152
183
|
nr.open_spiel_game(KER, 'leduc_poker'),
|
|
153
184
|
)
|
|
185
|
+
SEED = 42
|
|
154
186
|
|
|
155
187
|
def test_actions_and_children(self):
|
|
156
188
|
for game in self.GAMES:
|
|
@@ -210,6 +242,16 @@ class BlackBoxGameTestCase(TestCase):
|
|
|
210
242
|
|
|
211
243
|
self.assertAlmostEqual(epsilon, epsilon2)
|
|
212
244
|
|
|
245
|
+
def test_simulation(self):
|
|
246
|
+
np = self.KER.numpy
|
|
247
|
+
|
|
248
|
+
for game in self.GAMES:
|
|
249
|
+
np.random.seed(self.SEED)
|
|
250
|
+
|
|
251
|
+
sigma = nr.UniformStrategyProfile(self.KER, game)
|
|
252
|
+
|
|
253
|
+
game.simulate(sigma)
|
|
254
|
+
|
|
213
255
|
|
|
214
256
|
if __name__ == '__main__':
|
|
215
257
|
main() # pragma: no cover
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from functools import partial
|
|
2
2
|
from math import inf
|
|
3
|
-
from random import seed
|
|
4
3
|
from unittest import main, TestCase
|
|
5
4
|
|
|
6
5
|
import noregret as nr
|
|
@@ -218,9 +217,11 @@ class StochasticRegretMinimizationTestCase(TestCase):
|
|
|
218
217
|
SEED = 42
|
|
219
218
|
|
|
220
219
|
def test_external_sampling(self):
|
|
220
|
+
np = self.KER.numpy
|
|
221
|
+
|
|
221
222
|
assert self.GAME.is_two_player and self.GAME.is_zero_sum
|
|
222
223
|
|
|
223
|
-
seed(self.SEED)
|
|
224
|
+
np.random.seed(self.SEED)
|
|
224
225
|
|
|
225
226
|
R = nr.MCCFR(self.KER, self.GAME)
|
|
226
227
|
sigma = nr.stochastic_rm(
|
|
@@ -235,9 +236,11 @@ class StochasticRegretMinimizationTestCase(TestCase):
|
|
|
235
236
|
self.assertLess(epsilon, self.TARGET_EXPLOITABILITY)
|
|
236
237
|
|
|
237
238
|
def test_outcome_sampling(self):
|
|
239
|
+
np = self.KER.numpy
|
|
240
|
+
|
|
238
241
|
assert self.GAME.is_two_player and self.GAME.is_zero_sum
|
|
239
242
|
|
|
240
|
-
seed(self.SEED)
|
|
243
|
+
np.random.seed(self.SEED)
|
|
241
244
|
|
|
242
245
|
R = nr.MCCFR(
|
|
243
246
|
self.KER,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""Module for utilities."""
|
|
2
2
|
from importlib import import_module
|
|
3
|
-
from random import choices
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
def import_object(object_path):
|
|
@@ -35,16 +34,3 @@ def tuple_or_none(values):
|
|
|
35
34
|
:return: Tuple or ``None``.
|
|
36
35
|
"""
|
|
37
36
|
return None if values is None else tuple(values)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def sample(values, probabilities):
|
|
41
|
-
"""Sample a random value as per the probabilities.
|
|
42
|
-
|
|
43
|
-
>>> sample(range(5), [0, 0, 1, 0, 0])
|
|
44
|
-
2
|
|
45
|
-
|
|
46
|
-
:param values: Values to be sampled from.
|
|
47
|
-
:param probabilities: The probabilities of sampling each value.
|
|
48
|
-
:return: The sampled value.
|
|
49
|
-
"""
|
|
50
|
-
return choices(values, probabilities)[0]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/battle-of-the-sexes.json
RENAMED
|
File without changes
|
|
File without changes
|
{noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/gift-exchange-game.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/rock-paper-scissors-plus.json
RENAMED
|
File without changes
|
{noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/rock-paper-scissors.json
RENAMED
|
File without changes
|
{noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/games/examples/rock-paper-superscissors.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/regret_minimizers/probability_simplices.py
RENAMED
|
File without changes
|
{noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/regret_minimizers/regret_minimizers.py
RENAMED
|
File without changes
|
{noregret-0.0.0.dev9 → noregret-0.0.0.dev10}/noregret/regret_minimizers/sequence_form_polytopes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|