Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
def reduce_game(full_game, red_players):
    """Reduce a game using hierarchical reduction

    Parameters
    ----------
    full_game : Game
        The game to reduce.
    red_players : ndarray-like
        The reduced number of players for each role. This will be coerced
        into the proper shape if necessary.
    """
    red_game = rsgame.empty_names(full_game.role_names, red_players,
                                  full_game.strat_names)
    utils.check(np.all(red_game.num_role_players > 0),
                'all reduced players must be greater than zero')
    utils.check(
        np.all(full_game.num_role_players >= red_game.num_role_players),
        'all full counts must not be less than reduced counts')

    if full_game.is_empty():
        return red_game
    elif full_game.num_profiles < red_game.num_all_profiles:
        profiles = full_game.profiles()
        payoffs = full_game.payoffs()
    else:
        profiles = expand_profiles(full_game, red_game.all_profiles())
        payoffs = full_game.get_payoffs(profiles)
        valid = ~np.all(np.isnan(payoffs) | (profiles == 0), 1)
        profiles = profiles[valid]
        payoffs = payoffs[valid]

    red_profiles, mask = _common.reduce_profiles(
        full_game, red_game.num_role_players[None], profiles)
    return paygame.game_replace(red_game, red_profiles, payoffs[mask])
Ejemplo n.º 3
0
def full_game(role_names, role_players, strat_names):
    """Return a full game"""
    base = rsgame.empty_names(
        role_names, role_players, strat_names)
    return paygame.game_replace(
        base, base.all_profiles(),
        np.zeros((base.num_all_profiles, base.num_strats)))
Ejemplo n.º 4
0
 def get_game(self):
     profs = []
     pays = []
     for hprof, fpay in self._profiles.items():
         if fpay.done():
             profs.append(hprof.array)
             pays.append(fpay.result())
     return self._wrap(paygame.game_replace(
         self, np.stack(profs), np.stack(pays)))
Ejemplo n.º 5
0
def reduce_game(full_game, red_players):  # pylint: disable=too-many-locals
    """Reduce a game using deviation preserving reduction

    Parameters
    ----------
    full_game : Game
        The game to reduce.
    red_players : ndarray-like
        The reduced number of players for each role. This will be coerced
        into the proper shape if necessary.
    """
    red_game = rsgame.empty_names(full_game.role_names, red_players,
                                  full_game.strat_names)
    utils.check(
        np.all((red_game.num_role_players > 1)
               | (full_game.num_role_players == 1)),
        'all reduced players must be greater than zero')
    utils.check(
        np.all(full_game.num_role_players >= red_game.num_role_players),
        'all full counts must not be less than reduced counts')

    if full_game.is_empty():
        return red_game
    elif full_game.num_profiles < red_game.num_all_dpr_profiles:
        full_profiles = full_game.profiles()
        full_payoffs = full_game.payoffs()
    else:
        full_profiles = expand_profiles(full_game, red_game.all_profiles())
        full_payoffs = full_game.get_payoffs(full_profiles)
        valid = ~np.all(np.isnan(full_payoffs) | (full_profiles == 0), 1)
        full_profiles = full_profiles[valid]
        full_payoffs = full_payoffs[valid]

    # Reduce
    red_profiles, red_inds, full_inds, strat_inds = _reduce_profiles(
        red_game, full_profiles, True)

    if red_profiles.size == 0:  # Empty reduction
        return red_game

    # Build mapping from payoffs to reduced profiles, and use bincount
    # to count the number of payoffs mapped to a specific location, and
    # sum the number of payoffs mapped to a specific location
    cum_inds = red_inds * full_game.num_strats + strat_inds
    payoff_vals = full_payoffs[full_inds, strat_inds]
    red_payoffs = np.bincount(cum_inds, payoff_vals,
                              red_profiles.size).reshape(red_profiles.shape)
    red_payoff_counts = np.bincount(
        cum_inds, minlength=red_profiles.size).reshape(red_profiles.shape)
    mask = red_payoff_counts > 1
    red_payoffs[mask] /= red_payoff_counts[mask]

    unknown = (red_profiles > 0) & (red_payoff_counts == 0)
    red_payoffs[unknown] = np.nan
    valid = ~np.all((red_profiles == 0) | np.isnan(red_payoffs), 1)
    return paygame.game_replace(red_game, red_profiles[valid],
                                red_payoffs[valid])
Ejemplo n.º 6
0
def test_random_game_addition(strats):
    """Test random addition"""
    mpayoffs = rand.random(tuple(strats) + (len(strats),))
    matg = matgame.matgame(mpayoffs)
    payoffs = rand.random(matg.payoffs().shape)
    payoffs[matg.profiles() == 0] = 0
    game = paygame.game_replace(matg, matg.profiles(), payoffs)
    assert paygame.game_copy(matg + game) == game + matg

    empty = rsgame.empty_copy(matg)
    assert matg + empty == empty
Ejemplo n.º 7
0
def test_random_game_addition(strats):
    """Test random addition"""
    mpayoffs = rand.random(tuple(strats) + (len(strats), ))
    matg = matgame.matgame(mpayoffs)
    payoffs = rand.random(matg.payoffs().shape)
    payoffs[matg.profiles() == 0] = 0
    game = paygame.game_replace(matg, matg.profiles(), payoffs)
    assert paygame.game_copy(matg + game) == game + matg

    empty = rsgame.empty_copy(matg)
    assert matg + empty == empty
Ejemplo n.º 8
0
 def deviation_payoffs(self, mixture, *, jacobian=False, **_):
     # TODO This is not smooth because there are discontinuities when the
     # maximum probability profile jumps at the boundary. If we wanted to
     # make it smooth, one option would be to compute the smoother
     # interpolation between this and lower probability profiles. All we
     # need to ensure smoothness is that the weight at profile
     # discontinuities is 0.
     profiles = self.nearby_profiles(
         self.max_prob_prof(mixture), self.num_neighbors)
     payoffs = self.get_payoffs(profiles)
     game = paygame.game_replace(self, profiles, payoffs)
     return game.deviation_payoffs(mixture, ignore_incomplete=True,
                                   jacobian=jacobian)
Ejemplo n.º 9
0
 def deviation_payoffs(self, mixture, *, jacobian=False, **_):
     # TODO This is not smooth because there are discontinuities when the
     # maximum probability profile jumps at the boundary. If we wanted to
     # make it smooth, one option would be to compute the smoother
     # interpolation between this and lower probability profiles. All we
     # need to ensure smoothness is that the weight at profile
     # discontinuities is 0.
     profiles = self.nearby_profiles(self.max_prob_prof(mixture),
                                     self.num_neighbors)
     payoffs = self.get_payoffs(profiles)
     game = paygame.game_replace(self, profiles, payoffs)
     return game.deviation_payoffs(mixture,
                                   ignore_incomplete=True,
                                   jacobian=jacobian)
Ejemplo n.º 10
0
 async def _get_game(self, profs):
     """Get a game from the profiles to sample"""
     futures = []
     for prof in profs:
         hprof = utils.hash_array(prof)
         future = self._profiles.get(hprof, None)
         if future is None:
             future = asyncio.ensure_future(
                 self._sched.sample_payoffs(prof))
             self._profiles[hprof] = future
         futures.append(future)
     lpays = await asyncio.gather(*futures)
     pays = np.stack(lpays)
     return paygame.game_replace(self, profs, pays)
Ejemplo n.º 11
0
def travellers_dilemma(players=2, max_value=100):
    """Return an instance of travellers dilemma

    Strategies range from 2 to max_value, thus there will be max_value - 1
    strategies."""
    utils.check(players > 1, 'players must be more than one')
    utils.check(max_value > 2, 'max value must be more than 2')
    base = rsgame.empty(players, max_value - 1)
    profiles = base.all_profiles()
    payoffs = np.zeros(profiles.shape)
    mins = np.argmax(profiles, -1)
    mask = profiles > 0
    payoffs[mask] = mins.repeat(mask.sum(-1))
    rows = np.arange(profiles.shape[0])
    ties = profiles[rows, mins] > 1
    lowest_pays = mins + 4
    lowest_pays[ties] -= 2
    payoffs[rows, mins] = lowest_pays
    return paygame.game_replace(base, profiles, payoffs)
Ejemplo n.º 12
0
def gen_num_profiles(base, num, distribution=default_distribution):
    """Generate profiles given game structure

    Parameters
    ----------
    base : RsGame
        Game to generate payoffs for.
    count : int
        The number of profiles to generate.
    distribution : (shape) -> ndarray, optional
        Distribution function to draw payoffs from.
    """
    utils.check(
        0 <= num <= base.num_all_profiles,
        'num must be in [0, {:d}] but was {:d}', base.num_all_profiles, num)
    profiles = sample_profiles(base, num)
    payoffs = np.zeros(profiles.shape)
    mask = profiles > 0
    payoffs[mask] = distribution(mask.sum())
    return paygame.game_replace(base, profiles, payoffs)
Ejemplo n.º 13
0
def test_payoff_vals():
    """Test payoff values"""
    profiles = [[2, 0, 1, 0, 0],
                [2, 0, 0, 1, 0],
                [2, 0, 0, 0, 1],
                [1, 1, 1, 0, 0],
                [1, 1, 0, 1, 0],
                [1, 1, 0, 0, 1],
                [0, 2, 1, 0, 0],
                [0, 2, 0, 1, 0],
                [0, 2, 0, 0, 1]]
    payoffs = [[4, 0, 8, 0, 0],
               [-1, 0, 0, 10, 0],
               [1, 0, 0, 0, 5],
               [1, 6, 6, 0, 0],
               [0, 3, 0, 5, 0],
               [-2, 3, 0, 0, 9],
               [0, 6, 11, 0, 0],
               [0, 3, 0, 6, 0],
               [0, 3, 0, 0, 10]]
    copy = paygame.game_replace(_GAME, profiles, payoffs)
    assert paygame.game_copy(_GAME) == copy
Ejemplo n.º 14
0
def keep_num_profiles(base, num):
    """Keep random profiles from an existing game

    Parameters
    ----------
    base : RsGame
        Game to keep profiles from.
    num : int
        The number of profiles to keep from the game.
    """
    utils.check(
        0 <= num <= base.num_profiles, 'num must be in [0, {:d}] but was {:d}',
        base.num_profiles, num)
    if num == 0:
        profiles = np.empty((0, base.num_strats), int)
        payoffs = np.empty((0, base.num_strats))
    elif base.is_complete():
        profiles = sample_profiles(base, num)
        payoffs = base.get_payoffs(profiles)
    else:
        inds = rand.choice(base.num_profiles, num, replace=False)
        profiles = base.profiles()[inds]
        payoffs = base.payoffs()[inds]
    return paygame.game_replace(base, profiles, payoffs)
Ejemplo n.º 15
0
def reduce_game(full_game, red_players):
    """Reduce a game using hierarchical reduction

    Parameters
    ----------
    full_game : Game
        The game to reduce.
    red_players : ndarray-like
        The reduced number of players for each role. This will be coerced
        into the proper shape if necessary.
    """
    red_game = rsgame.empty_names(
        full_game.role_names, red_players, full_game.strat_names)
    utils.check(
        np.all(red_game.num_role_players > 0),
        'all reduced players must be greater than zero')
    utils.check(
        np.all(full_game.num_role_players >= red_game.num_role_players),
        'all full counts must not be less than reduced counts')

    if full_game.is_empty():
        return red_game
    elif full_game.num_profiles < red_game.num_all_profiles:
        profiles = full_game.profiles()
        payoffs = full_game.payoffs()
    else:
        profiles = expand_profiles(
            full_game, red_game.all_profiles())
        payoffs = full_game.get_payoffs(profiles)
        valid = ~np.all(np.isnan(payoffs) | (profiles == 0), 1)
        profiles = profiles[valid]
        payoffs = payoffs[valid]

    red_profiles, mask = _common.reduce_profiles(
        full_game, red_game.num_role_players[None], profiles)
    return paygame.game_replace(red_game, red_profiles, payoffs[mask])
Ejemplo n.º 16
0
def full_game(role_names, role_players, strat_names):
    """Return a full game"""
    base = rsgame.empty_names(role_names, role_players, strat_names)
    return paygame.game_replace(
        base, base.all_profiles(),
        np.zeros((base.num_all_profiles, base.num_strats)))
Ejemplo n.º 17
0
def reduce_game(full_game, red_players): # pylint: disable=too-many-locals
    """Reduce a game using deviation preserving reduction

    Parameters
    ----------
    full_game : Game
        The game to reduce.
    red_players : ndarray-like
        The reduced number of players for each role. This will be coerced
        into the proper shape if necessary.
    """
    red_game = rsgame.empty_names(
        full_game.role_names, red_players, full_game.strat_names)
    utils.check(
        np.all((red_game.num_role_players > 1) |
               (full_game.num_role_players == 1)),
        'all reduced players must be greater than zero')
    utils.check(
        np.all(full_game.num_role_players >= red_game.num_role_players),
        'all full counts must not be less than reduced counts')

    if full_game.is_empty():
        return red_game
    elif full_game.num_profiles < red_game.num_all_dpr_profiles:
        full_profiles = full_game.profiles()
        full_payoffs = full_game.payoffs()
    else:
        full_profiles = expand_profiles(
            full_game, red_game.all_profiles())
        full_payoffs = full_game.get_payoffs(full_profiles)
        valid = ~np.all(np.isnan(full_payoffs) |
                        (full_profiles == 0), 1)
        full_profiles = full_profiles[valid]
        full_payoffs = full_payoffs[valid]

    # Reduce
    red_profiles, red_inds, full_inds, strat_inds = _reduce_profiles(
        red_game, full_profiles, True)

    if red_profiles.size == 0:  # Empty reduction
        return red_game

    # Build mapping from payoffs to reduced profiles, and use bincount
    # to count the number of payoffs mapped to a specific location, and
    # sum the number of payoffs mapped to a specific location
    cum_inds = red_inds * full_game.num_strats + strat_inds
    payoff_vals = full_payoffs[full_inds, strat_inds]
    red_payoffs = np.bincount(
        cum_inds, payoff_vals, red_profiles.size).reshape(
            red_profiles.shape)
    red_payoff_counts = np.bincount(
        cum_inds, minlength=red_profiles.size).reshape(
            red_profiles.shape)
    mask = red_payoff_counts > 1
    red_payoffs[mask] /= red_payoff_counts[mask]

    unknown = (red_profiles > 0) & (red_payoff_counts == 0)
    red_payoffs[unknown] = np.nan
    valid = ~np.all((red_profiles == 0) | np.isnan(red_payoffs), 1)
    return paygame.game_replace(red_game, red_profiles[valid],
                                red_payoffs[valid])