From bcf8bd302b53a5484800b7962600d5c794bbbe34 Mon Sep 17 00:00:00 2001 From: Daisuke Oyama Date: Sun, 24 Nov 2024 22:27:48 +0900 Subject: [PATCH] [WIP] Implement GAMReader --- quantecon/game_theory/__init__.py | 1 + quantecon/game_theory/game_converters.py | 149 ++++++++++++++++++----- 2 files changed, 119 insertions(+), 31 deletions(-) diff --git a/quantecon/game_theory/__init__.py b/quantecon/game_theory/__init__.py index 03249c3c..9fb71f02 100644 --- a/quantecon/game_theory/__init__.py +++ b/quantecon/game_theory/__init__.py @@ -24,3 +24,4 @@ from .logitdyn import LogitDynamics from .polymatrix_game import PolymatrixGame from .howson_lcp import polym_lcp_solver +from .game_converters import GAMReader, qe_nfg_from_gam_file diff --git a/quantecon/game_theory/game_converters.py b/quantecon/game_theory/game_converters.py index 8218a3d6..2a3577a2 100644 --- a/quantecon/game_theory/game_converters.py +++ b/quantecon/game_theory/game_converters.py @@ -24,9 +24,124 @@ [[-19., -19., 1.], [ -8., -8., 2.], [ 3., 3., 3.]]]] """ +import numpy as np +from .normal_form_game import Player, NormalFormGame -from .normal_form_game import NormalFormGame -from itertools import product + +def str2num(s): + if '.' in s: + return float(s) + return int(s) + + +class GAMReader: + """ + Reader object that converts a game in GameTracer gam format into + a NormalFormGame. + + """ + @classmethod + def from_file(cls, file_path): + """ + Read from a gam format file. + + Parameters + ---------- + file_path : str + Path to gam file. + + Returns + ------- + NormalFormGame + + Examples + -------- + Save a gam format string in a temporary file: + + >>> import tempfile + >>> fname = tempfile.mkstemp()[1] + >>> with open(fname, mode='w') as f: + ... f.write(\"\"\"\\ + ... 2 + ... 3 2 + ... + ... 3 2 0 3 5 6 3 2 3 2 6 1\"\"\") + + Read the file: + + >>> g = GAMReader.from_file(fname) + >>> print(g) + 2-player NormalFormGame with payoff profile array: + [[[3, 3], [3, 2]], + [[2, 2], [5, 6]], + [[0, 3], [6, 1]]] + + """ + with open(file_path, 'r') as f: + string = f.read() + return cls.parse(string) + + @classmethod + def from_url(cls, url): + """ + Read from a URL. + + """ + import urllib.request + with urllib.request.urlopen(url) as response: + string = response.read().decode() + return cls.parse(string) + + @classmethod + def from_string(cls, string): + """ + Read from a gam format string. + + Parameters + ---------- + string : str + String in gam format. + + Returns + ------- + NormalFormGame + + Examples + -------- + >>> string = \"\"\"\\ + ... 2 + ... 3 2 + ... + ... 3 2 0 3 5 6 3 2 3 2 6 1\"\"\" + >>> g = GAMReader.from_string(string) + >>> print(g) + 2-player NormalFormGame with payoff profile array: + [[[3, 3], [3, 2]], + [[2, 2], [5, 6]], + [[0, 3], [6, 1]]] + + """ + return cls.parse(string) + + @staticmethod + def parse(string): + tokens = string.split() + + N = int(tokens.pop(0)) + nums_actions = tuple(int(tokens.pop(0)) for _ in range(N)) + payoffs = np.array([str2num(s) for s in tokens]) + + na = np.prod(nums_actions) + payoffs2d = payoffs.reshape(N, na) + players = [ + Player( + payoffs2d[i, :].reshape(nums_actions, order='F').transpose( + list(range(i, N)) + list(range(i)) + ) + ) for i in range(N) + ] + + return NormalFormGame(players) def qe_nfg_from_gam_file(filename: str) -> NormalFormGame: @@ -51,32 +166,4 @@ def qe_nfg_from_gam_file(filename: str) -> NormalFormGame: http://dags.stanford.edu/Games/gametracer.html """ - with open(filename, 'r') as file: - lines = file.readlines() - combined = [ - token - for line in lines - for token in line.split() - ] - - i = iter(combined) - players = int(next(i)) - actions = [int(next(i)) for _ in range(players)] - - nfg = NormalFormGame(actions) - - entries = [ - { - tuple(reversed(action_combination)): float(next(i)) - for action_combination in product( - *[range(a) for a in actions]) - } - for _ in range(players) - ] - - for action_combination in product(*[range(a) for a in actions]): - nfg[action_combination] = tuple( - entries[p][action_combination] for p in range(players) - ) - - return nfg + return GAMReader.from_file(filename)