def test_nan_deviations(players, strategies): game = gamegen.role_symmetric_game(players, strategies) for mix in game.random_mixtures(20, 0.05): mix = game.trim_mixture_support(mix) gains = regret.mixture_deviation_gains(game, mix) assert not np.isnan(gains).any(), \ "deviation gains in complete game were nan"
def test_json_copy_game(players, strategies): game1 = gamegen.role_symmetric_game(players, strategies) serial = gamegen.serializer(game1) game2, _ = gameio.read_game(serial.to_game_json(game1)) assert game1 == game2 assert np.all(game1.profiles == game2.profiles) assert np.allclose(game1.payoffs, game2.payoffs)
def test_maximal_subgames(players, strategies): game = gamegen.role_symmetric_game(players, strategies) subs = subgame.maximal_subgames(game) assert subs.shape[0] == 1, \ "found more than maximal subgame in a complete game" assert subs.all(), \ "found subgame wasn't the full one"
def create(args): assert len(args.pands) % 2 == 0, \ 'Must specify matching sets of players and strategies' game = gamegen.role_symmetric_game(args.pands[::2], args.pands[1::2]) serial = gamegen.game_serializer(game) return game, serial
def test_empty_add_noise(): base_game = rsgame.game([3, 3], [4, 4]) game = gamegen.add_noise(base_game, 1) assert game.is_empty() base_game = gamegen.role_symmetric_game([3] * 3, 4) game = gamegen.add_noise(base_game, 0) assert game.is_empty()
def test_game_to_str(): game = gamegen.role_symmetric_game([2, 1], [1, 2]) serial = gamegen.game_serializer(game) expected = ('Game:\n Roles: r0, r1\n Players:\n 2x r0\n' ' 1x r1\n Strategies:\n r0:\n s0\n' ' r1:\n s0\n s1\n' 'payoff data for 2 out of 2 profiles') assert game.to_str(serial) == expected
def test_mixture_regret_single_mix(players, strategies): num_boots = 200 game = gamegen.add_noise(gamegen.role_symmetric_game(players, strategies), 1, 3) mix = game.random_mixtures()[0] boots = bootstrap.mixture_regret(game, mix, num_boots, processes=1) assert boots.shape == (1, num_boots) assert np.all(boots >= 0)
def test_mixture_welfare(players, strategies): num_mixes = 5 num_boots = 200 game = gamegen.add_noise(gamegen.role_symmetric_game(players, strategies), 1, 3) mixes = game.random_mixtures(num_mixes) boots = bootstrap.mixture_welfare(game, mixes, num_boots, processes=1) assert boots.shape == (num_mixes, num_boots)
def test_game_function(players, strategies): game = gamegen.role_symmetric_game(players, strategies) # Test copy game2 = rsgame.Game(game) assert not np.may_share_memory(game.profiles, game2.profiles) assert not np.may_share_memory(game.payoffs, game2.payoffs) assert np.all(game.profiles == game2.profiles) assert np.all(game.payoffs == game2.payoffs) game3 = rsgame.Game(rsgame.BaseGame(game)) assert game3.is_empty() mask = game.profiles > 0 # Check that min payoffs are actually minimum min_payoffs = game.role_repeat(game.min_payoffs()) assert np.all((game.payoffs >= min_payoffs)[mask]), \ "not all payoffs less than min payoffs" max_payoffs = game.role_repeat(game.max_payoffs()) assert np.all((game.payoffs <= max_payoffs)[mask]), \ "not all payoffs greater than max payoffs" # Test profile methods for prof in game.profiles: game.get_payoffs(prof) # Works assert prof in game, "profile from game not in game" # Test expected payoff mix = game.random_mixtures()[0] dev1 = game.deviation_payoffs(mix) dev2, dev_jac = game.deviation_payoffs(mix, jacobian=True) assert not np.isnan(dev1).any() assert not np.isnan(dev_jac).any() assert np.allclose(dev1, dev2) pay1 = game.get_expected_payoffs(mix) pay2 = game.get_expected_payoffs(mix, deviations=dev1) pay3, jac1 = game.get_expected_payoffs(mix, jacobian=True) pay4, jac2 = game.get_expected_payoffs( mix, deviations=(dev1, dev_jac), jacobian=True) assert not np.isnan(pay1).any() assert (np.allclose(pay1, pay2) and np.allclose(pay1, pay3) and np.allclose(pay1, pay4)) assert not np.isnan(jac1).any() assert np.allclose(jac1, jac2) # Max social welfare welfare, profile = game.get_max_social_welfare() assert not np.isnan(welfare) assert profile is not None for welfare, profile in zip(*game.get_max_social_welfare(True)): assert not np.isnan(welfare) assert profile is not None # Test that various methods can be called assert repr(game) is not None
def test_mixture_regret_parallel(): num_mixes = 5 num_boots = 200 game = gamegen.add_noise(gamegen.role_symmetric_game([4, 3], [3, 4]), 1, 3) mixes = game.random_mixtures(num_mixes) boots = bootstrap.mixture_regret(game, mixes, num_boots) assert boots.shape == (num_mixes, num_boots) assert np.all(boots >= 0)
def test_json_copy_samplegame(game_size, samples): base = gamegen.role_symmetric_game(*game_size) game1 = gamegen.add_noise(base, 1, samples) serial = gamegen.serializer(game1) game2, _ = gameio.read_samplegame(serial.to_samplegame_json(game1)) assert game1 == game2 assert np.all(game1.profiles == game2.profiles) assert np.allclose(game1.payoffs, game2.payoffs) for spay1, spay2 in zip(game1.sample_payoffs, game2.sample_payoffs): assert np.allclose(spay1, spay2)
def test_add_noise(players, strategies, lower, upper): roles = max(np.array(players).size, np.array(strategies).size) base_game = gamegen.role_symmetric_game(players, strategies) game = gamegen.add_noise(base_game, lower, upper) assert lower == 0 or game.is_complete(), "didn't generate a full game" assert game.num_roles == roles, \ "didn't generate correct number of players" assert np.all(strategies == game.num_strategies), \ "didn't generate correct number of strategies" assert (np.all(game.num_samples >= min(lower, 1)) and np.all(game.num_samples <= upper)), \ "didn't generate appropriate number of samples"
def test_role_symmetric_game(players, strategies): game = gamegen.role_symmetric_game(players, strategies) assert game.is_complete(), "didn't generate a full game" assert np.all(players == game.num_players), \ "didn't generate correct number of strategies" assert np.all(strategies == game.num_strategies), \ "didn't generate correct number of strategies" conv = gamegen.serializer(game) assert conv.role_names == ('all',) or all( r.startswith('r') for r in conv.role_names) assert all(all(s.startswith('s') for s in strats) for strats in conv.strat_names)
def test_random_approximate_dpr(players, strategies): """Test approximate dpr preserves completeness on random games""" game = gamegen.role_symmetric_game(players, strategies) red_counts = 2 + (rand.random(game.num_roles) * (game.num_players - 1))\ .astype(int) red_counts[game.num_players == 1] = 1 red = reduction.DeviationPreserving(strategies, players, red_counts) # Try to reduce game red_game = red.reduce_game(game) # Assert that reducing all profiles covers reduced game assert red_game.is_complete(), "DPR did not preserve completeness"
def test_subgame_preserves_completeness(players, strategies, _): """Test that subgame function preserves completeness""" game = gamegen.role_symmetric_game(players, strategies) assert game.is_complete(), "gamegen didn't create complete game" mask = game.random_subgames() sub_game = subgame.subgame(game, mask) assert sub_game.is_complete(), "subgame didn't preserve game completeness" sgame = gamegen.add_noise(game, 1, 3) sub_sgame = subgame.subgame(sgame, mask) assert sub_sgame.is_complete(), \ "subgame didn't preserve sample game completeness"
def test_dpr(keep_prob, game_desc): """Simple test that dpr functions are consistent""" players, strategies, red_players = game_desc # Create game and reduction game = gamegen.role_symmetric_game(players, strategies) game = gamegen.drop_profiles(game, keep_prob) sgame = gamegen.add_noise(game, 1, 3) red = reduction.DeviationPreserving(strategies, players, red_players) # Try to reduce game assert rsgame.basegame_copy(game) == red.full_game assert red.reduce_game(rsgame.basegame_copy(game)) == red.red_game red_game = red.reduce_game(game) red_game2 = reduction.reduce_game_dpr(game, red_players) red_sgame = red.reduce_game(sgame) # Assert that reduce_game_dpr produces identical results reduced_profiles = utils.axis_to_elem(red_game.profiles) reduced_profiles2 = utils.axis_to_elem(red_game2.profiles) assert np.setxor1d(reduced_profiles, reduced_profiles2).size == 0, \ "different reduction functions didn't produce identical results" # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( red.reduce_profiles(game.profiles)) assert np.setdiff1d(reduced_profiles, reduced_full_profiles).size == 0, \ "reduced game contained profiles it shouldn't have" reduced_sample_profiles = utils.axis_to_elem(red_sgame.profiles) assert np.setdiff1d(reduced_sample_profiles, reduced_full_profiles).size == 0, \ "reduced sample game contained profiles it shouldn't have" assert np.setxor1d(reduced_sample_profiles, reduced_profiles).size == 0, \ "reduced sample game and reduced game had different profiles" # Assert that all contributing profiles are in the expansion of the reduced # game full_profiles = utils.axis_to_elem(game.profiles) full_reduced_profiles = utils.axis_to_elem( red.expand_profiles(red_game.profiles)) assert np.setdiff1d(full_reduced_profiles, full_profiles).size == 0, \ "full game did not have data for all profiles required of reduced" full_reduced_sample_profiles = utils.axis_to_elem( red.expand_profiles(red_sgame.profiles)) assert np.setdiff1d(full_reduced_sample_profiles, full_profiles).size == 0, \ ("full sample game did not have data for all profiles required of " "reduced") assert np.setxor1d(full_reduced_profiles, full_reduced_sample_profiles).size == 0, \ "sample game didn't produce identical results"
def test_mixture_regret(players, strategies): num_mixes = 5 num_boots = 200 game = gamegen.add_noise(gamegen.role_symmetric_game(players, strategies), 1, 3) mixes = game.random_mixtures(num_mixes) boots = bootstrap.mixture_regret(game, mixes, num_boots, processes=1) assert boots.shape == (num_mixes, num_boots) assert np.all(boots >= 0) perc_boots = bootstrap.mixture_regret(game, mixes, num_boots, [2.5, 97.5], processes=1) assert perc_boots.shape == (num_mixes, 2) assert np.all(perc_boots >= 0)
def test_sgboot_1(): with tempfile.NamedTemporaryFile('w') as mixed, \ tempfile.NamedTemporaryFile('w') as game: sgame = gamegen.add_noise(gamegen.role_symmetric_game([2, 3], [4, 3]), 20) serial = gamegen.serializer(sgame) json.dump(serial.to_samplegame_json(sgame), game) game.flush() profs = [serial.to_prof_json(sgame.uniform_mixture())] json.dump(profs, mixed) mixed.flush() run('sgboot', '-i', game.name, mixed.name, '-o/dev/null')
def test_learning(): with tempfile.NamedTemporaryFile('w') as game: sgame = gamegen.add_noise(gamegen.role_symmetric_game([2, 2], [3, 3]), 10) serial = gamegen.game_serializer(sgame) json.dump(sgame.to_json(serial), game) game.flush() assert not subprocess.run([GA, 'learning', '-i', game.name, '-o/dev/null', '-p1', '--dist-thresh', '1e-3', '-r1e-3', '-t1e-3', '--rand-restarts', '0', '-m10000', '-c1e-8']).returncode game.seek(0) assert not subprocess.run([GA, 'learning'], stdin=game).returncode
def test_drop_profiles(players, strategies): game = gamegen.role_symmetric_game(players, strategies) # Since independent drops might drop nothing, we keep nothing dropped = gamegen.drop_profiles(game, 0) assert dropped.is_empty(), "didn't drop any profiles" # 40% mean even one profile games will be incomplete dropped = gamegen.drop_profiles(game, 0.4, independent=False) assert not dropped.is_complete(), "didn't drop any profiles" sgame = gamegen.add_noise(game, 3) dropped = gamegen.drop_profiles(sgame, 0) assert dropped.is_empty(), "didn't drop any profiles" # 40% mean even one profile games will be incomplete dropped = gamegen.drop_profiles(sgame, 0.4, independent=False) assert not dropped.is_complete(), "didn't drop any profiles"
def test_sgboot_2(): with tempfile.NamedTemporaryFile('w') as mixed: sgame = gamegen.add_noise(gamegen.role_symmetric_game([2, 3], [4, 3]), 20) serial = gamegen.serializer(sgame) game_str = json.dumps(serial.to_samplegame_json(sgame)) profs = [serial.to_prof_json(sgame.uniform_mixture())] json.dump(profs, mixed) mixed.flush() string, _ = run('sgboot', mixed.name, '-tsurplus', '--processes', '1', '-n21', '-p', '5', '95', '-m', input=game_str) data = json.loads(string) assert all(j.keys() == {'5', '95', 'mean'} for j in data) assert all(j['5'] <= j['95'] for j in data)
def test_drop_samples(players, strategies): game = gamegen.role_symmetric_game(players, strategies) num_samples = 10000 // game.num_profiles game = gamegen.add_noise(game, num_samples) # Since independent drops might drop nothing, we keep nothing dropped = gamegen.drop_samples(game, 0) assert dropped.is_empty(), "didn't drop any profiles" # 40% mean even one profile games will be incomplete dropped = gamegen.drop_samples(game, 1) assert (dropped.is_complete() and np.all(dropped.num_samples == [num_samples])) # We drop half of samples, meaning is highly unlikely the game is complete # or empty, but these "can" still happen dropped = gamegen.drop_samples(game, .5) assert (not dropped.is_complete() or not np.all(dropped.num_samples == [num_samples])) assert not dropped.is_empty()
def test_approximate_dpr_reduce_game(): """Test approximate dpr game reduction""" game = gamegen.role_symmetric_game([3, 4], 2) red = reduction.DeviationPreserving([2, 2], [3, 4], [2, 2]) redgame = red.reduce_game(game) # Pure strategies are reduced properly assert (redgame.get_payoffs([2, 0, 0, 2])[0] == game.get_payoffs([3, 0, 0, 4])[0]) # Mixed strategies are reduced properly assert (redgame.get_payoffs([1, 1, 1, 1])[0] == game.get_payoffs([1, 2, 2, 2])[0]) assert (redgame.get_payoffs([1, 1, 1, 1])[1] == game.get_payoffs([2, 1, 2, 2])[1]) assert (redgame.get_payoffs([1, 1, 1, 1])[2] == game.get_payoffs([2, 1, 1, 3])[2]) assert (redgame.get_payoffs([1, 1, 1, 1])[3] == game.get_payoffs([2, 1, 3, 1])[3])
def test_sgboot(): with tempfile.NamedTemporaryFile('w') as mixed, \ tempfile.NamedTemporaryFile('w') as game: sgame = gamegen.add_noise(gamegen.role_symmetric_game([2, 3], [4, 3]), 20) serial = gamegen.game_serializer(sgame) json.dump(sgame.to_json(serial), game) game.flush() profs = [serial.to_prof_json(sgame.uniform_mixture())] json.dump(profs, mixed) mixed.flush() assert not subprocess.run([GA, 'sgboot', '-i', game.name, mixed.name, '-o/dev/null']).returncode game.seek(0) assert not subprocess.run([GA, 'sgboot', mixed.name, '-tsurplus', '--processes', '1', '-n21', '-p', '5', '95', '-m'], stdin=game).returncode
def test_sample_game_to_str(): base = gamegen.role_symmetric_game([2, 1], [1, 2]) serial = gamegen.game_serializer(base) game = gamegen.add_noise(base, 3) expected = ('SampleGame:\n Roles: r0, r1\n Players:\n 2x r0\n' ' 1x r1\n Strategies:\n r0:\n s0\n' ' r1:\n s0\n s1\n' 'payoff data for 2 out of 2 profiles\n' '3 observations per profile') assert game.to_str(serial) == expected game = rsgame.SampleGame(base) expected = ('SampleGame:\n Roles: r0, r1\n Players:\n 2x r0\n' ' 1x r1\n Strategies:\n r0:\n s0\n' ' r1:\n s0\n s1\n' 'payoff data for 2 out of 2 profiles\n' '1 observation per profile') assert game.to_str(serial) == expected game = rsgame.SampleGame(rsgame.BaseGame(base)) expected = ('SampleGame:\n Roles: r0, r1\n Players:\n 2x r0\n' ' 1x r1\n Strategies:\n r0:\n s0\n' ' r1:\n s0\n s1\n' 'payoff data for 0 out of 2 profiles\n' 'no observations') assert game.to_str(serial) == expected profiles = [[2, 1, 0], [2, 0, 1]] spayoffs = [ [[[1], [2], [0]]], [[[3, 4], [0] * 2, [5, 6]]], ] game = rsgame.SampleGame(base, profiles, spayoffs) expected = ('SampleGame:\n Roles: r0, r1\n Players:\n 2x r0\n' ' 1x r1\n Strategies:\n r0:\n s0\n' ' r1:\n s0\n s1\n' 'payoff data for 2 out of 2 profiles\n' '1 to 2 observations per profile') assert game.to_str(serial) == expected
def test_hierarchical(keep_prob, game_desc): players, strategies, red_players = game_desc # Create game and reduction game = gamegen.role_symmetric_game(players, strategies) game = gamegen.drop_profiles(game, keep_prob) sgame = gamegen.add_noise(game, 1, 3) red = reduction.Hierarchical(strategies, players, red_players) # Try to reduce game assert rsgame.basegame_copy(game) == red.full_game assert red.reduce_game(rsgame.basegame_copy(game)) == red.red_game red_game = red.reduce_game(game) red_sgame = red.reduce_game(sgame) # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( red.reduce_profiles(game.profiles)) reduced_profiles = utils.axis_to_elem(red_game.profiles) assert np.setxor1d(reduced_profiles, reduced_full_profiles).size == 0, \ "reduced game contained profiles it shouldn't have" reduced_sample_profiles = utils.axis_to_elem(red_sgame.profiles) assert np.setxor1d(reduced_sample_profiles, reduced_full_profiles).size == 0, \ "reduced game contained profiles it shouldn't have" # Assert that all contributing profiles are in the expansion of the reduced # game full_profiles = utils.axis_to_elem(game.profiles) full_reduced_profiles = utils.axis_to_elem( red.expand_profiles(red_game.profiles)) assert np.setdiff1d(full_reduced_profiles, full_profiles).size == 0, \ "full game did not have data for all profiles required of reduced" full_reduced_sample_profiles = utils.axis_to_elem( red.expand_profiles(red_sgame.profiles)) assert np.setdiff1d(full_reduced_sample_profiles, full_profiles).size == 0, \ "full game did not have data for all profiles required of reduced" assert np.setxor1d(full_reduced_profiles, full_reduced_sample_profiles).size == 0, \ "sample game didn't produce identical results"
def test_samplegame_resample(): game = gamegen.role_symmetric_game([1, 2, 3], [4, 3, 2]) game = gamegen.add_noise(game, 1, 20) payoffs = game.payoffs.copy() min_values = game.min_payoffs().copy() max_values = game.max_payoffs().copy() game.resample() # This isn't guaranteed to be true, but they're highly unlikely assert np.any(payoffs != game.payoffs), \ "resampling didn't change payoffs" game.remean() assert np.allclose(payoffs, game.payoffs), \ "remeaning didn't reset payoffs properly" assert np.allclose(min_values, game.min_payoffs()), \ "remeaning didn't reset minimum payoffs properly" assert np.allclose(max_values, game.max_payoffs()), \ "remeaning didn't reset minimum payoffs properly"
def test_identity(keep_prob, game_desc): players, strategies = game_desc # Create game and reduction game = gamegen.role_symmetric_game(players, strategies) game = gamegen.drop_profiles(game, keep_prob) red = reduction.Identity(strategies, players) # Try to reduce game red_game = red.reduce_game(game) # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( red.reduce_profiles(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( red.expand_profiles(red_game.profiles)) assert np.setxor1d(full_profiles, full_reduced_profiles).size == 0, \ "full game did not match reduced game"
def test_sample_game_function(game_size, samples): base = gamegen.role_symmetric_game(*game_size) game = gamegen.add_noise(base, 1, samples) # Test constructors game2 = rsgame.SampleGame(game) assert not np.may_share_memory(game.profiles, game2.profiles) assert not np.may_share_memory(game.payoffs, game2.payoffs) assert not any(np.may_share_memory(sp, sp2) for sp, sp2 in zip(game.sample_payoffs, game2.sample_payoffs)) assert np.all(game.profiles == game2.profiles) assert np.all(game.payoffs == game2.payoffs) assert all(np.all(sp == sp2) for sp, sp2 in zip(game.sample_payoffs, game2.sample_payoffs)) game3 = rsgame.SampleGame(base) assert not np.may_share_memory(base.profiles, game3.profiles) assert not np.may_share_memory(base.payoffs, game3.payoffs) assert np.all(base.profiles == game3.profiles) assert np.all(base.payoffs == game3.payoffs) assert np.all(game3.num_samples == 1) game4 = rsgame.SampleGame(rsgame.BaseGame(*game_size)) assert game4.is_empty() game5 = rsgame.SampleGame(*game_size) assert game5.is_empty() game5 = rsgame.SampleGame(game.num_players, game.num_strategies, game.profiles, game.sample_payoffs) # Test that various methods can be called assert (np.all(1 <= game.num_samples) and np.all(game.num_samples <= samples)) game.resample() game.resample(1) game.remean() assert repr(game) is not None
def test_sample_regret(players, strategies): n = 100 game = gamegen.role_symmetric_game(players, strategies) max_regret = np.max(game.max_payoffs() - game.min_payoffs()) mix = game.random_mixtures()[0] dev_profs = game.random_deviator_profiles(mix, n) dev_profs.shape = (-1, game.num_role_strats) inds = np.broadcast_to(np.arange(game.num_role_strats), (n, game.num_role_strats)).flat dev_payoffs = np.fromiter( (game.get_payoffs(p)[i] for p, i in zip(dev_profs, inds)), float, n * game.num_role_strats) dev_payoffs.shape = (n, game.num_role_strats) mix_payoffs = game.role_reduce(dev_payoffs * mix) boots = bootstrap.sample_regret(game, mix_payoffs, dev_payoffs, 200) assert boots.shape == (200,) assert np.all(boots <= max_regret) assert np.all(boots >= 0) perc_boots = bootstrap.sample_regret(game, mix_payoffs, dev_payoffs, 200, [2.5, 97.5]) assert perc_boots.shape == (2,) assert np.all(perc_boots <= max_regret) assert np.all(perc_boots >= 0)