async def test_sparse_trace(): """Test that tracing sparsely samples profiles""" base = rsgame.empty(4, 3) game1 = paygame.game_replace(base, base.all_profiles(), (base.all_profiles() > 0) * [1, 0, 0]) game2 = paygame.game_replace(base, base.all_profiles(), (base.all_profiles() > 0) * [-0.5, 1.5, 0]) save1 = savesched.savesched(gamesched.gamesched(game1)) save2 = savesched.savesched(gamesched.gamesched(game2)) sgame1 = schedgame.schedgame(save1) sgame2 = schedgame.schedgame(save2) await asyncio.gather(innerloop.inner_loop(sgame1), innerloop.inner_loop(sgame2)) # Assert that innerloop doesn't scheduler all profiles assert save1.get_game().num_profiles == 11 assert save2.get_game().num_profiles == 11 ((st1, *_, en1), _), ((st2, *_, en2), _) = ( # pylint: disable=too-many-star-expressions await trace.trace_all_equilibria(sgame1, sgame2)) # Assert that trace found the expected equilibria assert np.isclose(st1, 0) assert np.isclose(en1, 1 / 3, atol=1e-3) assert np.isclose(st2, 1 / 3, atol=1e-3) assert np.isclose(en2, 1) # Assert that trace didn't need many extra profiles assert save1.get_game().num_profiles == 12 assert save2.get_game().num_profiles == 12
async def test_random_trace_sched(base): """Test tracing for random schedulers""" sched1 = gamesched.gamesched(gamegen.game_replace(base)) sched2 = gamesched.gamesched(gamegen.game_replace(base)) traces = await trace.trace_all_equilibria( schedgame.schedgame(sched1), schedgame.schedgame(sched2), style="one" ) verify_complete_traces(traces)
async def test_nash_failure(): """With regret thresh of zero, nash will fail""" game = gamegen.sym_2p2s_known_eq(1 / 3) sched = gamesched.gamesched(game) eqa = await innerloop.inner_loop(schedgame.schedgame(sched), regret_thresh=0) assert not eqa.size
async def test_innerloop_failures(players, strats, count, when): """Test that inner loop handles exceptions during scheduling""" game = gamegen.game(players, strats) sched = gamesched.gamesched(game) esched = tu.ExceptionScheduler(sched, count, when) sgame = schedgame.schedgame(esched) with pytest.raises(tu.SchedulerException): await innerloop.inner_loop(sgame, restricted_game_size=5)
async def test_innerloop_game(base): """Test that inner loop works for a game""" game = gamegen.samplegame_replace(base) sched = gamesched.gamesched(game) eqas = await innerloop.inner_loop(schedgame.schedgame(sched)) verify_dist_thresh(eqas) eqag = await innerloop.inner_loop(asyncgame.wrap(game)) assert utils.allclose_perm(eqas, eqag, atol=1e-3, rtol=1e3)
async def test_random_mean_reg(players, strats): """Test that no bootstraps works""" game = gamegen.game(players, strats) mix = game.random_mixture() sched = gamesched.gamesched(game) mean, boot = await bootstrap.deviation_payoffs(sched, mix, 20) assert mean.shape == (game.num_strats, ) assert boot.shape == (0, game.num_strats)
async def test_basic_profile(players, strats): """Test that basic profile sampling works""" game = gamegen.game(players, strats) basesched = gamesched.gamesched(game) sched = canonsched.canon(basesched) assert np.all(sched.num_role_strats > 1) pay = await sched.sample_payoffs(sched.random_profile()) assert pay.size == sched.num_strats assert str(sched) == str(basesched)
async def test_basic_profile_aggfn(): """Test using an action graph game""" agame = gamegen.normal_aggfn([4, 3], [3, 4], 5) profs = agame.random_profiles(20) sched = gamesched.gamesched(agame) paylist = await asyncio.gather(*[sched.sample_payoffs(p) for p in profs]) pays = np.stack(paylist) assert np.allclose(pays[profs == 0], 0)
async def test_innerloop_known_eq(eq_prob): """Test that inner loop finds known equilibria""" game = gamegen.sym_2p2s_known_eq(eq_prob) sched = gamesched.gamesched(game) eqa = await innerloop.inner_loop(schedgame.schedgame(sched), devs_by_role=True) assert eqa.size, "didn't find equilibrium" expected = [eq_prob, 1 - eq_prob] assert np.isclose(eqa, expected, atol=1e-3, rtol=1e-3).all(-1).any() verify_dist_thresh(eqa)
async def test_basic_profile(): """Test basic profile""" game = gamegen.game([4, 3], [3, 4]) profs = game.random_profiles(20) sched = gamesched.gamesched(game) assert rsgame.empty_copy(sched) == rsgame.empty_copy(game) paylist = await asyncio.gather(*[sched.sample_payoffs(p) for p in profs]) pays = np.stack(paylist) assert np.allclose(pays[profs == 0], 0) assert str(sched) == repr(game)
async def test_backups_used(): """Test that outerloop uses backups Since restricted game size is 1, but the only equilibria has support two, this must use backups to find an equilibrium.""" sgame = gamegen.sym_2p2s_known_eq(0.5) sched = gamesched.gamesched(sgame) eqa = await innerloop.inner_loop(schedgame.schedgame(sched), restricted_game_size=1) assert eqa.size, "didn't find equilibrium" expected = [0.5, 0.5] assert np.isclose(eqa, expected, atol=1e-3, rtol=1e-3).all(-1).any() verify_dist_thresh(eqa)
async def test_duplicate_prof(): """Test that duplicate profiles can be scheduled""" game = gamegen.game([4, 3], [3, 4]) profs = game.random_profiles(20) sched = gamesched.gamesched(game) paylist1 = await asyncio.gather(*[sched.sample_payoffs(p) for p in profs]) pays1 = np.stack(paylist1) paylist2 = await asyncio.gather(*[sched.sample_payoffs(p) for p in profs]) pays2 = np.stack(paylist2) assert np.allclose(pays1[profs == 0], 0) assert np.allclose(pays2[profs == 0], 0) assert np.allclose(pays1, pays2)
async def test_random_pure_boot_reg(players, strats): """Test that bootstrap works for pure mixtures""" game = gamegen.game(players, strats) sched = gamesched.gamesched(game) for mix in game.pure_mixtures(): devs = game.deviation_payoffs(mix) mean, boot = await bootstrap.deviation_payoffs(sched, mix, 20, boots=101) assert np.allclose(devs, mean) assert np.allclose(devs, boot) assert mean.shape == (game.num_strats, ) assert boot.shape == (101, game.num_strats)
async def test_random_boot_reg(players, strats): """Test that bootstrap works for random mixtures""" game = gamegen.game(players, strats) mix = game.random_mixture() devs = game.deviation_payoffs(mix) sched = gamesched.gamesched(game) mean, boot = await bootstrap.deviation_payoffs(sched, mix, 20, boots=101, chunk_size=5) assert mean.shape == (game.num_strats, ) assert boot.shape == (101, game.num_strats) # These aren't guaranteed to be false, but it's incredibly unlikely assert not np.allclose(devs, mean) assert not np.allclose(devs, boot)