Example #1
0
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"
Example #2
0
def test_subreduction(players, strategies, _):
    players = np.asarray(players, int)
    game = rsgame.basegame(players ** 2, strategies)
    mask = game.random_subgames()
    red = reduction.DeviationPreserving(
        game.num_strategies, game.num_players, players)

    redgame = red.reduce_game(game)
    redsubg1 = subgame.subgame(redgame, mask)

    subred = subgame.subreduction(red, mask)
    subg = subgame.subgame(game, mask)
    redsubg2 = subred.reduce_game(subg)

    assert redsubg1 == redsubg2
Example #3
0
    def nearby_profs(self, prof, num_devs):
        """Returns profiles reachable by at most num_devs deviations"""
        # XXX this is the bottleneck for gpgame.neighbor_EVs. It seems like
        # there should be some clever way to speed it up.
        assert num_devs >= 0
        dev_players = utils.acomb(self.num_roles, num_devs, True)
        mask = np.all(dev_players <= self.num_players, 1)
        dev_players = dev_players[mask]
        supp = prof > 0
        sub = subgame.subgame(rsgame.BaseGame(self), supp)

        profs = [prof[None]]
        for players in dev_players:
            to_dev_profs = rsgame.BaseGame(
                players, self.num_strategies).all_profiles()
            from_dev_profs = subgame.translate(
                rsgame.BaseGame(players, sub.num_strategies).all_profiles(),
                supp)
            before_devs = prof - from_dev_profs
            before_devs = before_devs[np.all(before_devs >= 0, 1)]
            before_devs = utils.unique_axis(before_devs)
            nearby = before_devs[:, None] + to_dev_profs
            nearby.shape = (-1, self.num_role_strats)
            profs.append(utils.unique_axis(nearby))
        profs = np.concatenate(profs)
        return utils.unique_axis(profs)
Example #4
0
 def get_subgame(self):
     profiles = np.concatenate([phash.array[None] for phash
                                in self._profs])
     payoffs = np.concatenate([self._sched._prof_scheduler[phash][None]
                               for phash in self._profs])
     return subgame.subgame(self._sched._red.reduce_game(rsgame.Game(
         self._sched._game, profiles, payoffs)), self.subgame_mask)
Example #5
0
def test_subgame_extract_2():
    with tempfile.NamedTemporaryFile('w') as sub:
        subg = [False, True, True, False, False, False, False, False, False]
        json.dump([SERIAL.to_subgame_json(subg)], sub)
        sub.flush()
        string, _ = run('sub', '-i', GAME, '-f', sub.name)
        game, serial = gameio.read_game(json.loads(string)[0])
        assert serial == subgame.subserializer(SERIAL, subg)
        assert game == subgame.subgame(GAME_DATA, subg)
Example #6
0
def test_subgame():
    game = rsgame.BaseGame([3, 4], [3, 2])
    subg = np.asarray([1, 0, 1, 0, 1], bool)
    devs = subgame.deviation_profiles(game, subg)
    assert devs.shape[0] == 7, \
        "didn't generate the right number of deviating profiles"
    adds = subgame.additional_strategy_profiles(game, subg, 1).shape[0]
    assert adds == 6, \
        "didn't generate the right number of additional profiles"
    subg2 = subg.copy()
    subg2[1] = True
    assert (subgame.subgame(game, subg2).num_all_profiles ==
            adds + subgame.subgame(game, subg).num_all_profiles), \
        "additional profiles didn't return the proper amount"

    serial = gamegen.game_serializer(game)
    sub_serial = subgame.subserializer(serial, subg)
    assert (subgame.subgame(game, subg).num_role_strats ==
            sub_serial.num_role_strats)
Example #7
0
def main(args):
    game, serial = gameio.read_game(json.load(args.input))
    sub_mask = dominance.iterated_elimination(game, args.criterion,
                                              args.unconditional)
    if args.strategies:
        res = {r: list(s) for r, s in serial.to_subgame_json(sub_mask).items()}
        json.dump(res, args.output)
    else:
        sub_game = subgame.subgame(game, sub_mask)
        sub_serial = subgame.subserializer(serial, sub_mask)
        json.dump(sub_serial.to_game_json(sub_game), args.output)

    args.output.write('\n')
Example #8
0
def test_missing_data_maximal_subgames(game_desc, prob):
    base = rsgame.BaseGame(*game_desc)
    game = gamegen.add_profiles(base, prob)
    subs = subgame.maximal_subgames(game)

    if subs.size:
        maximal = np.all(subs <= subs[:, None], -1)
        np.fill_diagonal(maximal, False)
        assert not maximal.any(), \
            "One maximal subgame dominated another"

    for sub in subs:
        subprofs = subgame.translate(subgame.subgame(base, sub).all_profiles(),
                                     sub)
        assert all(p in game for p in subprofs), \
            "Maximal subgame didn't have all profiles"
        for dev in np.nonzero(~sub)[0]:
            devprofs = subgame.additional_strategy_profiles(
                game, sub, dev)
            assert not all(p in game for p in devprofs), \
                "Maximal subgame could be bigger {} {}".format(
                    dev, sub)
Example #9
0
def main(args):
    game, serial = gameio.read_game(json.load(args.input))

    # Collect all subgames
    subgames = []
    if args.detect:
        subgames.extend(subgame.maximal_subgames(game))

    for sub_file in args.subgame_file:
        subgames.extend(serial.from_subgame_json(spec)
                        for spec in json.load(sub_file))

    subgames.extend(parse_text_spec(serial, spec) for spec in args.text_spec)
    subgames.extend(parse_index_spec(serial, spec) for spec in args.index_spec)

    if args.no_extract:
        json.dump([serial.to_subgame_json(sub) for sub in subgames],
                  args.output)
    else:
        json.dump([
            subgame.subserializer(serial, sub).to_game_json(
                subgame.subgame(game, sub))
            for sub in subgames], args.output)
    args.output.write('\n')
Example #10
0
def main(args):
    game, serial = gameio.read_game(json.load(args.input))

    if args.dpr:
        red_players = serial.from_role_json(dict(zip(
            args.dpr[::2], map(int, args.dpr[1::2]))))
        red = reduction.DeviationPreserving(game.num_strategies,
                                            game.num_players, red_players)
        redgame = red.reduce_game(game, True)
    else:
        redgame = game
    redserial = serial

    if args.dominance:
        domsub = dominance.iterated_elimination(redgame, 'strictdom')
        redgame = subgame.subgame(redgame, domsub)
        redserial = subgame.subserializer(redserial, domsub)

    if args.subgames:
        subgames = subgame.maximal_subgames(redgame)
    else:
        subgames = np.ones(redgame.num_role_strats, bool)[None]

    methods = {
        'replicator': {
            'max_iters': args.max_iters,
            'converge_thresh': args.converge_thresh},
        'optimize': {}}
    noeq_subgames = []
    candidates = []
    for submask in subgames:
        subg = subgame.subgame(redgame, submask)
        subeqa = nash.mixed_nash(
            subg, regret_thresh=args.regret_thresh,
            dist_thresh=args.dist_thresh, processes=args.processes, **methods)
        eqa = subgame.translate(subg.trim_mixture_support(
            subeqa, supp_thresh=args.supp_thresh), submask)
        if eqa.size:
            for eqm in eqa:
                if not any(linalg.norm(eqm - eq) < args.dist_thresh
                           for eq in candidates):
                    candidates.append(eqm)
        else:
            noeq_subgames.append(submask)  # pragma: no cover

    equilibria = []
    unconfirmed = []
    unexplored = []
    for eqm in candidates:
        support = eqm > 0
        gains = regret.mixture_deviation_gains(redgame, eqm)
        role_gains = redgame.role_reduce(gains, ufunc=np.fmax)
        gain = np.nanmax(role_gains)

        if np.isnan(gains).any() and gain <= args.regret_thresh:
            # Not fully explored but might be good
            unconfirmed.append((eqm, gain))

        elif np.any(role_gains > args.regret_thresh):
            # There are deviations, did we explore them?
            dev_inds = ([np.argmax(gs == mg) for gs, mg
                         in zip(redgame.role_split(gains), role_gains)] +
                        redgame.role_starts)[role_gains > args.regret_thresh]
            for dind in dev_inds:
                devsupp = support.copy()
                devsupp[dind] = True
                if not np.all(devsupp <= subgames, -1).any():
                    unexplored.append((devsupp, dind, gains[dind], eqm))

        else:
            # Equilibrium!
            equilibria.append((eqm, np.max(gains)))

    # Output Game
    args.output.write('Game Analysis\n')
    args.output.write('=============\n')
    args.output.write(serial.to_game_printstr(game))
    args.output.write('\n\n')
    if args.dpr is not None:
        args.output.write('With DPR reduction: ')
        args.output.write(' '.join(args.dpr))
        args.output.write('\n\n')
    if args.dominance:
        num = np.sum(~domsub)
        if num:
            args.output.write('Found {:d} dominated strateg{}\n'.format(
                num, 'y' if num == 1 else 'ies'))
            args.output.write(serial.to_subgame_printstr(~domsub))
            args.output.write('\n')
        else:
            args.output.write('Found no dominated strategies\n\n')
    if args.subgames:
        num = subgames.shape[0]
        if num:
            args.output.write(
                'Found {:d} maximal complete subgame{}\n\n'.format(
                    num, '' if num == 1 else 's'))
        else:
            args.output.write('Found no complete subgames\n\n')
    args.output.write('\n')

    # Output social welfare
    args.output.write('Social Welfare\n')
    args.output.write('--------------\n')
    welfare, profile = regret.max_pure_social_welfare(game)
    if profile is None:
        args.output.write('There was no profile with complete payoff data\n\n')
    else:
        args.output.write('\nMaximum social welfare profile:\n')
        args.output.write(serial.to_prof_printstr(profile))
        args.output.write('Welfare: {:.4f}\n\n'.format(welfare))

        if game.num_roles > 1:
            for role, welfare, profile in zip(
                    serial.role_names,
                    *regret.max_pure_social_welfare(game, True)):
                args.output.write('Maximum "{}" welfare profile:\n'.format(
                    role))
                args.output.write(serial.to_prof_printstr(profile))
                args.output.write('Welfare: {:.4f}\n\n'.format(welfare))

    args.output.write('\n')

    # Output Equilibria
    args.output.write('Equilibria\n')
    args.output.write('----------\n')
    if equilibria:
        args.output.write('Found {:d} equilibri{}\n\n'.format(
            len(equilibria), 'um' if len(equilibria) == 1 else 'a'))
        for i, (eqm, reg) in enumerate(equilibria, 1):
            args.output.write('Equilibrium {:d}:\n'.format(i))
            args.output.write(redserial.to_mix_printstr(eqm))
            args.output.write('Regret: {:.4f}\n\n'.format(reg))
    else:
        args.output.write('Found no equilibria\n\n')  # pragma: no cover
    args.output.write('\n')

    # Output No-equilibria Subgames
    args.output.write('No-equilibria Subgames\n')
    args.output.write('----------------------\n')
    if noeq_subgames:  # pragma: no cover
        args.output.write('Found {:d} no-equilibria subgame{}\n\n'.format(
            len(noeq_subgames), '' if len(noeq_subgames) == 1 else 's'))
        noeq_subgames.sort(key=lambda x: x.sum())
        for i, subg in enumerate(noeq_subgames, 1):
            args.output.write('No-equilibria subgame {:d}:\n'.format(i))
            args.output.write(redserial.to_subgame_printstr(subg))
            args.output.write('\n')
    else:
        args.output.write('Found no no-equilibria subgames\n\n')
    args.output.write('\n')

    # Output Unconfirmed Candidates
    args.output.write('Unconfirmed Candidate Equilibria\n')
    args.output.write('--------------------------------\n')
    if unconfirmed:
        args.output.write('Found {:d} unconfirmed candidate{}\n\n'.format(
            len(unconfirmed), '' if len(unconfirmed) == 1 else 's'))
        unconfirmed.sort(key=lambda x: ((x[0] > 0).sum(), x[1]))
        for i, (eqm, reg_bound) in enumerate(unconfirmed, 1):
            args.output.write('Unconfirmed candidate {:d}:\n'.format(i))
            args.output.write(redserial.to_mix_printstr(eqm))
            args.output.write('Regret at least: {:.4f}\n\n'.format(reg_bound))
    else:
        args.output.write('Found no unconfirmed candidate equilibria\n\n')
    args.output.write('\n')

    # Output Unexplored Subgames
    args.output.write('Unexplored Best-response Subgames\n')
    args.output.write('---------------------------------\n')
    if unexplored:
        min_supp = min(supp.sum() for supp, _, _, _ in unexplored)
        args.output.write(
            'Found {:d} unexplored best-response subgame{}\n'.format(
                len(unexplored), '' if len(unexplored) == 1 else 's'))
        args.output.write(
            'Smallest unexplored subgame has support {:d}\n\n'.format(
                min_supp))

        unexplored.sort(key=lambda x: (x[0].sum(), -x[2]))
        for i, (sub, dev, gain, eqm) in enumerate(unexplored, 1):
            args.output.write('Unexplored subgame {:d}:\n'.format(i))
            args.output.write(redserial.to_subgame_printstr(sub))
            args.output.write('{:.4f} for deviating to {} from:\n'.format(
                gain, redserial.strat_name(dev)))
            args.output.write(redserial.to_mix_printstr(eqm))
            args.output.write('\n')
    else:
        args.output.write('Found no unexplored best-response subgames\n\n')
    args.output.write('\n')

    # Output json data
    args.output.write('Json Data\n')
    args.output.write('=========\n')
    json_data = {
        'equilibria': [redserial.to_mix_json(eqm) for eqm, _ in equilibria]}
    json.dump(json_data, args.output)
    args.output.write('\n')