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

Add 'interior-point' option to is_dominated; add dominated_actions #415

Merged
merged 3 commits into from
Jun 5, 2018
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
38 changes: 35 additions & 3 deletions quantecon/game_theory/normal_form_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,9 @@ def is_dominated(self, action, tol=None, method=None):
method : str, optional(default=None)
If None, `lemke_howson` from `quantecon.game_theory` is used
to solve for a Nash equilibrium of an auxiliary zero-sum
game. If `method='simplex'`, `scipy.optimize.linprog` is
used with `method='simplex'`.
game. If `method` is set to `'simplex'` or
`'interior-point'`, `scipy.optimize.linprog` is used with
the method as specified by `method`.

Returns
-------
Expand Down Expand Up @@ -425,7 +426,7 @@ def is_dominated(self, action, tol=None, method=None):
g_zero_sum = NormalFormGame([Player(D), Player(-D.T)])
NE = lemke_howson(g_zero_sum)
return NE[0] @ D @ NE[1] > tol
elif method in ['simplex']:
elif method in ['simplex', 'interior-point']:
from scipy.optimize import linprog
m, n = D.shape
A = np.empty((n+2, m+1))
Expand All @@ -449,6 +450,37 @@ def is_dominated(self, action, tol=None, method=None):
else:
raise ValueError('Unknown method {0}'.format(method))

def dominated_actions(self, tol=None, method=None):
"""
Return a list of actions that are strictly dominated by some
mixed actions.

Parameters
----------
tol : scalar(float), optional(default=None)
Tolerance level used in determining domination. If None,
default to the value of the `tol` attribute.

method : str, optional(default=None)
If None, `lemke_howson` from `quantecon.game_theory` is used
to solve for a Nash equilibrium of an auxiliary zero-sum
game. If `method` is set to `'simplex'` or
`'interior-point'`, `scipy.optimize.linprog` is used with
the method as specified by `method`.

Returns
-------
list(int)
List of integers representing pure actions, each of which is
strictly dominated by some mixed action.

"""
out = []
for action in range(self.num_actions):
if self.is_dominated(action, tol=tol, method=method):
out.append(action)
return out


class NormalFormGame:
"""
Expand Down
47 changes: 33 additions & 14 deletions quantecon/game_theory/tests/test_normal_form_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
Tests for normal_form_game.py

"""
from __future__ import division

import numpy as np
from numpy.testing import assert_array_equal
from nose.tools import eq_, ok_, raises
Expand All @@ -15,6 +13,9 @@

# Player #

LP_METHODS = [None, 'simplex', 'interior-point']


class TestPlayer_1opponent:
"""Test the methods of Player with one opponent player"""

Expand Down Expand Up @@ -69,9 +70,13 @@ def test_is_best_response_against_mixed(self):

def test_is_dominated(self):
for action in range(self.player.num_actions):
for method in [None, 'simplex']:
for method in LP_METHODS:
eq_(self.player.is_dominated(action, method=method), False)

def test_dominated_actions(self):
for method in LP_METHODS:
eq_(self.player.dominated_actions(method=method), [])


class TestPlayer_2opponents:
"""Test the methods of Player with two opponent players"""
Expand Down Expand Up @@ -106,9 +111,13 @@ def test_best_response_list_when_tie(self):

def test_is_dominated(self):
for action in range(self.player.num_actions):
for method in [None, 'simplex']:
for method in LP_METHODS:
eq_(self.player.is_dominated(action, method=method), False)

def test_dominated_actions(self):
for method in LP_METHODS:
eq_(self.player.dominated_actions(method=method), [])


def test_random_choice():
n, m = 5, 4
Expand All @@ -126,17 +135,20 @@ def test_player_corner_cases():
player = Player(np.zeros((n, m)))
for action in range(n):
eq_(player.is_best_response(action, [1/m]*m), True)
for method in [None, 'simplex']:
for method in LP_METHODS:
eq_(player.is_dominated(action, method=method), False)

e = 1e-8
player = Player([[-e, -e], [1, -1], [-1, 1]])
action = 0
eq_(player.is_best_response(action, [1/2, 1/2], tol=e), True)
eq_(player.is_best_response(action, [1/2, 1/2], tol=e/2), False)
for method in [None, 'simplex']:
eq_(player.is_dominated(action, tol=e, method=method), False)
for method in LP_METHODS:
eq_(player.is_dominated(action, tol=2*e, method=method), False)
eq_(player.dominated_actions(tol=2*e, method=method), [])

eq_(player.is_dominated(action, tol=e/2, method=method), True)
eq_(player.dominated_actions(tol=e/2, method=method), [action])


# NormalFormGame #
Expand Down Expand Up @@ -289,25 +301,32 @@ class TestPlayer_0opponents:

def setUp(self):
"""Setup a Player instance"""
payoffs = [0, 1]
self.player = Player(payoffs)
self.payoffs = [0, 1, -1]
self.player = Player(self.payoffs)
self.best_response_action = 1
self.dominated_actions = [0, 2]

def test_payoff_vector(self):
"""Trivial player: payoff_vector"""
assert_array_equal(self.player.payoff_vector(None), [0, 1])
assert_array_equal(self.player.payoff_vector(None), self.payoffs)

def test_is_best_response(self):
"""Trivial player: is_best_response"""
ok_(self.player.is_best_response(1, None))
ok_(self.player.is_best_response(self.best_response_action, None))

def test_best_response(self):
"""Trivial player: best_response"""
eq_(self.player.best_response(None), 1)
eq_(self.player.best_response(None), self.best_response_action)

def test_is_dominated(self):
"""Trivial player: is_dominated"""
eq_(self.player.is_dominated(0), True)
eq_(self.player.is_dominated(1), False)
for action in range(self.player.num_actions):
eq_(self.player.is_dominated(action),
(action in self.dominated_actions))

def test_dominated_actions(self):
"""Trivial player: dominated_actions"""
eq_(self.player.dominated_actions(), self.dominated_actions)


class TestNormalFormGame_1p:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def write_version_py(filename=None):
install_requires=[
'numba>=0.36.2',
'numpy',
'scipy',
'scipy>=1.0.0',
'sympy',
]
)