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)
def test_acomb(): actual = utils.acomb(5, 0) assert actual.shape == (1, 5) assert not actual.any() actual = utils.acomb(5, 5) assert actual.shape == (1, 5) assert actual.all() actual = utils.acomb(6, 4) expected = np.zeros_like(actual) for i, inds in enumerate(itertools.combinations(range(6), 4)): expected[i, inds] = True assert array_set_equals(actual, expected) actual = utils.acomb(6, 4, True) expected = np.zeros_like(actual) for i, inds in enumerate(map(list, itertools.combinations_with_replacement( range(6), 4))): np.add.at(expected[i], inds, 1) assert array_set_equals(actual, expected)
def test_acomb(): """Test acomb""" actual = utils.acomb(5, 0) assert actual.shape == (1, 5) assert not actual.any() actual = utils.acomb(5, 5) assert actual.shape == (1, 5) assert actual.all() actual = utils.acomb(6, 4) expected = np.zeros_like(actual) for i, inds in enumerate(itertools.combinations(range(6), 4)): expected[i, inds] = True assert array_set_equals(actual, expected) actual = utils.acomb(6, 4, True) expected = np.zeros_like(actual) for i, inds in enumerate( map(list, itertools.combinations_with_replacement(range(6), 4))): np.add.at(expected[i], inds, 1) assert array_set_equals(actual, expected)
def grid_mixtures(self, num_points): """Returns all of the mixtures in a grid with n points Arguments --------- num_points : int > 1 The number of points to have along one dimensions """ assert num_points > 1, "Must have at least two points on a dimensions" role_mixtures = [utils.acomb(num_strats, num_points - 1, True) / (num_points - 1) for num_strats in self.num_strategies] return utils.acartesian2(*role_mixtures)
def test_acomb(): actual = utils.acomb(5, 0) assert actual.shape == (1, 5) assert not actual.any() actual = utils.acomb(5, 5) assert actual.shape == (1, 5) assert actual.all() actual = utils.acomb(6, 4) expected = np.zeros_like(actual) for i, inds in enumerate(itertools.combinations(range(6), 4)): expected[i, inds] = True assert np.setxor1d(utils.axis_to_elem(actual), utils.axis_to_elem(expected)).size == 0 actual = utils.acomb(6, 4, True) expected = np.zeros_like(actual) for i, inds in enumerate(map(list, itertools.combinations_with_replacement( range(6), 4))): np.add.at(expected[i], inds, 1) assert np.setxor1d(utils.axis_to_elem(actual), utils.axis_to_elem(expected)).size == 0
def congestion(num_players, num_facilities, num_required, degree=2, coef_dist=lambda d: -np.random.exponential(10. ** (1 - d))): """Generate a congestion game Parameters ---------- num_players : int num_facilities : int num_required : int degree : int, optional Degree of payoff polynomials coef_dist : f(int) -> float, optional Numpy compatible function for generating random coefficients conditioned on degree. Leading degree must be negative to generate a true congestion game. """ function_inputs = utils.acomb(num_facilities, num_required) table_dist = random_poly_dist(coef_dist, np.insert(np.zeros(degree), 2, 1)) functions = table_dist((num_facilities, num_players + 1)) return aggfn.aggfn(num_players, function_inputs.shape[0], function_inputs.T, function_inputs, functions)
def congestion(num_players, num_facilities, num_required, *, degree=2): """Generate a congestion game A congestion game is a symmetric game, where there are a given number of facilities, and each player must choose to use some amount of them. The payoff for each facility decreases as more players use it, and a players utility is the sum of the utilities for every facility. In this formulation, facility payoffs are random polynomials of the number of people using said facility. Parameters ---------- num_players : int > 1 The number of players. num_facilities : int > 1 The number of facilities. num_required : 0 < int < num_facilities The number of required facilities. degree : int > 0, optional Degree of payoff polynomials. """ utils.check(num_players > 1, 'must have more than one player') utils.check(num_facilities > 1, 'must have more than one facility') utils.check( 0 < num_required < num_facilities, 'must require more than zero but less than num_facilities') utils.check(degree > 0, 'degree must be greater than zero') function_inputs = utils.acomb(num_facilities, num_required) functions = -_random_monotone_polynomial(num_facilities, num_players, degree) facs = tuple(utils.prefix_strings('', num_facilities)) strats = tuple('_'.join(facs[i] for i, m in enumerate(mask) if m) for mask in function_inputs) return aggfn.aggfn_names( ['all'], num_players, [strats], function_inputs.T, function_inputs, functions)
def all_profiles(self): """Return all profiles""" role_arrays = [utils.acomb(n_strats, players, True) for n_strats, players in zip(self.num_strategies, self.num_players)] return utils.acartesian2(*role_arrays)