-
Notifications
You must be signed in to change notification settings - Fork 0
/
gametheory.py
76 lines (66 loc) · 2.32 KB
/
gametheory.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
"""
Solver for single-stage, zero-sum matrix-form games using scipy default
linear programming routines.
Original by Matthew Farrugia-Roberts, 2021
Students
* please note this implementation is not guaranteed to be free of errors,
for example it has not been extensively tested.
* please report bugs to <[email protected]>.
* please feel free adapt for your own use-case.
"""
import numpy as np
import scipy.optimize as opt
def solve_game(V, maximiser=True, rowplayer=True):
"""
Given a utility matrix V for a zero-sum game, compute a mixed-strategy
security strategy/Nash equilibrium solution along with the bound on the
expected value of the game to the player.
By default, assume the player is the MAXIMISER and chooses the ROW of V,
and the opponent is the MINIMISER choosing the COLUMN. Use the flags to
change this behaviour.
Parameters
----------
* V: (n, m)-array or array-like; utility/payoff matrix;
* maximiser: bool (default True); compute strategy for the maximiser.
Set False to play as the minimiser.
* rowplayer: bool (default True); compute strategy for the row-chooser.
Set False to play as the column-chooser.
Returns
-------
* s: (n,)-array; probability vector; an equilibrium mixed strategy over
the rows (or columns) ensuring expected value v.
* v: float; mixed security level / guaranteed minimum (or maximum)
expected value of the equilibrium mixed strategy.
Exceptions
----------
* OptimisationError: If the optimisation reports failure. The message
from the optimiser will accompany this exception.
"""
V = np.asarray(V)
# lprog will solve for the column-maximiser
if rowplayer:
V = V.T
if not maximiser:
V = -V
m, n = V.shape
# ensure positive
c = -V.min() + 1
Vpos = V + c
# solve linear program
res = opt.linprog(
np.ones(n),
A_ub=-Vpos,
b_ub=-np.ones(m),
options={'tol':1e-8},
)
if res.status:
raise OptimisationError(res.message) # TODO: propagate whole result
# compute strategy and value
v = 1 / res.x.sum()
s = res.x * v
v = v - c # re-scale
if not maximiser:
v = -v
return s, v
class OptimisationError(Exception):
"""For if the optimiser reports failure."""