def test_random_canongame(strats): """Test random canon games""" strats = np.array(strats) if np.all(strats == 1): return # not a game game = gamegen.normal_aggfn(2, strats, strats.sum()) cgame = canongame.canon(game) paygame.game_copy(cgame)
def test_conv_game_gambit_inv(game, game_str): """Test game to gambit and back""" with stdin(game_str), stdout() as out, stderr() as err: assert run('conv', 'gambit'), err.getvalue() with stdin(out.getvalue()), stdout() as out, stderr() as err: assert run('conv', 'game'), err.getvalue() copy = gamereader.loads(out.getvalue()) assert copy == paygame.game_copy(matgame.matgame_copy(game))
def test_random_min_max(strats): """Test min and max""" payoffs = rand.random(tuple(strats) + (len(strats), )) matg = matgame.matgame(payoffs) game = paygame.game_copy(matg) assert np.allclose(matg.min_strat_payoffs(), game.min_strat_payoffs()) assert np.allclose(matg.max_strat_payoffs(), game.max_strat_payoffs())
def test_profiles_payoffs(): """Test payoffs""" matg = matgame.matgame([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) copy = paygame.game_copy(matg) profs = [[1, 0, 1, 0], [1, 0, 0, 1], [0, 1, 1, 0], [0, 1, 0, 1]] pays = [[1, 0, 2, 0], [3, 0, 0, 4], [0, 5, 6, 0], [0, 7, 0, 8]] game = paygame.game([1, 1], 2, profs, pays) assert copy == game
def test_random_min_max(strats): """Test min and max""" payoffs = rand.random(tuple(strats) + (len(strats),)) matg = matgame.matgame(payoffs) game = paygame.game_copy(matg) assert np.allclose(matg.min_strat_payoffs(), game.min_strat_payoffs()) assert np.allclose(matg.max_strat_payoffs(), game.max_strat_payoffs())
def verify_aggfn(game): """Verify that aggfn matches the expanded version""" payoff_game = paygame.game_copy(game) assert not game.is_empty() assert game.is_complete() ngame = game.normalize() assert np.all( np.isclose(ngame.max_role_payoffs(), 1) | np.isclose(ngame.max_role_payoffs(), 0)) assert np.allclose(ngame.min_role_payoffs(), 0) # Check accuracy of min and max payoffs assert np.all( (payoff_game.payoffs() >= game.min_strat_payoffs() - 1e-6) | (payoff_game.profiles() == 0)) assert np.all( (payoff_game.payoffs() <= game.max_strat_payoffs() + 1e-6) | (payoff_game.profiles() == 0)) # Test that get payoffs works for multiple dimensions profiles = game.random_profiles(20).reshape((4, 5, -1)) payoffs = game.get_payoffs(profiles) true_payoffs = payoff_game.get_payoffs(profiles) assert np.allclose(payoffs, true_payoffs) # Check that we get the same deviations if we construct the full game # game deviation payoff jacobian is inaccurate for sparse mixtures, so we # can't use it as ground truth for mix in game.random_mixtures(20): idev, ijac = game.deviation_payoffs(mix, jacobian=True) tdev, tjac = payoff_game.deviation_payoffs(mix, jacobian=True) assert np.allclose(idev, tdev) tjac -= np.repeat(np.add.reduceat(tjac, game.role_starts, 1) / game.num_role_strats, game.num_role_strats, 1) ijac -= np.repeat(np.add.reduceat(ijac, game.role_starts, 1) / game.num_role_strats, game.num_role_strats, 1) assert np.allclose(ijac, tjac) # Check that sparse mixtures produce correct deviations # TODO For some reason, the jacobians don't match with the jacobians for # the standard payoff game when the mixture is sparse. My hunch is that the # aggfn version has payoff effects clse to zero that aren't captured by # simply recording the payoffs. However, this doesn't make a whole lot of # sense. for mix in game.random_sparse_mixtures(20): dev = game.deviation_payoffs(mix) tdev = payoff_game.deviation_payoffs(mix) assert np.allclose(dev, tdev) # Check that it serializes properly jgame = json.dumps(game.to_json()) copy = aggfn.aggfn_json(json.loads(jgame)) assert game == copy # As does it's normalized version jgame = json.dumps(ngame.to_json()) copy = aggfn.aggfn_json(json.loads(jgame)) assert ngame == copy
def test_rbfgame_min_max_payoffs(players, strats): """Test min and max payoffs of rbf game""" game = gamegen.sparse_game(players, strats, 11) reggame = learning.rbfgame_train(game) full = paygame.game_copy(reggame) assert np.all( full.min_strat_payoffs() >= reggame.min_strat_payoffs() - 1e-4) assert np.all( full.max_strat_payoffs() <= reggame.max_strat_payoffs() + 1e-4)
def test_random_game_addition(strats): """Test random addition""" mpayoffs = rand.random(tuple(strats) + (len(strats),)) matg = matgame.matgame(mpayoffs) payoffs = rand.random(matg.payoffs().shape) payoffs[matg.profiles() == 0] = 0 game = paygame.game_replace(matg, matg.profiles(), payoffs) assert paygame.game_copy(matg + game) == game + matg empty = rsgame.empty_copy(matg) assert matg + empty == empty
def test_random_game_addition(strats): """Test random addition""" mpayoffs = rand.random(tuple(strats) + (len(strats), )) matg = matgame.matgame(mpayoffs) payoffs = rand.random(matg.payoffs().shape) payoffs[matg.profiles() == 0] = 0 game = paygame.game_replace(matg, matg.profiles(), payoffs) assert paygame.game_copy(matg + game) == game + matg empty = rsgame.empty_copy(matg) assert matg + empty == empty
def test_random_matgame_hash_eq(strats): """Test hash and eq""" payoffs = rand.random(tuple(strats) + (len(strats), )) matg = matgame.matgame(payoffs) copy = matgame.matgame_copy(matg) assert hash(copy) == hash(matg) assert copy == matg game = paygame.game_copy(matg) copy = matgame.matgame_copy(game) assert hash(copy) == hash(matg) assert copy == matg
def test_random_matgame_hash_eq(strats): """Test hash and eq""" payoffs = rand.random(tuple(strats) + (len(strats),)) matg = matgame.matgame(payoffs) copy = matgame.matgame_copy(matg) assert hash(copy) == hash(matg) assert copy == matg game = paygame.game_copy(matg) copy = matgame.matgame_copy(game) assert hash(copy) == hash(matg) assert copy == matg
def reduce_game(full_game, red_players=None): """Return original game Parameters ---------- full_game : Game The game to reduce. red_players : ndarray-like, optional If specified, this must match the number of players per role in full_game. """ utils.check( red_players is None or np.all(full_game.num_role_players == red_players), 'identity reduction must have same number of players') return paygame.game_copy(full_game)
def reduce_game(full_game, red_players=None): """Return original game Parameters ---------- full_game : Game The game to reduce. red_players : ndarray-like, optional If specified, this must match the number of players per role in full_game. """ utils.check( red_players is None or np.all( full_game.num_role_players == red_players), 'identity reduction must have same number of players') return paygame.game_copy(full_game)
def __init__(self, game, conf, zipf, *, max_procs=4, simultaneous_obs=1): super().__init__(game.role_names, game.strat_names, game.num_role_players) self._game = paygame.game_copy(rsgame.empty_copy(game)) self.conf = conf self.zipf = zipf self._extra_profs = {} self._base = {} self._count = simultaneous_obs self._is_open = False self._sim_dir = None self._prof_dir = None self._sim_root = None self._num = 0 self._procs = asyncio.Semaphore(max_procs)
def __init__(self, game, config, command, buff_size=65536): super().__init__(game.role_names, game.strat_names, game.num_role_players) self._game = paygame.game_copy(rsgame.empty_copy(game)) self._base = {"configuration": config} self.command = command self.buff_size = buff_size self._is_open = False self._proc = None self._reader = None self._read_queue = asyncio.Queue() self._write_lock = asyncio.Lock() self._buffer_empty = asyncio.Event() self._buffer_bytes = 0 self._line_bytes = collections.deque() self._buffer_empty.set()
def test_random_deviations(strats): """Test random devs""" payoffs = rand.random(tuple(strats) + (len(strats), )) matg = matgame.matgame(payoffs) game = paygame.game_copy(matg) mix = matg.random_mixture() matdev = matg.deviation_payoffs(mix) gamedev = game.deviation_payoffs(mix) assert np.allclose(matdev, gamedev) matdev, matjac = matg.deviation_payoffs(mix, jacobian=True) gamedev, gamejac = game.deviation_payoffs(mix, jacobian=True) assert np.allclose(matdev, gamedev) assert np.allclose(matjac, gamejac) for mix in matg.random_mixtures(20): matdev, matjac = matg.deviation_payoffs(mix, jacobian=True) gamedev, gamejac = game.deviation_payoffs(mix, jacobian=True) assert np.allclose(matdev, gamedev) assert np.allclose(matjac, gamejac)
def test_random_deviations(strats): """Test random devs""" payoffs = rand.random(tuple(strats) + (len(strats),)) matg = matgame.matgame(payoffs) game = paygame.game_copy(matg) mix = matg.random_mixture() matdev = matg.deviation_payoffs(mix) gamedev = game.deviation_payoffs(mix) assert np.allclose(matdev, gamedev) matdev, matjac = matg.deviation_payoffs(mix, jacobian=True) gamedev, gamejac = game.deviation_payoffs(mix, jacobian=True) assert np.allclose(matdev, gamedev) assert np.allclose(matjac, gamejac) for mix in matg.random_mixtures(20): matdev, matjac = matg.deviation_payoffs(mix, jacobian=True) gamedev, gamejac = game.deviation_payoffs(mix, jacobian=True) assert np.allclose(matdev, gamedev) assert np.allclose(matjac, gamejac)
def test_neighbor(): # pylint: disable=too-many-locals """Test neighbor games""" game = gamegen.sparse_game([2, 3], [3, 2], 10) model = gp.GaussianProcessRegressor(1.0 * gp.kernels.RBF(2, [1, 3]) + gp.kernels.WhiteKernel(1), normalize_y=True) learn = learning.neighbor(learning.sklgame_train(game, model)) full = paygame.game_copy(learn) errors = np.zeros(game.num_strats) for i, mix in enumerate( itertools.chain(game.random_mixtures(20), game.random_sparse_mixtures(20)), 1): tdev = full.deviation_payoffs(mix) dev, _ = learn.deviation_payoffs(mix, jacobian=True) err = (tdev - dev)**2 / (np.abs(dev) + 1e-5) errors += (err - errors) / i assert np.all(errors < 5) submask = game.random_restriction() sublearn = learn.restrict(submask) subfull = full.restrict(submask) assert np.allclose(sublearn.get_payoffs(subfull.profiles()), subfull.payoffs()) norm = learn.normalize() assert np.allclose(norm.min_role_payoffs(), 0) assert np.allclose(norm.max_role_payoffs(), 1) assert learning.neighbor(learn, learn.num_neighbors) == learn learn = learning.neighbor(learning.rbfgame_train(game)) jgame = json.dumps(learn.to_json()) copy = learning.neighbor_json(json.loads(jgame)) assert hash(copy) == hash(learn) assert copy == learn assert learn + copy == copy + learn empty = rsgame.empty_copy(learn) assert learn + empty == empty
def test_continuous_approximation(base, num): # pylint: disable=too-many-locals """Test continuous approximation""" # pylint: disable-msg=protected-access game = gamegen.gen_num_profiles(base, num) learn = learning.rbfgame_train(game) full = paygame.game_copy(learn) red = np.eye(game.num_roles).repeat(game.num_role_strats, 0) size = np.eye(game.num_strats).repeat(learn._sizes, 0) def devpays(mix): """Compute dev pays""" players = learn._dev_players.repeat(game.num_role_strats, 1) avg_prof = players * mix diag = 1 / (learn._lengths**2 + avg_prof) diag_sizes = anp.dot(size, diag) diff = learn._profiles - anp.dot(size, avg_prof) det = 1 / (1 - learn._dev_players * anp.dot(mix**2 * diag, red)) det_sizes = anp.dot(size, det) cov_diag = anp.einsum('ij,ij,ij->i', diff, diff, diag_sizes) cov_outer = anp.dot(mix * diag_sizes * diff, red) sec_term = anp.einsum('ij,ij,ij,ij->i', learn._dev_players.repeat(learn._sizes, 0), det_sizes, cov_outer, cov_outer) exp = anp.exp(-(cov_diag + sec_term) / 2) coef = anp.prod(learn._lengths, 1) * anp.sqrt( anp.prod(diag, 1) * anp.prod(det, 1)) avg = anp.dot(learn._alpha * exp, size) return learn._coefs * coef * avg + learn._offset devpays_jac = autograd.jacobian(devpays) # pylint: disable=no-value-for-parameter for mix in itertools.chain(game.random_mixtures(20), game.random_sparse_mixtures(20)): dev = full.deviation_payoffs(mix) adev, ajac = learn.deviation_payoffs(mix, jacobian=True) assert np.allclose(adev, dev, rtol=0.1, atol=0.2) tdev = devpays(mix) tjac = devpays_jac(mix) assert np.allclose(adev, tdev) assert np.allclose(ajac, tjac)
def test_payoff_vals(): """Test payoff values""" profiles = [[2, 0, 1, 0, 0], [2, 0, 0, 1, 0], [2, 0, 0, 0, 1], [1, 1, 1, 0, 0], [1, 1, 0, 1, 0], [1, 1, 0, 0, 1], [0, 2, 1, 0, 0], [0, 2, 0, 1, 0], [0, 2, 0, 0, 1]] payoffs = [[4, 0, 8, 0, 0], [-1, 0, 0, 10, 0], [1, 0, 0, 0, 5], [1, 6, 6, 0, 0], [0, 3, 0, 5, 0], [-2, 3, 0, 0, 9], [0, 6, 11, 0, 0], [0, 3, 0, 6, 0], [0, 3, 0, 0, 10]] copy = paygame.game_replace(_GAME, profiles, payoffs) assert paygame.game_copy(_GAME) == copy
def test_random_identity(keep_prob, game_desc, _): """Test random identity""" players, strategies = game_desc # Create game and reduction game = gamegen.game(players, strategies, keep_prob) assert (paygame.game_copy(rsgame.empty(players, strategies)) == ir.reduce_game(rsgame.empty(players, strategies))) # Try to reduce game red_game = ir.reduce_game(game) # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( ir.reduce_profiles(red_game, game.profiles())) reduced_profiles = utils.axis_to_elem(red_game.profiles()) assert np.setxor1d(reduced_full_profiles, reduced_profiles).size == 0, \ "reduced game didn't match full game" full_profiles = utils.axis_to_elem(game.profiles()) full_reduced_profiles = utils.axis_to_elem( ir.expand_profiles(game, red_game.profiles())) assert np.setxor1d(full_profiles, full_reduced_profiles).size == 0, \ 'full game did not match reduced game'
def test_sample(): # pylint: disable=too-many-locals """Test sample game""" # pylint: disable-msg=protected-access game = gamegen.sparse_game([2, 3], [3, 2], 10) model = learning.sklgame_train( game, gp.GaussianProcessRegressor(1.0 * gp.kernels.RBF(2, [1, 3]) + gp.kernels.WhiteKernel(1), normalize_y=True)) learn = learning.sample(model) full = paygame.game_copy(learn) @autograd.primitive def sample_profs(mix): """Sample profiles""" return game.random_role_deviation_profiles(learn.num_samples, mix).astype(float) @autograd.primitive def model_pays(profs): """Get pays from model""" return model.get_dev_payoffs(profs) @autograd.primitive def const_weights(profs, mix): """Get the weights""" return np.prod(mix**profs, 2).repeat(game.num_role_strats, 1) @autograd.primitive def rep(probs): """Repeat an array""" return probs.repeat(game.num_role_strats, 1) def rep_vjp(_repd, _probs): """The jacobian of repeat""" return lambda grad: np.add.reduceat(grad, game.role_starts, 1) autograd.extend.defvjp(sample_profs, None) autograd.extend.defvjp(model_pays, None) autograd.extend.defvjp(const_weights, None, None) autograd.extend.defvjp(rep, rep_vjp) # This is wrong in autograd def devpays(mix): """Compute the dev pays""" profs = sample_profs(mix) payoffs = model_pays(profs) numer = rep(anp.prod(mix**profs, 2)) denom = const_weights(profs, mix) weights = numer / denom / learn.num_samples return anp.einsum('ij,ij->j', weights, payoffs) devpays_jac = autograd.jacobian(devpays) # pylint: disable=no-value-for-parameter errors = np.zeros(game.num_strats) samp_errors = np.zeros(game.num_strats) for i, mix in enumerate( itertools.chain(game.random_mixtures(20), game.random_sparse_mixtures(20)), 1): seed = random.randint(0, 10**9) fdev = full.deviation_payoffs(mix) np.random.seed(seed) dev, jac = learn.deviation_payoffs(mix, jacobian=True) avg_err = (fdev - dev)**2 / (np.abs(fdev) + 1e-5) errors += (avg_err - errors) / i samp_err = ((learn.deviation_payoffs(mix) - dev)**2 / (np.abs(dev) + 1e-5)) samp_errors += (samp_err - samp_errors) / i np.random.seed(seed) tdev = devpays(mix) assert np.allclose(dev, tdev) np.random.seed(seed) tjac = devpays_jac(mix) assert np.allclose(jac, tjac) assert np.all(errors <= 200 * (samp_errors + 1e-5)) submask = game.random_restriction() sublearn = learn.restrict(submask) subfull = full.restrict(submask) assert np.allclose(sublearn.get_payoffs(subfull.profiles()), subfull.payoffs()) norm = learn.normalize() assert np.allclose(norm.min_role_payoffs(), 0) assert np.allclose(norm.max_role_payoffs(), 1) assert learning.sample(learn, learn.num_samples) == learn learn = learning.sample(learning.rbfgame_train(game)) jgame = json.dumps(learn.to_json()) copy = learning.sample_json(json.loads(jgame)) assert hash(copy) == hash(learn) assert copy == learn assert learn + copy == copy + learn empty = rsgame.empty_copy(learn) assert learn + empty == empty
from gameanalysis import paygame from gameanalysis import rsgame _TYPES = { 'emptygame': ( ['empty'], 'Strip payoff data', """Strip all payoff data from a game and return only its base structure---role and strategy names and player counts.""", lambda game, out: json.dump( rsgame.empty_copy(game).to_json(), out)), 'game': ( [], 'Sparse payoff format', """Convert a game to a sparse mapping of profiles to their corresponding payoff data.""", lambda game, out: json.dump( paygame.game_copy(game).to_json(), out)), 'samplegame': ( ['samp'], 'Multiple payoffs per profile', """Convert a game to a format with a sparse mapping of profiles to potentially several samples of payoff data. There should be little reason to convert a non-samplegame to a samplegame as all profiles will have exactly one sample.""", lambda game, out: json.dump( paygame.samplegame_copy(game).to_json(), out)), 'matgame': ( ['mat'], 'Asymmetric format', """Convert a game to a compact representation for asymmetric games. If the input game is not asymmetric, role names will be duplicated and modified to allow for the conversion. This will only work if the input game is complete.""", lambda game, out: json.dump( matgame.matgame_copy(game).to_json(), out)), 'norm': (
from gameanalysis import gambit from gameanalysis import gamereader from gameanalysis import matgame from gameanalysis import paygame from gameanalysis import rsgame _TYPES = { 'emptygame': (['empty'], 'Strip payoff data', """Strip all payoff data from a game and return only its base structure---role and strategy names and player counts.""", lambda game, out: json.dump(rsgame.empty_copy(game).to_json(), out)), 'game': ([], 'Sparse payoff format', """Convert a game to a sparse mapping of profiles to their corresponding payoff data.""", lambda game, out: json.dump(paygame.game_copy(game).to_json(), out)), 'samplegame': (['samp'], 'Multiple payoffs per profile', """Convert a game to a format with a sparse mapping of profiles to potentially several samples of payoff data. There should be little reason to convert a non-samplegame to a samplegame as all profiles will have exactly one sample.""", lambda game, out: json.dump(paygame.samplegame_copy(game).to_json(), out) ), 'matgame': (['mat'], 'Asymmetric format', """Convert a game to a compact representation for asymmetric games. If the input game is not asymmetric, role names will be duplicated and modified to allow for the conversion. This will only work if the input game is complete.""", lambda game, out: json.dump(matgame.matgame_copy(game).to_json(), out)), 'norm': ([], 'Normalize payoffs to [0, 1]', """Modify the input game by scaling
def test_point(): # pylint: disable=too-many-locals """Test point We increase player number so point is a more accurate estimator. """ # pylint: disable-msg=protected-access game = gamegen.sparse_game(1000, 2, 10) model = learning.rbfgame_train(game) learn = learning.point(model) full = paygame.game_copy(learn) red = np.eye(game.num_roles).repeat(game.num_role_strats, 0) size = np.eye(game.num_strats).repeat(model._sizes, 0) def devpays(mix): """The deviation payoffs""" profile = learn._dev_players * mix dev_profiles = anp.dot(size, anp.dot(red, profile)) vec = ((dev_profiles - model._profiles) / model._lengths.repeat(model._sizes, 0)) rbf = anp.einsum('...ij,...ij->...i', vec, vec) exp = anp.exp(-rbf / 2) * model._alpha return model._offset + model._coefs * anp.dot(exp, size) devpays_jac = autograd.jacobian(devpays) # pylint: disable=no-value-for-parameter errors = np.zeros(game.num_strats) for i, mix in enumerate( itertools.chain(game.random_mixtures(20), game.random_sparse_mixtures(20)), 1): fdev = full.deviation_payoffs(mix) dev, jac = learn.deviation_payoffs(mix, jacobian=True) err = (fdev - dev)**2 / (np.abs(dev) + 1e-5) errors += (err - errors) / i tdev = devpays(mix) tjac = devpays_jac(mix) assert np.allclose(learn.deviation_payoffs(mix), dev) assert np.allclose(dev, tdev) assert np.allclose(jac, tjac) # Point is a very biased estimator, so errors are large assert np.all(errors < 10) submask = game.random_restriction() sublearn = learn.restrict(submask) subfull = full.restrict(submask) assert np.allclose(sublearn.get_payoffs(subfull.profiles()), subfull.payoffs()) norm = learn.normalize() assert np.allclose(norm.min_role_payoffs(), 0) assert np.allclose(norm.max_role_payoffs(), 1) assert learning.point(learn) == learn jgame = json.dumps(learn.to_json()) copy = learning.point_json(json.loads(jgame)) assert hash(copy) == hash(learn) assert copy == learn assert learn + copy == copy + learn empty = rsgame.empty_copy(learn) assert learn + empty == empty