diff --git a/quantecon/game_theory/__init__.py b/quantecon/game_theory/__init__.py index b7beb74cf..4d05ef5b1 100644 --- a/quantecon/game_theory/__init__.py +++ b/quantecon/game_theory/__init__.py @@ -4,3 +4,4 @@ """ from .normal_form_game import Player, NormalFormGame from .normal_form_game import pure2mixed, best_response_2p +from .random import random_game, covariance_game diff --git a/quantecon/game_theory/random.py b/quantecon/game_theory/random.py new file mode 100644 index 000000000..2c4397105 --- /dev/null +++ b/quantecon/game_theory/random.py @@ -0,0 +1,100 @@ +""" +Filename: random.py + +Author: Daisuke Oyama + +Generate random NormalFormGame instances. + +""" +import numpy as np + +from .normal_form_game import Player, NormalFormGame +from ..util import check_random_state + + +def random_game(nums_actions, random_state=None): + """ + Return a random NormalFormGame instance where the payoffs are drawn + independently from the uniform distribution on [0, 1). + + Parameters + ---------- + nums_actions : tuple(int) + Tuple of the numbers of actions, one for each player. + + random_state : scalar(int) or np.random.RandomState, + optional(default=None) + Random seed (integer) or np.random.RandomState instance to set + the initial state of the random number generator for + reproducibility. If None, a randomly initialized RandomState is + used. + + Returns + ------- + g : NormalFormGame + + """ + N = len(nums_actions) + if N == 0: + raise ValueError('nums_actions must be non-empty') + + random_state = check_random_state(random_state) + players = [ + Player(random_state.random_sample(nums_actions[i:]+nums_actions[:i])) + for i in range(N) + ] + g = NormalFormGame(players) + return g + + +def covariance_game(nums_actions, rho, random_state=None): + """ + Return a random NormalFormGame instance where the payoff profiles + are drawn independently from the standard multi-normal with the + covariance of any pair of payoffs equal to `rho`, as studied in + [1]_. + + Parameters + ---------- + nums_actions : tuple(int) + Tuple of the numbers of actions, one for each player. + + rho : scalar(float) + Covariance of a pair of payoff values. Must be in [-1/(N-1), 1], + where N is the number of players. + + random_state : scalar(int) or np.random.RandomState, + optional(default=None) + Random seed (integer) or np.random.RandomState instance to set + the initial state of the random number generator for + reproducibility. If None, a randomly initialized RandomState is + used. + + Returns + ------- + g : NormalFormGame + + References + ---------- + .. [1] Y. Rinott and M. Scarsini, "On the Number of Pure Strategy + Nash Equilibria in Random Games," Games and Economic Behavior + (2000), 274-293. + + """ + N = len(nums_actions) + if N <= 1: + raise ValueError('length of nums_actions must be at least 2') + if not (-1 / (N - 1) <= rho <= 1): + lb = '-1' if N == 2 else '-1/{0}'.format(N-1) + raise ValueError('rho must be in [{0}, 1]'.format(lb)) + + mean = np.zeros(N) + cov = np.empty((N, N)) + cov.fill(rho) + cov[range(N), range(N)] = 1 + + random_state = check_random_state(random_state) + payoff_profile_array = \ + random_state.multivariate_normal(mean, cov, nums_actions) + g = NormalFormGame(payoff_profile_array) + return g diff --git a/quantecon/game_theory/tests/test_random.py b/quantecon/game_theory/tests/test_random.py new file mode 100644 index 000000000..d4ac714ca --- /dev/null +++ b/quantecon/game_theory/tests/test_random.py @@ -0,0 +1,72 @@ +""" +Filename: test_random.py +Author: Daisuke Oyama + +Tests for game_theory/random.py + +""" +import numpy as np +from numpy.testing import assert_allclose, assert_raises +from nose.tools import eq_ + +from quantecon.game_theory import random_game, covariance_game + + +def test_random_game(): + nums_actions = (2, 3, 4) + g = random_game(nums_actions) + eq_(g.nums_actions, nums_actions) + + +def test_covariance_game(): + nums_actions = (2, 3, 4) + N = len(nums_actions) + + rho = 0.5 + g = covariance_game(nums_actions, rho=rho) + eq_(g.nums_actions, nums_actions) + + rho = 1 + g = covariance_game(nums_actions, rho=rho) + for a in np.ndindex(*nums_actions): + for i in range(N-1): + payoff_profile = g.payoff_profile_array[a] + assert_allclose(payoff_profile[i], payoff_profile[-1]) + + rho = -1 / (N - 1) + g = covariance_game(nums_actions, rho=rho) + for a in np.ndindex(*nums_actions): + assert_allclose(g.payoff_profile_array.sum(axis=-1), + np.zeros(nums_actions), + atol=1e-10) + + +def test_random_game_value_error(): + nums_actions = () # empty + assert_raises(ValueError, random_game, nums_actions) + + +def test_covariance_game_value_error(): + nums_actions = () # empty + assert_raises(ValueError, covariance_game, nums_actions, rho=0) + + nums_actions = (2,) # length one + assert_raises(ValueError, covariance_game, nums_actions, rho=0) + + nums_actions = (2, 3, 4) + + rho = 1.1 # > 1 + assert_raises(ValueError, covariance_game, nums_actions, rho) + + rho = -1 # < -1/(N-1) + assert_raises(ValueError, covariance_game, nums_actions, rho) + + +if __name__ == '__main__': + import sys + import nose + + argv = sys.argv[:] + argv.append('--verbose') + argv.append('--nocapture') + nose.main(argv=argv, defaultTest=__file__)