Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

game_theory: Add random game generators #270

Merged
merged 1 commit into from
Nov 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions quantecon/game_theory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
100 changes: 100 additions & 0 deletions quantecon/game_theory/random.py
Original file line number Diff line number Diff line change
@@ -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
72 changes: 72 additions & 0 deletions quantecon/game_theory/tests/test_random.py
Original file line number Diff line number Diff line change
@@ -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__)