Пример #1
0
def test_game_size():
    """Test game size"""
    assert utils.game_size(2000, 10) == 1442989326579174917694151
    assert np.all(utils.game_size([10, 20, 100], 10) ==
                  [92378, 10015005, 4263421511271])
    assert np.all(utils.game_size(10, [10, 20, 100]) ==
                  [92378, 20030010, 42634215112710])
    assert np.all(utils.game_size([100, 20, 10], [10, 20, 100]) ==
                  [4263421511271, 68923264410, 42634215112710])
    assert utils.game_size_inv(1442989326579174917694151, 2000) == 10
    assert np.all(utils.game_size_inv(
        [92378, 10015005, 4263421511271], [10, 20, 100]) == 10)
    assert np.all(utils.game_size_inv(
        [92378, 20030010, 42634215112710], 10) == [10, 20, 100])
    assert np.all(utils.game_size_inv(
        [4263421511271, 68923264410, 42634215112710],
        [100, 20, 10]) == [10, 20, 100])
    assert np.all(utils.game_size_inv(100, [1, 5, 20]) == [100, 4, 2])
Пример #2
0
 def profile_id(self, profiles):
     """Return a unique integer representing a profile"""
     profiles = -np.asarray(profiles, int)
     profiles[..., self.role_starts] += self.num_players
     profiles = profiles.cumsum(-1)
     rev_arange = -np.ones(self.num_role_strats, int)
     rev_arange[self.role_starts] += self.num_strategies
     rev_arange = rev_arange.cumsum()
     base = np.insert(self.role_sizes[:-1].cumprod(), 0, 1)
     return self.role_reduce(utils.game_size(
         rev_arange, profiles)).dot(base)
Пример #3
0
def test_game_size():
    """Test game size"""
    assert utils.game_size(2000, 10) == 1442989326579174917694151
    assert np.all(
        utils.game_size([10, 20, 100], 10) == [92378, 10015005, 4263421511271])
    assert np.all(
        utils.game_size(10, [10, 20, 100]) ==
        [92378, 20030010, 42634215112710])
    assert np.all(
        utils.game_size([100, 20, 10], [10, 20, 100]) ==
        [4263421511271, 68923264410, 42634215112710])
    assert utils.game_size_inv(1442989326579174917694151, 2000) == 10
    assert np.all(
        utils.game_size_inv([92378, 10015005, 4263421511271], [10, 20, 100]) ==
        10)
    assert np.all(
        utils.game_size_inv([92378, 20030010, 42634215112710], 10) ==
        [10, 20, 100])
    assert np.all(
        utils.game_size_inv([4263421511271, 68923264410, 42634215112710],
                            [100, 20, 10]) == [10, 20, 100])
    assert np.all(utils.game_size_inv(100, [1, 5, 20]) == [100, 4, 2])
Пример #4
0
def num_deviation_profiles(game, rest):
    """Returns the number of deviation profiles

    This is a closed form way to compute `deviation_profiles(game,
    rest).shape[0]`.
    """
    rest = np.asarray(rest, bool)
    utils.check(game.is_restriction(rest), 'restriction must be valid')
    num_role_strats = np.add.reduceat(rest, game.role_starts)
    num_devs = game.num_role_strats - num_role_strats
    dev_players = game.num_role_players - np.eye(game.num_roles, dtype=int)
    return np.sum(utils.game_size(dev_players, num_role_strats).prod(1) *
                  num_devs)
Пример #5
0
def num_deviation_profiles(game, rest):
    """Returns the number of deviation profiles

    This is a closed form way to compute `deviation_profiles(game,
    rest).shape[0]`.
    """
    rest = np.asarray(rest, bool)
    utils.check(game.is_restriction(rest), 'restriction must be valid')
    num_role_strats = np.add.reduceat(rest, game.role_starts)
    num_devs = game.num_role_strats - num_role_strats
    dev_players = game.num_role_players - np.eye(game.num_roles, dtype=int)
    return np.sum(
        utils.game_size(dev_players, num_role_strats).prod(1) * num_devs)
Пример #6
0
def num_deviation_profiles(game, subgame_mask):
    """Returns the number of deviation profiles

    This is a closed form way to compute `deviation_profiles(game,
    subgame_mask).shape[0]`.
    """
    subgame_mask = np.asarray(subgame_mask, bool)
    assert game.num_role_strats == subgame_mask.size
    num_strategies = game.role_reduce(subgame_mask)
    num_devs = game.num_strategies - num_strategies
    dev_players = game.num_players - np.eye(game.num_roles, dtype=int)
    return np.sum(utils.game_size(dev_players, num_strategies).prod(1) *
                  num_devs)
Пример #7
0
def num_deviation_payoffs(game, subgame_mask):
    """Returns the number of deviation payoffs

    This is a closed form way to compute `np.sum(deviation_profiles(game,
    subgame_mask) > 0)`."""
    subgame_mask = np.asarray(subgame_mask, bool)
    assert game.num_role_strats == subgame_mask.size
    num_strategies = game.role_reduce(subgame_mask)
    num_devs = game.num_strategies - num_strategies
    dev_players = (game.num_players - np.eye(game.num_roles, dtype=int) -
                   np.eye(game.num_roles, dtype=int)[:, None])
    temp = utils.game_size(dev_players, num_strategies).prod(2)
    non_deviators = np.sum(np.sum(temp * num_strategies, 1) * num_devs)
    return non_deviators + num_deviation_profiles(game, subgame_mask)
Пример #8
0
def num_deviation_payoffs(game, rest):
    """Returns the number of deviation payoffs

    This is a closed form way to compute `np.sum(deviation_profiles(game, rest)
    > 0)`."""
    rest = np.asarray(rest, bool)
    utils.check(game.is_restriction(rest), 'restriction must be valid')
    num_role_strats = np.add.reduceat(rest, game.role_starts)
    num_devs = game.num_role_strats - num_role_strats
    dev_players = (game.num_role_players - np.eye(game.num_roles, dtype=int) -
                   np.eye(game.num_roles, dtype=int)[:, None])
    temp = utils.game_size(dev_players, num_role_strats).prod(2)
    non_deviators = np.sum(np.sum(temp * num_role_strats, 1) * num_devs)
    return non_deviators + num_deviation_profiles(game, rest)
Пример #9
0
def num_deviation_payoffs(game, rest):
    """Returns the number of deviation payoffs

    This is a closed form way to compute `np.sum(deviation_profiles(game, rest)
    > 0)`."""
    rest = np.asarray(rest, bool)
    utils.check(game.is_restriction(rest), 'restriction must be valid')
    num_role_strats = np.add.reduceat(rest, game.role_starts)
    num_devs = game.num_role_strats - num_role_strats
    dev_players = (game.num_role_players - np.eye(game.num_roles, dtype=int) -
                   np.eye(game.num_roles, dtype=int)[:, None])
    temp = utils.game_size(dev_players, num_role_strats).prod(2)
    non_deviators = np.sum(np.sum(temp * num_role_strats, 1) * num_devs)
    return non_deviators + num_deviation_profiles(game, rest)
Пример #10
0
    def num_all_dpr_profiles(self):
        """The number of unique dpr profiles

        This calculation takes time exponential in the number of roles.
        """
        # Get all combinations of "pure" roles and then filter by ones with
        # support at least 2. Thus, 0, 1, and 2 can be safely ignored
        pure = (np.arange(3, 1 << self.num_roles)[:, None] &
                (1 << np.arange(self.num_roles))).astype(bool)
        cards = pure.sum(1)
        pure = pure[cards > 1]
        cards = cards[cards > 1] - 1
        # For each combination of pure roles, compute the number of profiles
        # conditioned on those roles being pure, then multiply them by the
        # cardinality of the pure roles.
        pure_counts = np.prod(self.num_strategies * pure + ~pure, 1)
        unpure_counts = np.prod((utils.game_size(self.num_players,
                                                 self.num_strategies) -
                                 self.num_strategies) * ~pure + pure, 1)
        overcount = np.sum(cards * pure_counts * unpure_counts)
        return self.num_all_payoffs - overcount
Пример #11
0
def num_dpr_deviation_profiles(game, subgame_mask):
    """Returns the number of dpr deviation profiles"""
    subgame_mask = np.asarray(subgame_mask, bool)
    assert game.num_role_strats == subgame_mask.size
    num_strategies = game.role_reduce(subgame_mask)
    num_devs = game.num_strategies - num_strategies

    pure = (np.arange(3, 1 << game.num_roles)[:, None] &
            (1 << np.arange(game.num_roles))).astype(bool)
    cards = pure.sum(1)
    pure = pure[cards > 1]
    card_counts = cards[cards > 1, None] - 1 - ((game.num_players > 1) & pure)
    # For each combination of pure roles, compute the number of profiles
    # conditioned on those roles being pure, then multiply them by the
    # cardinality of the pure roles.
    sp_dev = np.eye(game.num_roles, dtype=bool) & (game.num_players == 1)
    pure_counts = num_strategies * ~sp_dev + sp_dev
    dev_players = game.num_players - np.eye(game.num_roles, dtype=int)
    unpure_counts = utils.game_size(dev_players, num_strategies) - pure_counts
    pure_counts = np.prod(pure_counts * pure[:, None] + ~pure[:, None], 2)
    unpure_counts = np.prod(unpure_counts * ~pure[:, None] + pure[:, None], 2)
    overcount = np.sum(card_counts * pure_counts * unpure_counts * num_devs)
    return num_deviation_payoffs(game, subgame_mask) - overcount
Пример #12
0
def num_dpr_deviation_profiles(game, rest):
    """Returns the number of dpr deviation profiles"""
    rest = np.asarray(rest, bool)
    utils.check(game.is_restriction(rest), 'restriction must be valid')
    num_role_strats = np.add.reduceat(rest, game.role_starts)
    num_devs = game.num_role_strats - num_role_strats

    pure = (np.arange(3, 1 << game.num_roles)[:, None] &
            (1 << np.arange(game.num_roles))).astype(bool)
    cards = pure.sum(1)
    pure = pure[cards > 1]
    card_counts = cards[cards > 1, None] - 1 - \
        ((game.num_role_players > 1) & pure)
    # For each combination of pure roles, compute the number of profiles
    # conditioned on those roles being pure, then multiply them by the
    # cardinality of the pure roles.
    sp_dev = np.eye(game.num_roles, dtype=bool) & (game.num_role_players == 1)
    pure_counts = num_role_strats * ~sp_dev + sp_dev
    dev_players = game.num_role_players - np.eye(game.num_roles, dtype=int)
    unpure_counts = utils.game_size(dev_players, num_role_strats) - pure_counts
    pure_counts = np.prod(pure_counts * pure[:, None] + ~pure[:, None], 2)
    unpure_counts = np.prod(unpure_counts * ~pure[:, None] + pure[:, None], 2)
    overcount = np.sum(card_counts * pure_counts * unpure_counts * num_devs)
    return num_deviation_payoffs(game, rest) - overcount
Пример #13
0
def num_dpr_deviation_profiles(game, rest):
    """Returns the number of dpr deviation profiles"""
    rest = np.asarray(rest, bool)
    utils.check(game.is_restriction(rest), 'restriction must be valid')
    num_role_strats = np.add.reduceat(rest, game.role_starts)
    num_devs = game.num_role_strats - num_role_strats

    pure = (np.arange(3, 1 << game.num_roles)[:, None] &
            (1 << np.arange(game.num_roles))).astype(bool)
    cards = pure.sum(1)
    pure = pure[cards > 1]
    card_counts = cards[cards > 1, None] - 1 - \
        ((game.num_role_players > 1) & pure)
    # For each combination of pure roles, compute the number of profiles
    # conditioned on those roles being pure, then multiply them by the
    # cardinality of the pure roles.
    sp_dev = np.eye(game.num_roles, dtype=bool) & (game.num_role_players == 1)
    pure_counts = num_role_strats * ~sp_dev + sp_dev
    dev_players = game.num_role_players - np.eye(game.num_roles, dtype=int)
    unpure_counts = utils.game_size(dev_players, num_role_strats) - pure_counts
    pure_counts = np.prod(pure_counts * pure[:, None] + ~pure[:, None], 2)
    unpure_counts = np.prod(unpure_counts * ~pure[:, None] + pure[:, None], 2)
    overcount = np.sum(card_counts * pure_counts * unpure_counts * num_devs)
    return num_deviation_payoffs(game, rest) - overcount
Пример #14
0
 def role_sizes(self):
     """The number of profiles in each role (independent of others)"""
     return utils.game_size(self.num_players, self.num_strategies)
Пример #15
0
def num_profiles(subgame, role_counts, **_):
    """Number of profiles in a subgame"""
    return utils.prod(utils.game_size(role_counts[role], len(strats)) for role, strats in subgame.items())
Пример #16
0
 def num_all_payoffs(self):
     """The number of payoffs in all profiles"""
     dev_players = self.num_players - np.eye(self.num_roles, dtype=int)
     return np.sum(utils.game_size(dev_players, self.num_strategies)
                   .prod(1) * self.num_strategies)
Пример #17
0
def test_game_size():
    assert utils.game_size(2000, 10) == 1442989326579174917694151
Пример #18
0
    def deviation_payoffs(self, mix, assume_complete=False, jacobian=False):
        """Computes the expected value of each pure strategy played against all
        opponents playing mix.

        Parameters
        ----------
        mix : ndarray
            The mix all other players are using
        assume_complete : bool
            If true, don't compute missing data and replace with nans. Just
            return the potentially inaccurate results.
        jacobian : bool
            If true, the second returned argument will be the jacobian of the
            deviation payoffs with respect to the mixture. The first axis is
            the deviating strategy, the second axis is the strategy in the mix
            the jacobian is taken with respect to. The values that are marked
            nan are not very aggressive, so don't rely on accurate nan values
            in the jacobian.
        """
        # TODO It wouldn't be hard to extend this to multiple mixtures, which
        # would allow array calculation of mixture regret. Support would have
        # to be iterative though.
        mix = np.asarray(mix, float)
        nan_mask = np.empty_like(mix, dtype=bool)

        # Fill out mask where we don't have data
        if self.is_complete() or assume_complete:
            nan_mask.fill(False)
        elif self.is_empty():
            nan_mask.fill(True)
        else:
            # These calculations are approximate, but for games we can do
            # anything with, the size is bounded, and so numeric methods are
            # actually exact.
            support = mix > 0
            strats = self.role_reduce(support)
            devs = self.profiles[:, ~support]
            num_supp = utils.game_size(self.num_players, strats).prod()
            dev_players = self.num_players - np.eye(self.num_roles, dtype=int)
            role_num_dev = utils.game_size(dev_players, strats).prod(1)
            num_dev = role_num_dev.repeat(self.num_strategies)[~support]

            nan_mask[support] = np.all(devs == 0, 1).sum() < num_supp
            nan_mask[~support] = devs[devs.sum(1) == 1].sum(0) < num_dev

        # Compute values
        if not nan_mask.all():
            # _TINY effectively makes 0^0=1 and 0/0=0.
            log_mix = np.log(mix + _TINY)
            prof_prob = np.sum(self.profiles * log_mix, 1, keepdims=True)
            with np.errstate(under='ignore'):
                # Ignore underflow caused when profile probability is not
                # representable in floating point.
                probs = np.exp(prof_prob + self._dev_reps - log_mix)
            zero_prob = _TINY * self.num_players.sum()
            weighted_payoffs = probs * np.where(probs > zero_prob,
                                                self.payoffs, 0)
            values = np.sum(weighted_payoffs, 0)

        else:
            values = np.empty(self.num_role_strats)

        values[nan_mask] = np.nan

        if not jacobian:
            return values

        if not nan_mask.all():
            tmix = mix + zero_prob
            product_rule = self.profiles[:, None] / tmix - np.diag(1 / tmix)
            dev_jac = np.sum(weighted_payoffs[..., None] * product_rule, 0)
        else:
            dev_jac = np.empty((self.num_role_strats, self.num_role_strats))

        dev_jac[nan_mask] = np.nan
        return values, dev_jac