-
Notifications
You must be signed in to change notification settings - Fork 2
/
gambitutils.py
175 lines (123 loc) · 5.89 KB
/
gambitutils.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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import logging
import subprocess
from string import Template
# TODO: Set a value according to your system
GAMBIT_DIR = "/Applications/Gambit.app/Contents/MacOS/"
# GAMBIT_DIR = "C:\\Program Files (x86)\\Gambit\\"
# GAMBIT_DIR = "/home/user/gambit-15.1.1/"
# These are the names of the solvers in Windows.
# ALL_EQUILIBRIA = "gambit-enumpoly.exe"
# PURE_EQUILIBRIA = "gambit-enumpure.exe"
ALL_EQUILIBRIA = "gambit-enumpoly"
PURE_EQUILIBRIA = "gambit-enumpure"
def start_nfg_section(nfg_file):
nfg_file.write("\n{")
def close_nfg_section(nfg_file):
nfg_file.write("}")
def register_profile_payoff(nfg_file, profile_name, payoffs):
payoff_strings = [str(payoff) for payoff in payoffs]
payoff_line = '{ "' + profile_name + '" ' + ",".join(payoff_strings) + " }"
nfg_file.write(payoff_line + "\n")
def write_profile_ordering(nfg_file, profile_ordering):
profile_ordering = " ".join(profile_ordering)
nfg_file.write("\n" + profile_ordering)
def start_nfg_file(game_description, strategies_catalogues):
first_line = 'NFG 1 R "$game_desc" { $player_catalog }'
first_line_template = Template(first_line)
players = set(['"Player_' + str(player_number) + '"' for player_number in range(len(strategies_catalogues))])
player_catalog = " ".join(players)
file_name = game_description + ".nfg"
with open(file_name, "w") as nfg_file:
nfg_file.write(first_line_template.substitute({
'game_desc': game_description,
'player_catalog': player_catalog}))
start_nfg_section(nfg_file)
for strategy_catalogue in strategies_catalogues:
actions = " ".join(['"' + strategy + '"' for strategy in strategy_catalogue])
nfg_file.write("{ " + actions + " }\n")
close_nfg_section(nfg_file)
return file_name
def get_strategic_game_format(game_desc, strategies_catalogues, profile_payoffs):
"""
Generates the content of a Gambit NFG file.
:return: Name of the generated file.
"""
template = 'NFG 1 R "$game_desc" { $player_catalog } \n\n ' \
'{ $actions_per_player \n}\n""\n\n' \
'{\n$payoff_per_profile\n}\n$profile_ordering'
nfg_template = Template(template)
players = set(['"Player_' + str(player_number) + '"' for player_number in range(len(strategies_catalogues))])
action_list = []
for strategies_catalog in strategies_catalogues:
actions = " ".join(['"' + strategy + '"' for strategy in strategies_catalog])
action_list.append("{ " + actions + " }")
profile_lines = []
profile_ordering = []
for index, profile_info in enumerate(profile_payoffs):
profile_name, payoffs = profile_info
payoff_strings = [str(payoff) for payoff in payoffs]
payoff_line = '{ "' + profile_name + '" ' + ",".join(payoff_strings) + " }"
profile_lines.append(payoff_line)
profile_ordering.append(str(index + 1))
player_catalog = " ".join(players)
actions_per_player = "\n".join(action_list)
payoff_per_profile = "\n".join(profile_lines)
profile_ordering = " ".join(profile_ordering)
file_content = nfg_template.substitute({
'game_desc': game_desc,
'player_catalog': player_catalog,
'actions_per_player': actions_per_player,
'payoff_per_profile': payoff_per_profile,
'profile_ordering': profile_ordering})
file_name = game_desc + ".nfg"
with open(file_name, "w") as gambit_file:
gambit_file.write(file_content)
return file_name
def calculate_equilibrium(strategy_catalogues, gambit_file, tool=PURE_EQUILIBRIA):
"""
Executes Gambit for equilibrium calculation.
:param tool: Gambit solver to use
:param strategy_catalogues: Catalog of available strategies.
:param gambit_file:
:return: List of equilibrium profiles.
"""
no_banner_option = "-q"
gambit_process = GAMBIT_DIR + tool
command_line = [gambit_process, no_banner_option, gambit_file]
logging.info("Starting equilibrium calculation using: " + gambit_process)
solver_process = subprocess.Popen(command_line, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = solver_process.communicate()
logging.info("Command-line output: Return Code " + str(solver_process.returncode))
if solver_process.returncode == 0:
logging.info("Command-line output: " + str(out))
nash_equilibrium_strings = str(out.decode()).splitlines()
if len(nash_equilibrium_strings) == 0:
logging.warning("NO EQUILIBRIA WAS FOUND FOR GAME " + gambit_file)
equilibrium_list = []
start_index = 3
for index, nash_equilibrium in enumerate(nash_equilibrium_strings):
logging.info("Equilibrium " + str(index + 1) + " of " + str(len(nash_equilibrium_strings)))
nash_equilibrium = nash_equilibrium.strip()
nash_equilibrium = nash_equilibrium[start_index:].split(",")
player_index = 0
strategy_index = 0
equilibrium_profile = {}
for probability in nash_equilibrium:
strategies_catalog = strategy_catalogues[player_index]
strategy_name = strategies_catalog[strategy_index]
if float(probability) > 0.0:
logging.info(
"Player " + str(player_index) + "-> Strategy: " + str(
strategy_name) + " \t\tProbability " + str(
probability))
equilibrium_profile[(player_index, strategy_index)] = probability
if strategy_index < len(strategies_catalog) - 1:
strategy_index += 1
else:
player_index += 1
strategy_index = 0
equilibrium_list.append(equilibrium_profile)
else:
logging.error("ERROR WHILE PROCESSING FILE: " + gambit_file + " . Error: " + str(err))
return
return equilibrium_list