def test_hierarchical_dev_expansion(): """Test that hierarchical dev expansion is correct""" game = rsgame.empty([9, 16], [4, 3]) mask = [True, False, True, False, False, True, False] profs = hr.expand_deviation_profiles(game, mask, [3, 4]) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [6, 3, 0, 0, 0, 16, 0], [3, 3, 3, 0, 0, 16, 0], [0, 3, 6, 0, 0, 16, 0], [6, 0, 0, 3, 0, 16, 0], [3, 0, 3, 3, 0, 16, 0], [0, 0, 6, 3, 0, 16, 0], [9, 0, 0, 0, 4, 12, 0], [6, 0, 3, 0, 4, 12, 0], [3, 0, 6, 0, 4, 12, 0], [0, 0, 9, 0, 4, 12, 0], [9, 0, 0, 0, 0, 12, 4], [6, 0, 3, 0, 0, 12, 4], [3, 0, 6, 0, 0, 12, 4], [0, 0, 9, 0, 0, 12, 4], ]) assert np.setxor1d(actual, expected).size == 0 profs = hr.expand_deviation_profiles(game, mask, [3, 4], 0) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [6, 3, 0, 0, 0, 16, 0], [3, 3, 3, 0, 0, 16, 0], [0, 3, 6, 0, 0, 16, 0], [6, 0, 0, 3, 0, 16, 0], [3, 0, 3, 3, 0, 16, 0], [0, 0, 6, 3, 0, 16, 0], ]) assert np.setxor1d(actual, expected).size == 0
def test_rand_dpr_allow_incomplete(add_prob, num_obs, game_desc): """Test that allow_incomplete works for random games""" # Generate games players, strategies, red_players = game_desc base = rsgame.BaseGame(players, strategies) game = gamegen.add_profiles(base, add_prob) sgame = gamegen.add_noise(game, 1, num_obs) red = reduction.DeviationPreserving(strategies, players, red_players) # Try to reduce game red_game = red.reduce_game(game, True) red_sgame = red.reduce_game(sgame, True) # Verify that when allow_incomplete, then reduce returns all profiles reduced_full_profiles = utils.axis_to_elem( red.reduce_profiles(game.profiles)) reduced_profiles = utils.axis_to_elem(red_game.profiles) assert np.setxor1d(reduced_profiles, reduced_full_profiles).size == 0 reduced_sample_profiles = utils.axis_to_elem(red_sgame.profiles) assert np.setxor1d(reduced_sample_profiles, reduced_full_profiles).size == 0 redord = np.argsort(reduced_profiles) redsord = np.argsort(reduced_sample_profiles) assert np.all(np.isnan(red_game.payoffs[redord]) == np.isnan(red_sgame.payoffs[redsord])), \ "sample game and game didn't have same nan payoffs" assert all(np.all(np.isnan(p).any(-1) == np.isnan(p).all(-1)) for p in red_sgame.sample_payoffs), \ "some sample payoffs had partial nans"
def test_identity_dev_expansion(): """Test that identity dev expansion is correct""" game = rsgame.empty([3, 4], [4, 3]) mask = [True, False, True, False, False, True, False] profs = ir.expand_deviation_profiles(game, mask) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [2, 1, 0, 0, 0, 4, 0], [1, 1, 1, 0, 0, 4, 0], [0, 1, 2, 0, 0, 4, 0], [2, 0, 0, 1, 0, 4, 0], [1, 0, 1, 1, 0, 4, 0], [0, 0, 2, 1, 0, 4, 0], [3, 0, 0, 0, 1, 3, 0], [2, 0, 1, 0, 1, 3, 0], [1, 0, 2, 0, 1, 3, 0], [0, 0, 3, 0, 1, 3, 0], [3, 0, 0, 0, 0, 3, 1], [2, 0, 1, 0, 0, 3, 1], [1, 0, 2, 0, 0, 3, 1], [0, 0, 3, 0, 0, 3, 1], ]) assert np.setxor1d(actual, expected).size == 0 profs = ir.expand_deviation_profiles(game, mask, role_index=0) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [2, 1, 0, 0, 0, 4, 0], [1, 1, 1, 0, 0, 4, 0], [0, 1, 2, 0, 0, 4, 0], [2, 0, 0, 1, 0, 4, 0], [1, 0, 1, 1, 0, 4, 0], [0, 0, 2, 1, 0, 4, 0], ]) assert np.setxor1d(actual, expected).size == 0
def test_elem_axis(): """Test identity of axis_to_elem o axis_from_elem""" arr = np.array([[5.4, 2.2], [5.7, 2.8], [9.6, 1.2]], float) assert np.all(arr == utils.axis_from_elem(utils.axis_to_elem(arr))) assert np.all( arr.astype(int) == utils.axis_from_elem( utils.axis_to_elem(arr.astype(int))))
def test_twins_dev_expansion(): """Test that dpr dev expansion is correct Note, this is the only one that has "new" code, so it's the most important to test.""" game = rsgame.empty([9, 16], [4, 3]) mask = [True, False, True, False, False, True, False] profs = tr.expand_deviation_profiles(game, mask) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [8, 1, 0, 0, 0, 16, 0], [0, 1, 8, 0, 0, 16, 0], [8, 0, 0, 1, 0, 16, 0], [0, 0, 8, 1, 0, 16, 0], [9, 0, 0, 0, 1, 15, 0], [5, 0, 4, 0, 1, 15, 0], [0, 0, 9, 0, 1, 15, 0], [9, 0, 0, 0, 0, 15, 1], [5, 0, 4, 0, 0, 15, 1], [0, 0, 9, 0, 0, 15, 1], ]) assert np.setxor1d(actual, expected).size == 0 profs = tr.expand_deviation_profiles(game, mask, role_index=0) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [8, 1, 0, 0, 0, 16, 0], [0, 1, 8, 0, 0, 16, 0], [8, 0, 0, 1, 0, 16, 0], [0, 0, 8, 1, 0, 16, 0], ]) assert np.setxor1d(actual, expected).size == 0
def test_empty_elem_axis(): """Test axis_to_elem works for empty arrays""" arr = np.empty((0, 2), float) assert np.all(arr.shape == utils.axis_from_elem( utils.axis_to_elem(arr)).shape) assert np.all( arr.astype(int).shape == utils.axis_from_elem(utils.axis_to_elem(arr.astype(int))).shape)
def test_elem_axis(): """Test identity of axis_to_elem o axis_from_elem""" arr = np.array([[5.4, 2.2], [5.7, 2.8], [9.6, 1.2]], float) assert np.all(arr == utils.axis_from_elem(utils.axis_to_elem(arr))) assert np.all(arr.astype(int) == utils.axis_from_elem(utils.axis_to_elem(arr.astype(int))))
def test_empty_elem_axis(): """Test axis_to_elem works for empty arrays""" arr = np.empty((0, 2), float) assert np.all( arr.shape == utils.axis_from_elem(utils.axis_to_elem(arr)).shape) assert np.all( arr.astype(int).shape == utils.axis_from_elem( utils.axis_to_elem(arr.astype(int))).shape)
def test_elem_axis(): x = np.array([[5.4, 2.2], [5.7, 2.8], [9.6, 1.2]], float) assert np.all(x == utils.elem_to_axis(utils.axis_to_elem(x), float)) assert np.all(x.astype(int) == utils.elem_to_axis(utils.axis_to_elem(x.astype(int)), int)) assert utils.unique_axis(x).shape == (3, 2) array, counts = utils.unique_axis(x.astype(int), return_counts=True) assert array.shape == (2, 2) assert not np.setxor1d(counts, [2, 1]).size
def test_max_prob_prof(players, strategies): game = rsgame.basegame(players, strategies) profiles = game.all_profiles() log_prob = (np.sum(sps.gammaln(game.num_players + 1)) - np.sum(sps.gammaln(profiles + 1), 1)) for mix in game.random_mixtures(100): probs = np.sum(np.log(mix + TINY) * profiles, 1) + log_prob mask = np.max(probs) - EPS < probs max_prob_profs = profiles[mask] actual = game.max_prob_prof(mix) assert np.all(np.in1d(utils.axis_to_elem(actual), utils.axis_to_elem(max_prob_profs)))
def test_approximate_dpr_expansion(): """Test expansion on approximate dpr""" full_game = rsgame.empty([8, 11], [2, 2]) red_prof = [[1, 2, 2, 2]] full_profs = dpr.expand_profiles( full_game, red_prof) profs = utils.axis_to_elem(full_profs) expected = utils.axis_to_elem([ [4, 4, 6, 5], [1, 7, 6, 5], [3, 5, 4, 7], [3, 5, 7, 4]]) assert np.setxor1d(profs, expected).size == 0, \ 'generated different profiles than expected'
def test_maximal_subgames_partial_profiles(): """Test that maximal subgames properly handles partial profiles""" profiles = [[2, 0], [1, 1], [0, 2]] payoffs = [[1, 0], [np.nan, 2], [0, 3]] game = rsgame.Game([2], [2], profiles, payoffs) subs = subgame.maximal_subgames(game) expected = utils.axis_to_elem(np.array([ [True, False], [False, True]])) assert np.setxor1d(utils.axis_to_elem(subs), expected).size == 0, \ "Didn't produce both pure subgames"
def test_maximal_restrictions_partial_profiles(): """Test that maximal restrictions properly handles partial profiles""" profiles = [[2, 0], [1, 1], [0, 2]] payoffs = [[1, 0], [np.nan, 2], [0, 3]] game = paygame.game([2], [2], profiles, payoffs) rests = restrict.maximal_restrictions(game) expected = utils.axis_to_elem(np.array([ [True, False], [False, True]])) assert np.setxor1d(utils.axis_to_elem(rests), expected).size == 0, \ "Didn't produce both pure restrictions"
def expand_profiles(self, profiles, return_contributions=False): """Expand a set of profiles If `return_contributions` then a boolean array of matching shape is returned indicating the payoffs that are needed for the initial profiles.""" profiles = np.asarray(profiles, int) num_profs = profiles.shape[0] dev_profs = profiles[:, None] - np.eye(self.full_game.num_role_strats, dtype=int) dev_profs = np.reshape(dev_profs, (-1, self.full_game.num_role_strats)) dev_full_players = self._devs(self.full_game.num_players, num_profs) dev_red_players = self._devs(self.red_game.num_players, num_profs) mask = ~np.any(dev_profs < 0, 1) devs = np.eye(self.full_game.num_role_strats, dtype=bool)[None]\ .repeat(num_profs, 0)\ .reshape((-1, self.full_game.num_role_strats))[mask] dev_full_profs = _expand_rsym_profiles( self.full_game, dev_profs[mask], dev_full_players[mask], dev_red_players[mask]) + devs ids = utils.axis_to_elem(dev_full_profs) if not return_contributions: return dev_full_profs[np.unique(ids, return_index=True)[1]] else: # This is more complicated because we need to line up devs for the # same profile se we can "reduceat" to merge them order = np.argsort(ids) sids = ids[order] mask = np.insert(sids[1:] != sids[:-1], 0, True) profs = dev_full_profs[order[mask]] ored_devs = np.bitwise_or.reduceat(devs[order], mask.nonzero()[0], 0) return profs, ored_devs
def restrict(self, restriction): restriction = np.asarray(restriction, bool) base = rsgame.empty_copy(self).restrict(restriction) size_mask = restriction.repeat(self._sizes) sizes = self._sizes[restriction] profiles = self._profiles[size_mask] lengths = self._lengths[restriction] zeros = (profiles[:, ~restriction] / lengths[:, ~restriction].repeat(sizes, 0)) removed = np.exp(-np.einsum('ij,ij->i', zeros, zeros) / 2) # pylint: disable=invalid-unary-operand-type uprofs, inds = np.unique(recfunctions.merge_arrays([ np.arange(restriction.sum()).repeat(sizes).view([('s', int)]), utils.axis_to_elem(profiles[:, restriction]) ], flatten=True), return_inverse=True) new_alpha = np.bincount(inds, removed * self._alpha[size_mask]) new_sizes = np.diff( np.concatenate([[-1], np.flatnonzero(np.diff(uprofs['s'])), [new_alpha.size - 1]])) return _RbfGpGame(base.role_names, base.strat_names, base.num_role_players, self._offset[restriction], self._coefs[restriction], lengths[:, restriction], new_sizes, uprofs['axis'], new_alpha)
def restrict(self, restriction): restriction = np.asarray(restriction, bool) base = rsgame.empty_copy(self).restrict(restriction) size_mask = restriction.repeat(self._sizes) sizes = self._sizes[restriction] profiles = self._profiles[size_mask] lengths = self._lengths[restriction] zeros = (profiles[:, ~restriction] / lengths[:, ~restriction].repeat(sizes, 0)) removed = np.exp(-np.einsum('ij,ij->i', zeros, zeros) / 2) # pylint: disable=invalid-unary-operand-type uprofs, inds = np.unique( recfunctions.merge_arrays([ np.arange(restriction.sum()).repeat(sizes).view([('s', int)]), utils.axis_to_elem(profiles[:, restriction])], flatten=True), return_inverse=True) new_alpha = np.bincount(inds, removed * self._alpha[size_mask]) new_sizes = np.diff(np.concatenate([ [-1], np.flatnonzero(np.diff(uprofs['s'])), [new_alpha.size - 1]])) return _RbfGpGame( base.role_names, base.strat_names, base.num_role_players, self._offset[restriction], self._coefs[restriction], lengths[:, restriction], new_sizes, uprofs['axis'], new_alpha)
def __hash__(self): hprofs = np.sort( utils.axis_to_elem( np.concatenate([ np.arange(self.num_strats).repeat(self._sizes)[:, None], self._profiles ], 1))).tobytes() return hash((super().__hash__(), hprofs))
def rbfgame_train(game, num_restarts=3): # pylint: disable=too-many-locals """Train a regression game with an RBF Gaussian process This model is somewhat well tests and has a few added benefits over standard regression models due the nature of its functional form. Parameters ---------- game : RsGame The game to learn. Must have at least one payoff per strategy. num_restarts : int, optional The number of random restarts to make with the optimizer. Higher numbers will give a better fit (in expectation), but will take longer. """ dev_players = np.maximum(game.num_role_players - np.eye( game.num_roles, dtype=int), 1).repeat( game.num_role_strats, 0).repeat(game.num_role_strats, 1) bounds = np.insert(dev_players[..., None], 0, 1, 2) # TODO Add an alpha that is smaller for points near the edge of the # simplex, accounting for the importance of minimizing error at the # extrema. means = np.empty(game.num_strats) coefs = np.empty(game.num_strats) lengths = np.empty((game.num_strats, game.num_strats)) profiles = [] alpha = [] sizes = [] for (strat, profs, pays), bound in zip(_dev_profpay(game), bounds): pay_mean = pays.mean() pays -= pay_mean reg = gp.GaussianProcessRegressor( 1.0 * gp.kernels.RBF(bound.mean(1), bound) + gp.kernels.WhiteKernel(1), n_restarts_optimizer=num_restarts, copy_X_train=False) reg.fit(profs, pays) means[strat] = pay_mean coefs[strat] = reg.kernel_.k1.k1.constant_value lengths[strat] = reg.kernel_.k1.k2.length_scale uprofs, inds = np.unique( utils.axis_to_elem(profs), return_inverse=True) profiles.append(utils.axis_from_elem(uprofs)) alpha.append(np.bincount(inds, reg.alpha_)) sizes.append(uprofs.size) if np.any(lengths[..., None] == bounds): warnings.warn( 'some lengths were at their bounds, this may indicate a poor ' 'fit') return _RbfGpGame( game.role_names, game.strat_names, game.num_role_players, means, coefs, lengths, np.array(sizes), np.concatenate(profiles), np.concatenate(alpha))
def rbfgame_train(game, num_restarts=3): # pylint: disable=too-many-locals """Train a regression game with an RBF Gaussian process This model is somewhat well tests and has a few added benefits over standard regression models due the nature of its functional form. Parameters ---------- game : RsGame The game to learn. Must have at least one payoff per strategy. num_restarts : int, optional The number of random restarts to make with the optimizer. Higher numbers will give a better fit (in expectation), but will take longer. """ dev_players = np.maximum( game.num_role_players - np.eye(game.num_roles, dtype=int), 1).repeat(game.num_role_strats, 0).repeat(game.num_role_strats, 1) bounds = np.insert(dev_players[..., None], 0, 1, 2) # TODO Add an alpha that is smaller for points near the edge of the # simplex, accounting for the importance of minimizing error at the # extrema. means = np.empty(game.num_strats) coefs = np.empty(game.num_strats) lengths = np.empty((game.num_strats, game.num_strats)) profiles = [] alpha = [] sizes = [] for (strat, profs, pays), bound in zip(_dev_profpay(game), bounds): pay_mean = pays.mean() pays -= pay_mean reg = gp.GaussianProcessRegressor( 1.0 * gp.kernels.RBF(bound.mean(1), bound) + gp.kernels.WhiteKernel(1), n_restarts_optimizer=num_restarts, copy_X_train=False) reg.fit(profs, pays) means[strat] = pay_mean coefs[strat] = reg.kernel_.k1.k1.constant_value lengths[strat] = reg.kernel_.k1.k2.length_scale uprofs, inds = np.unique(utils.axis_to_elem(profs), return_inverse=True) profiles.append(utils.axis_from_elem(uprofs)) alpha.append(np.bincount(inds, reg.alpha_)) sizes.append(uprofs.size) if np.any(lengths[..., None] == bounds): warnings.warn( 'some lengths were at their bounds, this may indicate a poor ' 'fit') return _RbfGpGame(game.role_names, game.strat_names, game.num_role_players, means, coefs, lengths, np.array(sizes), np.concatenate(profiles), np.concatenate(alpha))
def test_approximate_dpr_expansion(): """Test expansion on approximate dpr""" red = reduction.DeviationPreserving([2, 2], [8, 11], [3, 4]) red_prof = [[1, 2, 2, 2]] full_profs, contributions = red.expand_profiles(red_prof, True) profs = utils.axis_to_elem(full_profs) expected = utils.axis_to_elem([ [4, 4, 6, 5], [1, 7, 6, 5], [3, 5, 4, 7], [3, 5, 7, 4]]) assert np.setxor1d(profs, expected).size == 0, \ "generated different profiles than expected" expected_conts = np.array([ [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], bool) eord = np.argsort(expected) pord = np.argsort(profs) assert np.all(expected_conts[eord] == contributions[pord])
def test_dpr_dev_expansion(): """Test that dpr dev expansion is correct Note, this is the only one that has "new" code, so it's the most important to test.""" game = rsgame.BaseGame([3, 4], [4, 3]) red = reduction.DeviationPreserving( game.num_strategies, game.num_players ** 2, game.num_players) mask = [True, False, True, False, False, True, False] profs = red.expand_deviation_profiles(mask) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [8, 1, 0, 0, 0, 16, 0], [4, 1, 4, 0, 0, 16, 0], [0, 1, 8, 0, 0, 16, 0], [8, 0, 0, 1, 0, 16, 0], [4, 0, 4, 1, 0, 16, 0], [0, 0, 8, 1, 0, 16, 0], [9, 0, 0, 0, 1, 15, 0], [6, 0, 3, 0, 1, 15, 0], [3, 0, 6, 0, 1, 15, 0], [0, 0, 9, 0, 1, 15, 0], [9, 0, 0, 0, 0, 15, 1], [6, 0, 3, 0, 0, 15, 1], [3, 0, 6, 0, 0, 15, 1], [0, 0, 9, 0, 0, 15, 1], ]) assert np.setxor1d(actual, expected).size == 0 profs = red.expand_deviation_profiles(mask, 0) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [8, 1, 0, 0, 0, 16, 0], [4, 1, 4, 0, 0, 16, 0], [0, 1, 8, 0, 0, 16, 0], [8, 0, 0, 1, 0, 16, 0], [4, 0, 4, 1, 0, 16, 0], [0, 0, 8, 1, 0, 16, 0], ]) assert np.setxor1d(actual, expected).size == 0
def test_random_dpr(keep_prob, game_desc, _): """Simple test that dpr functions are consistent""" players, strategies, red_players = game_desc # Create game game = gamegen.game(players, strategies, keep_prob) # Try to reduce game red_game = dpr.reduce_game(game, red_players) assert (rsgame.empty(red_players, strategies) == dpr.reduce_game(rsgame.empty(players, strategies), red_players)) # Assert that reducing all profiles covers reduced game reduced_profiles = utils.axis_to_elem(red_game.profiles()) reduced_full_profiles = utils.axis_to_elem( dpr.reduce_profiles(red_game, game.profiles())) assert np.setdiff1d(reduced_profiles, reduced_full_profiles).size == 0, \ "reduced game contained profiles it shouldn't have" # Assert that all contributing profiles are in the expansion of the reduced # game, we need to first filter for complete profiles full_profiles = utils.axis_to_elem(game.profiles()) complete_profs = ~np.isnan(red_game.payoffs()).any(1) full_reduced_profiles = utils.axis_to_elem( dpr.expand_profiles(game, red_game.profiles()[complete_profs])) assert np.setdiff1d(full_reduced_profiles, full_profiles).size == 0, \ 'full game did not have data for all profiles required of reduced' # Assert that dpr counts are accurate num_dpr_profiles = dpr.expand_profiles( game, red_game.all_profiles()).shape[0] assert num_dpr_profiles == red_game.num_all_dpr_profiles # Test the dpr deviation profile counts are accurate rest = red_game.random_restriction() dpr_devs = dpr.expand_profiles( game, restrict.deviation_profiles(red_game, rest)).shape[0] num = restrict.num_dpr_deviation_profiles(red_game, rest) assert dpr_devs == num, \ "num_dpr_deviation_profiles didn't return correct number"
def test_identity(keep_prob, game_desc): players, strategies = game_desc # Create game and reduction game = gamegen.role_symmetric_game(players, strategies) game = gamegen.drop_profiles(game, keep_prob) red = reduction.Identity(strategies, players) # Try to reduce game red_game = red.reduce_game(game) # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( red.reduce_profiles(game.profiles)) reduced_profiles = utils.axis_to_elem(red_game.profiles) assert np.setxor1d(reduced_full_profiles, reduced_profiles).size == 0, \ "reduced game didn't match full game" full_profiles = utils.axis_to_elem(game.profiles) full_reduced_profiles = utils.axis_to_elem( red.expand_profiles(red_game.profiles)) assert np.setxor1d(full_profiles, full_reduced_profiles).size == 0, \ "full game did not match reduced game"
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 test_random_identity(keep_prob, game_desc, _): """Test random identity""" players, strategies = game_desc # Create game and reduction game = gamegen.game(players, strategies, keep_prob) assert (paygame.game_copy(rsgame.empty(players, strategies)) == ir.reduce_game(rsgame.empty(players, strategies))) # Try to reduce game red_game = ir.reduce_game(game) # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( ir.reduce_profiles(red_game, game.profiles())) reduced_profiles = utils.axis_to_elem(red_game.profiles()) assert np.setxor1d(reduced_full_profiles, reduced_profiles).size == 0, \ "reduced game didn't match full game" full_profiles = utils.axis_to_elem(game.profiles()) full_reduced_profiles = utils.axis_to_elem( ir.expand_profiles(game, red_game.profiles())) assert np.setxor1d(full_profiles, full_reduced_profiles).size == 0, \ 'full game did not match reduced game'
def test_random_twins(players, strategies, keep_prob, _): """Test random twins reduction""" # Create game and reduction game = gamegen.game(players, strategies, keep_prob) # Try to reduce game red_game = tr.reduce_game(game) # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( tr.reduce_profiles(red_game, game.profiles())) reduced_profiles = utils.axis_to_elem(red_game.profiles()) assert np.setdiff1d(reduced_profiles, reduced_full_profiles).size == 0, \ "reduced game contained profiles it shouldn't have" # Assert that all contributing profiles are in the expansion of the reduced # game. We need to remove partial profiles first full_profiles = utils.axis_to_elem(game.profiles()) complete_profs = ~np.isnan(red_game.payoffs()).any(1) full_reduced_profiles = utils.axis_to_elem( tr.expand_profiles(game, red_game.profiles()[complete_profs])) assert np.setdiff1d(full_reduced_profiles, full_profiles).size == 0, \ 'full game did not have data for all profiles required of reduced'
def test_expansion_contributions(): """Test expansion on approximate dpr""" red = reduction.DeviationPreserving([2, 2], [4, 9], [2, 3]) red_profs = [ [2, 0, 0, 3], [1, 1, 3, 0]] full_profs, contributions = red.expand_profiles(red_profs, True) profs = utils.axis_to_elem(full_profs) expected = utils.axis_to_elem([ [4, 0, 0, 9], [1, 3, 9, 0], [3, 1, 9, 0], [2, 2, 9, 0]]) assert np.setxor1d(profs, expected).size == 0, \ "generated different profiles than expected" expected_conts = np.array([ [1, 0, 0, 1], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]], bool) eord = np.argsort(expected) pord = np.argsort(profs) assert np.all(expected_conts[eord] == contributions[pord])
def test_hierarchical_dev_expansion(): """Test that hierarchical dev expansion is correct""" game = rsgame.BaseGame([3, 4], [4, 3]) red = reduction.Hierarchical(game.num_strategies, game.num_players ** 2, game.num_players) mask = [True, False, True, False, False, True, False] profs = red.expand_deviation_profiles(mask) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [6, 3, 0, 0, 0, 16, 0], [3, 3, 3, 0, 0, 16, 0], [0, 3, 6, 0, 0, 16, 0], [6, 0, 0, 3, 0, 16, 0], [3, 0, 3, 3, 0, 16, 0], [0, 0, 6, 3, 0, 16, 0], [9, 0, 0, 0, 4, 12, 0], [6, 0, 3, 0, 4, 12, 0], [3, 0, 6, 0, 4, 12, 0], [0, 0, 9, 0, 4, 12, 0], [9, 0, 0, 0, 0, 12, 4], [6, 0, 3, 0, 0, 12, 4], [3, 0, 6, 0, 0, 12, 4], [0, 0, 9, 0, 0, 12, 4], ]) assert np.setxor1d(actual, expected).size == 0 profs = red.expand_deviation_profiles(mask, 0) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [6, 3, 0, 0, 0, 16, 0], [3, 3, 3, 0, 0, 16, 0], [0, 3, 6, 0, 0, 16, 0], [6, 0, 0, 3, 0, 16, 0], [3, 0, 3, 3, 0, 16, 0], [0, 0, 6, 3, 0, 16, 0], ]) assert np.setxor1d(actual, expected).size == 0
def test_identity_dev_expansion(): """Test that identity dev expansion is correct""" game = rsgame.BaseGame([3, 4], [4, 3]) red = reduction.Identity(game.num_strategies, game.num_players) mask = [True, False, True, False, False, True, False] profs = red.expand_deviation_profiles(mask) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [2, 1, 0, 0, 0, 4, 0], [1, 1, 1, 0, 0, 4, 0], [0, 1, 2, 0, 0, 4, 0], [2, 0, 0, 1, 0, 4, 0], [1, 0, 1, 1, 0, 4, 0], [0, 0, 2, 1, 0, 4, 0], [3, 0, 0, 0, 1, 3, 0], [2, 0, 1, 0, 1, 3, 0], [1, 0, 2, 0, 1, 3, 0], [0, 0, 3, 0, 1, 3, 0], [3, 0, 0, 0, 0, 3, 1], [2, 0, 1, 0, 0, 3, 1], [1, 0, 2, 0, 0, 3, 1], [0, 0, 3, 0, 0, 3, 1], ]) assert np.setxor1d(actual, expected).size == 0 profs = red.expand_deviation_profiles(mask, 0) actual = utils.axis_to_elem(profs) expected = utils.axis_to_elem([ [2, 1, 0, 0, 0, 4, 0], [1, 1, 1, 0, 0, 4, 0], [0, 1, 2, 0, 0, 4, 0], [2, 0, 0, 1, 0, 4, 0], [1, 0, 1, 1, 0, 4, 0], [0, 0, 2, 1, 0, 4, 0], ]) assert np.setxor1d(actual, expected).size == 0
async def test_basic_profile(): """Test that basic profiles are sampled twice""" sgame = gamegen.samplegame([4, 3], [3, 4]) profs = utils.axis_from_elem( np.unique(utils.axis_to_elem(sgame.random_profiles(20)))) save = savesched.savesched(gamesched.samplegamesched(sgame)) sched = countsched.countsched(save, 10) assert str(sched) is not None paylist = await asyncio.gather(*[sched.sample_payoffs(p) for p in profs]) pays = np.stack(paylist) assert np.allclose(pays[profs == 0], 0) savegame = save.get_game() assert list(savegame.num_samples) == [10]
def test_all_subgames(players, strategies): game = rsgame.BaseGame(players, strategies) all_subgames = subgame.all_subgames(game) assert game.role_reduce(all_subgames, ufunc=np.logical_or).all(), \ "Not all subgames were valid" distinct = np.unique(utils.axis_to_elem(all_subgames)).size assert distinct == all_subgames.shape[0] ids = subgame.subgame_id(game, all_subgames) distinct_ids = np.unique(ids).size assert distinct_ids == all_subgames.shape[0] all_subgames2 = subgame.subgame_from_id(game, ids) assert np.all(all_subgames == all_subgames2)
def test_twins(keep_prob, game_desc): players, strategies = game_desc # Create game and reduction game = gamegen.role_symmetric_game(players, strategies) game = gamegen.drop_profiles(game, keep_prob) red = reduction.Twins(strategies, players) # Try to reduce game red_game = red.reduce_game(game) # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( red.reduce_profiles(game.profiles)) reduced_profiles = utils.axis_to_elem(red_game.profiles) assert np.setdiff1d(reduced_profiles, reduced_full_profiles).size == 0, \ "reduced game contained profiles it shouldn't have" # Assert that all contributing profiles are in the expansion of the reduced # game full_profiles = utils.axis_to_elem(game.profiles) full_reduced_profiles = utils.axis_to_elem( red.expand_profiles(red_game.profiles)) assert np.setdiff1d(full_reduced_profiles, full_profiles).size == 0, \ "full game did not have data for all profiles required of reduced"
def test_dpr(keep_prob, game_desc): """Simple test that dpr functions are consistent""" players, strategies, red_players = game_desc # Create game and reduction game = gamegen.role_symmetric_game(players, strategies) game = gamegen.drop_profiles(game, keep_prob) sgame = gamegen.add_noise(game, 1, 3) red = reduction.DeviationPreserving(strategies, players, red_players) # Try to reduce game assert rsgame.basegame_copy(game) == red.full_game assert red.reduce_game(rsgame.basegame_copy(game)) == red.red_game red_game = red.reduce_game(game) red_game2 = reduction.reduce_game_dpr(game, red_players) red_sgame = red.reduce_game(sgame) # Assert that reduce_game_dpr produces identical results reduced_profiles = utils.axis_to_elem(red_game.profiles) reduced_profiles2 = utils.axis_to_elem(red_game2.profiles) assert np.setxor1d(reduced_profiles, reduced_profiles2).size == 0, \ "different reduction functions didn't produce identical results" # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( red.reduce_profiles(game.profiles)) assert np.setdiff1d(reduced_profiles, reduced_full_profiles).size == 0, \ "reduced game contained profiles it shouldn't have" reduced_sample_profiles = utils.axis_to_elem(red_sgame.profiles) assert np.setdiff1d(reduced_sample_profiles, reduced_full_profiles).size == 0, \ "reduced sample game contained profiles it shouldn't have" assert np.setxor1d(reduced_sample_profiles, reduced_profiles).size == 0, \ "reduced sample game and reduced game had different profiles" # Assert that all contributing profiles are in the expansion of the reduced # game full_profiles = utils.axis_to_elem(game.profiles) full_reduced_profiles = utils.axis_to_elem( red.expand_profiles(red_game.profiles)) assert np.setdiff1d(full_reduced_profiles, full_profiles).size == 0, \ "full game did not have data for all profiles required of reduced" full_reduced_sample_profiles = utils.axis_to_elem( red.expand_profiles(red_sgame.profiles)) assert np.setdiff1d(full_reduced_sample_profiles, full_profiles).size == 0, \ ("full sample game did not have data for all profiles required of " "reduced") assert np.setxor1d(full_reduced_profiles, full_reduced_sample_profiles).size == 0, \ "sample game didn't produce identical results"
def test_random_hierarchical(keep_prob, players, strategies, red_players, _): """Test random hierarchical""" # Create game and reduction game = gamegen.game(players, strategies, keep_prob) assert (rsgame.empty(red_players, strategies) == hr.reduce_game(rsgame.empty(players, strategies), red_players)) # Try to reduce game red_game = hr.reduce_game(game, red_players) # Assert that reducing all profiles covers reduced game reduced_full_profiles = utils.axis_to_elem( hr.reduce_profiles(red_game, game.profiles())) reduced_profiles = utils.axis_to_elem(red_game.profiles()) assert np.setxor1d(reduced_profiles, reduced_full_profiles).size == 0, \ "reduced game contained profiles it shouldn't have" # Assert that all contributing profiles are in the expansion of the reduced # game. Since hr doesn't add any incomplete profiles, it can't produce any full_profiles = utils.axis_to_elem(game.profiles()) full_reduced_profiles = utils.axis_to_elem( hr.expand_profiles(game, red_game.profiles())) assert np.setdiff1d(full_reduced_profiles, full_profiles).size == 0, \ 'full game did not have data for all profiles required of reduced'
def _reduce_profiles(red_game, profiles, return_contributions): # pylint: disable=too-many-locals """Reduce profiles using dpr Parameters ---------- red_game : Game Game that reduced profiles will be profiles for. profiles : ndarray-like The profiles to reduce. return_contributions : bool, optional If true return ancillary information about where the payoffs come from. """ profiles = np.asarray(profiles, int) utils.check( profiles.shape[-1] == red_game.num_strats, 'profiles not a valid shape') if not profiles.size: return np.empty((0, red_game.num_strats), int) profiles = profiles.reshape((-1, red_game.num_strats)) all_full_players = np.add.reduceat(profiles, red_game.role_starts, 1) full_players = all_full_players[0] utils.check( np.all(all_full_players == full_players), 'profiles must be valid') num_profs = profiles.shape[0] dev_profs = profiles.repeat(np.sum(profiles > 0, 1), 0) _, strat_inds = profiles.nonzero() dev_profs[np.arange(dev_profs.shape[0]), strat_inds] -= 1 dev_red_players = _devs(red_game, num_profs) mask = (profiles > 0).ravel() red_profs, reduced = _common.reduce_profiles( red_game, dev_red_players[mask], dev_profs) rstrat_inds = strat_inds[reduced] red_profs[np.arange(red_profs.shape[0]), rstrat_inds] += 1 red_profs, red_inds = np.unique( utils.axis_to_elem(red_profs), return_inverse=True) red_profs = utils.axis_from_elem(red_profs) if not return_contributions: return red_profs full_inds = np.arange(num_profs).repeat( red_game.num_strats)[mask][reduced] return red_profs, red_inds, full_inds, rstrat_inds
def _reduce_profiles(red_game, profiles, return_contributions): # pylint: disable=too-many-locals """Reduce profiles using dpr Parameters ---------- red_game : Game Game that reduced profiles will be profiles for. profiles : ndarray-like The profiles to reduce. return_contributions : bool, optional If true return ancillary information about where the payoffs come from. """ profiles = np.asarray(profiles, int) utils.check(profiles.shape[-1] == red_game.num_strats, 'profiles not a valid shape') if not profiles.size: return np.empty((0, red_game.num_strats), int) profiles = profiles.reshape((-1, red_game.num_strats)) all_full_players = np.add.reduceat(profiles, red_game.role_starts, 1) full_players = all_full_players[0] utils.check(np.all(all_full_players == full_players), 'profiles must be valid') num_profs = profiles.shape[0] dev_profs = profiles.repeat(np.sum(profiles > 0, 1), 0) _, strat_inds = profiles.nonzero() dev_profs[np.arange(dev_profs.shape[0]), strat_inds] -= 1 dev_red_players = _devs(red_game, num_profs) mask = (profiles > 0).ravel() red_profs, reduced = _common.reduce_profiles(red_game, dev_red_players[mask], dev_profs) rstrat_inds = strat_inds[reduced] red_profs[np.arange(red_profs.shape[0]), rstrat_inds] += 1 red_profs, red_inds = np.unique(utils.axis_to_elem(red_profs), return_inverse=True) red_profs = utils.axis_from_elem(red_profs) if not return_contributions: return red_profs full_inds = np.arange(num_profs).repeat(red_game.num_strats)[mask][reduced] return red_profs, red_inds, full_inds, rstrat_inds
def expand_profiles(full_game, profiles): # pylint: disable=too-many-locals """Expand profiles using dpr Parameters ---------- full_game : Game Game that expanded profiles will be valid for. profiles : ndarray-like The profiles to expand return_contributions : bool, optional If specified, returns a boolean array matching the shape is returned indicating the payoffs that are needed for the initial profiles. """ profiles = np.asarray(profiles, int) utils.check( profiles.shape[-1] == full_game.num_strats, 'profiles not a valid shape') if not profiles.size: return np.empty((0, full_game.num_strats), int) profiles = profiles.reshape((-1, full_game.num_strats)) all_red_players = np.add.reduceat(profiles, full_game.role_starts, 1) red_players = all_red_players[0] utils.check( np.all(all_red_players == red_players), 'profiles must be valid') num_profs = profiles.shape[0] dev_profs = profiles[:, None] - np.eye(full_game.num_strats, dtype=int) dev_profs = np.reshape(dev_profs, (-1, full_game.num_strats)) dev_full_players = _devs(full_game, num_profs) mask = ~np.any(dev_profs < 0, 1) devs = (np.eye(full_game.num_strats, dtype=bool)[None] .repeat(num_profs, 0) .reshape((-1, full_game.num_strats))[mask]) dev_full_profs = _common.expand_profiles( full_game, dev_full_players[mask], dev_profs[mask]) + devs ids = utils.axis_to_elem(dev_full_profs) return dev_full_profs[np.unique(ids, return_index=True)[1]]
def expand_profiles(full_game, profiles): # pylint: disable=too-many-locals """Expand profiles using dpr Parameters ---------- full_game : Game Game that expanded profiles will be valid for. profiles : ndarray-like The profiles to expand return_contributions : bool, optional If specified, returns a boolean array matching the shape is returned indicating the payoffs that are needed for the initial profiles. """ profiles = np.asarray(profiles, int) utils.check(profiles.shape[-1] == full_game.num_strats, 'profiles not a valid shape') if not profiles.size: return np.empty((0, full_game.num_strats), int) profiles = profiles.reshape((-1, full_game.num_strats)) all_red_players = np.add.reduceat(profiles, full_game.role_starts, 1) red_players = all_red_players[0] utils.check(np.all(all_red_players == red_players), 'profiles must be valid') num_profs = profiles.shape[0] dev_profs = profiles[:, None] - np.eye(full_game.num_strats, dtype=int) dev_profs = np.reshape(dev_profs, (-1, full_game.num_strats)) dev_full_players = _devs(full_game, num_profs) mask = ~np.any(dev_profs < 0, 1) devs = (np.eye(full_game.num_strats, dtype=bool)[None].repeat(num_profs, 0).reshape( (-1, full_game.num_strats))[mask]) dev_full_profs = _common.expand_profiles(full_game, dev_full_players[mask], dev_profs[mask]) + devs ids = utils.axis_to_elem(dev_full_profs) return dev_full_profs[np.unique(ids, return_index=True)[1]]
def __hash__(self): return hash( (super().__hash__(), np.sort(utils.axis_to_elem(self.function_inputs.T)).tobytes()))
def test_sample_profiles(players, strats, _): """Test sample profiles""" game = gamegen.game(players, strats) profiles = gamegen.sample_profiles(game, 5) uprofs = utils.axis_from_elem(np.unique(utils.axis_to_elem(profiles))) assert uprofs.shape == (5, game.num_strats)
def array_set_equals(one, two): """Returns true if the unique last dimensions are the same set""" return not np.setxor1d(utils.axis_to_elem(one), utils.axis_to_elem(two)).size
def array_set_equal(arr, brr): """Return true if two sets of arrays are equal""" return not np.setxor1d( utils.axis_to_elem(arr), utils.axis_to_elem(brr)).size