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 run(args): # pylint: disable=too-many-locals """Command line entry point for tracing""" sched0 = CanonWrapper(await schedspec.parse_scheduler(args.sched0)) sched1 = CanonWrapper(await schedspec.parse_scheduler(args.sched1)) red, red_players = utils.parse_reduction(sched0, args) agame0 = schedgame.schedgame(sched0, red, red_players) agame1 = schedgame.schedgame(sched1, red, red_players) async def get_point(prob, eqm): """Get the point in a trace for an equilibrium""" supp = eqm > 0 game0 = await agame0.get_deviation_game(supp) game1 = await agame1.get_deviation_game(supp) reg = regret.mixture_regret( rsgame.mix(game0, game1, prob), eqm) return { 't': float(prob), 'equilibrium': sched0.mixture_to_json(eqm), 'regret': float(reg)} async def get_trace(probs, peqa): """Get the trace for probabilities and equilibria""" return await asyncio.gather(*[ get_point(p, eqm) for p, eqm in zip(probs, peqa)]) async with sched0, sched1: with futures.ProcessPoolExecutor(args.procs) as executor: traces = await trace.trace_all_equilibria( agame0, agame1, regret_thresh=args.regret_thresh, dist_thresh=args.dist_thresh, restricted_game_size=args.max_restrict_size, num_equilibria=args.num_equilibria, num_backups=args.num_backups, devs_by_role=args.dev_by_role, style=args.style, executor=executor) jtraces = await asyncio.gather(*[ get_trace(ts, teqa) for ts, teqa in traces]) max_reg = 0.0 spans = [[-1, -1]] for jtrace in jtraces: max_reg = max(max_reg, max(p['regret'] for p in jtrace)) span = spans[-1] start, *_, end = jtrace if start['t'] <= span[1]: span[1] = max(span[1], end['t']) else: spans.append([start['t'], end['t']]) logging.error( 'tracing finished finding %d traces covering %s, with maximum regret ' '%g', len(jtraces), ' U '.join('[{:g}, {:g}]'.format(s, e) for s, e in spans[1:]), max_reg) json.dump(jtraces, args.output) args.output.write('\n')
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_by_role_simple(base): """Test that inner loop works by role""" sgame = gamegen.samplegame_replace(base) sched = gamesched.samplegamesched(sgame) eqa = await innerloop.inner_loop(schedgame.schedgame(sched), devs_by_role=True) verify_dist_thresh(eqa)
async def test_random_redgame(players, strats, _): """Test properties of games returned from scheduler""" game = gamegen.samplegame(players, strats) rest = game.random_restriction() sched = gamesched.samplegamesched(game) sgame = schedgame.schedgame(sched) devgame1 = await sgame.get_deviation_game(rest) prof = devgame1.profiles()[ np.all((devgame1.profiles() == 0) | ~np.isnan(devgame1.payoffs()), 1).nonzero()[ 0 ][0] ] assert prof in devgame1 assert ( devgame1.num_complete_profiles <= devgame1.num_profiles <= devgame1.num_all_profiles ) devgame2 = await sgame.get_deviation_game(rest) assert hash(devgame1) == hash(devgame2) assert devgame1 == devgame2 assert devgame1 + devgame2 == devgame2 + devgame1 assert np.allclose(devgame1.get_payoffs(prof), devgame2.get_payoffs(prof)) rrest = devgame1.random_restriction() assert devgame1.restrict(rrest) == devgame2.restrict(rrest)
async def test_innerloop_num_eqa(base, num): """Test that inner loop returns alternate number of equilibria""" sgame = gamegen.samplegame_replace(base) sched = gamesched.samplegamesched(sgame) eqa = await innerloop.inner_loop(schedgame.schedgame(sched), num_equilibria=num, devs_by_role=True) verify_dist_thresh(eqa)
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_normalize(players, strats, _): """Test normalizing random games""" game = gamegen.samplegame(players, strats) sched = gamesched.samplegamesched(game) sgame = schedgame.schedgame(sched) rgame = await sgame.get_restricted_game(np.ones(game.num_strats, bool)) ngame = rgame.normalize() assert np.all(ngame.payoffs() >= -1e-7) assert np.all(ngame.payoffs() <= 1 + 1e-7)
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_exception(players, strats, when, _): """Test that exceptions are raised appropriately""" game = gamegen.samplegame(players, strats) sched = gamesched.samplegamesched(game) esched = utils.ExceptionScheduler(sched, 10, when) sgame = schedgame.schedgame(esched) rests = np.concatenate( [game.random_restrictions(3), np.ones((1, game.num_strats), bool)] ) with pytest.raises(utils.SchedulerException): await asyncio.gather(*[sgame.get_restricted_game(rest) for rest in rests])
async def test_innerloop_dpr(redgame): """Test that inner loop handles dpr""" fullgame = rsgame.empty(redgame.num_role_players**2, redgame.num_role_strats) profs = dpr.expand_profiles(fullgame, redgame.all_profiles()) pays = np.random.random(profs.shape) pays[profs == 0] = 0 sgame = paygame.samplegame_replace(fullgame, profs, [pays[:, None]]) sched = gamesched.samplegamesched(sgame) game = schedgame.schedgame(sched, dpr, redgame.num_role_players) eqa = await innerloop.inner_loop(game) verify_dist_thresh(eqa)
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_random_complete_dev(players, strats, _): """Test the deviations are complete in random games""" game = gamegen.samplegame(players, strats) sched = gamesched.samplegamesched(game) sgame = schedgame.schedgame(sched) mix = sgame.random_sparse_mixture() supp = mix > 0 dev_game = await sgame.get_deviation_game(supp) devs, jac = dev_game.deviation_payoffs(mix, jacobian=True) assert not np.isnan(devs).any() assert not np.isnan(jac[supp]).any() assert np.isnan(jac[~supp]).all() for role in range(sgame.num_roles): mask = (role == sgame.role_indices) dev_game = await sgame.get_deviation_game(supp, role_index=role) rdevs = dev_game.deviation_payoffs(mix) assert np.allclose(rdevs[supp], devs[supp]) assert np.allclose(rdevs[mask], devs[mask]) assert supp[~mask].all() or np.isnan(rdevs[~mask]).any()
async def run(args): """Entry point for cli""" sched = await schedspec.parse_scheduler(args.scheduler) red, red_players = utils.parse_reduction(sched, args) agame = schedgame.schedgame(sched, red, red_players) async def get_regret(eqm): """Gets the regret of an equilibrium""" game = await agame.get_deviation_game(eqm > 0) return float(regret.mixture_regret(game, eqm)) async with sched: with futures.ProcessPoolExecutor(args.procs) as executor: eqa = await innerloop.inner_loop( agame, regret_thresh=args.regret_thresh, dist_thresh=args.dist_thresh, restricted_game_size=args.max_restrict_size, num_equilibria=args.num_equilibria, num_backups=args.num_backups, devs_by_role=args.dev_by_role, style=args.style, executor=executor, ) regrets = await asyncio.gather(*[get_regret(eqm) for eqm in eqa]) logging.error( "quiesce finished finding %d equilibria:\n%s", eqa.shape[0], "\n".join("{:d}) {} with regret {:g}".format( i, sched.mixture_to_repr(eqm), reg) for i, (eqm, reg) in enumerate(zip(eqa, regrets), 1)), ) json.dump( [{ "equilibrium": sched.mixture_to_json(eqm), "regret": reg } for eqm, reg in zip(eqa, regrets)], args.output, ) args.output.write("\n")
async def test_random_caching(players, strats, _): """Test that profiles are cached (identical)""" game = gamegen.samplegame(players, strats) rest1, rest2 = game.random_restrictions(2) sched = gamesched.samplegamesched(game) sgame = schedgame.schedgame(sched) assert str(sgame) == str(sched) rgame11 = await sgame.get_restricted_game(rest1) rgame21 = await sgame.get_restricted_game(rest2) devs11 = await sgame.get_deviation_game(rest1) devs21 = await sgame.get_deviation_game(rest2) rgame12 = await sgame.get_restricted_game(rest1) rgame22 = await sgame.get_restricted_game(rest2) assert rgame11 == rgame12 assert rgame21 == rgame22 devs12 = await sgame.get_deviation_game(rest1) devs22 = await sgame.get_deviation_game(rest2) assert devs11 == devs12 assert devs21 == devs22
async def test_innerloop_simple(base): """Test that inner loop works for a simple setup""" sgame = gamegen.samplegame_replace(base) sched = gamesched.samplegamesched(sgame) eqa = await innerloop.inner_loop(schedgame.schedgame(sched)) verify_dist_thresh(eqa)