예제 #1
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)
예제 #2
0
 def dev_profs(red_players, full_players, mask, rs):
     subg = rsgame.basegame(red_players, support)
     sub_profs = subgame.translate(subg.all_profiles(), subgame_mask)
     non_devs = _expand_rsym_profiles(self.full_game, sub_profs,
                                      full_players, red_players)
     ndevs = np.sum(~mask)
     devs = np.zeros((ndevs, self.full_game.num_role_strats), int)
     devs[:, rs:rs + mask.size][:, ~mask] = np.eye(ndevs, dtype=int)
     profs = non_devs[:, None] + devs
     profs.shape = (-1, self.full_game.num_role_strats)
     return profs
예제 #3
0
 def analyze_subgame(unsched_subgames, sub):
     """Process a subgame"""
     if sub.is_complete():
         subg = sub.get_subgame()
         sub_eqa = nash.mixed_nash(subg, regret_thresh=regret_thresh)
         eqa = subgame.translate(subg.trim_mixture_support(sub_eqa),
                                 sub.subgame_mask)
         if eqa.size == 0:  # No equilibria
             if sub.counts < reschedule_limit * observation_increment:
                 log.info(
                     'Found no equilibria in subgame:\n%s\n',
                     json.dumps(
                         {r: list(s) for r, s
                          in serial.to_prof_json(sub.subgame_mask).items()},
                         indent=2))
                 sub.update_counts(sub.counts + observation_increment)
                 unsched_subgames.append(sub)
             else:
                 log.error(
                     'Failed to find equilibria in subgame:\n%s\n',
                     json.dumps(
                         {r: list(s)
                          for r, s in serial.to_prof_json(subm).items()},
                         indent=2))
         else:
             log.debug(
                 'Found candidate equilibria:\n%s\nin subgame:\n%s\n',
                 json.dumps(list(map(serial.to_prof_json, eqa)), indent=2),
                 json.dumps(
                     {r: list(s) for r, s in
                      serial.to_prof_json(sub.subgame_mask).items()},
                     indent=2))
             if all_devs:
                 for eqm in eqa:
                     add_mixture(eqm)
             else:
                 for eqm in eqa:
                     add_mixture(eqm, 0)
     else:
         unsched_subgames.append(sub)
예제 #4
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)
예제 #5
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')
예제 #6
0
def test_translate():
    prof = np.arange(6) + 1
    mask = np.array([1, 0, 0, 1, 1, 0, 1, 1, 0, 1], bool)
    expected = [1, 0, 0, 2, 3, 0, 4, 5, 0, 6]
    assert np.all(expected == subgame.translate(prof, mask))
예제 #7
0
 def _profiles(self):
     support = self._sched._game.role_reduce(self.subgame_mask)
     return self._sched._red.expand_profiles(subgame.translate(
         rsgame.BaseGame(self._sched._red.reduced_players,
                         support).all_profiles(),
         self.subgame_mask))