Skip to content
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
12 changes: 6 additions & 6 deletions tests/games.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ def create_2x2x2_nfg() -> gbt.Game:
- The payoff to a player is the sum of their incident edges across the implied cut
- Pure equilibrium iff local max cuts; in addition, uniform mixture is an equilibrium
- Equilibrium analysis for pure profiles:
a a a: 0 0 0 -- Not Nash (2 can deviate and get 4)
b a a: 1 2 -1 -- Not Nash (3 can deviate and get 2)
a a a: 0 0 0 -- Not Nash (regrets: 1, 4, 1)
b a a: 1 2 -1 -- Not Nash (regrets: 0, 0, 3)
a b a: 2 4 2 -- Nash (global max cut)
b b a: -1 2 1 -- Not Nash (1 can deviate and get 2)
a a b: -1 2 1 -- Not Nash (1 can deviate and get 2)
b b a: -1 2 1 -- Not Nash (regrets: 3, 0, 0)
a a b: -1 2 1 -- Not Nash (regrets: 3, 0, 0)
b a b: 2 4 2 -- Nash (global max cut)
a b b: 1 2 -1 -- Not Nash (3 can deviate and get 2)
b b b: 0 0 0 -- Not Nash (2 can deviate and get 4)
a b b: 1 2 -1 -- Not Nash (regrets: 0, 0, 3)
b b b: 0 0 0 -- Not Nash (regrets: 1, 4, 1)
"""
return read_from_file("2x2x2_nfg_with_two_pure_one_mixed_eq.nfg")

Expand Down
182 changes: 160 additions & 22 deletions tests/test_mixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ def test_strategy_value_reference(game: gbt.Game, profile_data: list, rational_f


@pytest.mark.parametrize(
"game,profile_data,liap_expected,tol,rational_flag",
"game,profile_data,liap_exp,tol,rational_flag",
[
##############################################################################
# Zero matrix nfg, all liap_values are zero
Expand All @@ -513,37 +513,110 @@ def test_strategy_value_reference(game: gbt.Game, profile_data: list, rational_f
# 4x4 coordination nfg
(games.create_coord_4x4_nfg(), None, 0, ZERO, True),
(games.create_coord_4x4_nfg(), None, 0, TOL, False),
(games.create_coord_4x4_nfg(),
[["1/3", "1/2", "1/12", "1/12"], ["3/8", "1/8", "1/4", "1/4"]], "245/2304", ZERO, True),
(games.create_coord_4x4_nfg(),
[["1/4", "1/4", "1/4", "1/4"], ["1/4", "1/4", "1/4", "1/4"]], 0, ZERO, True),
(games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [1, 0, 0, 0]], 0, ZERO, True),
(games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [1, 0, 0, 0]], 0, TOL, False),
(games.create_coord_4x4_nfg(),
[["1/3", "1/2", "1/12", "1/12"], ["3/8", "1/8", "1/4", "1/4"]],
"245/2304", ZERO, True),
(games.create_coord_4x4_nfg(), [[1/3, 1/2, 1/12, 1/12], [3/8, 1/8, 1/4, 1/4]],
245/2304, TOL, False),
(games.create_coord_4x4_nfg(),
[["1/3", 0, 0, "2/3"], [1, 0, 0, 0]], "5/9", ZERO, True),
(games.create_coord_4x4_nfg(),
[[1/3, 0, 0, 2/3], [1, 0, 0, 0]], 5/9, TOL, False),
##############################################################################
# El Farol bar game efg
(games.create_el_farol_bar_game_efg(),
[["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"]], "0",
ZERO, True),
(games.create_el_farol_bar_game_efg(),
[["1/1", "0/1"], ["1/1", "0/1"], ["0/1", "1/1"], ["0/1", "1/1"], ["0/1", "1/1"]], "0",
ZERO, True),
[["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"]],
0, ZERO, True),
(games.create_el_farol_bar_game_efg(), [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1]],
0, ZERO, True),
##############################################################################
# 2x2x2 nfg with 2 pure and 1 mixed eq
# # 2x2x2 nfg with 2 pure and 1 mixed eq
# Pure non-Nash eq:
(games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], 18, ZERO, True), # 4^2+1+1
(games.create_2x2x2_nfg(), [[0, 1], [0, 1], [0, 1]], 18, ZERO, True), # 4^2+1+1
(games.create_2x2x2_nfg(), [[1, 0], [0, 1], [0, 1]], 9, ZERO, True), # 3^2
(games.create_2x2x2_nfg(), [[0, 1], [1, 0], [1, 0]], 9, ZERO, True), # 3^2
(games.create_2x2x2_nfg(), [[1, 0], [0, 1], [0, 1]], 9, ZERO, True), # 3^2
(games.create_2x2x2_nfg(), [[1, 1], [1, 0], [0, 0]], 9, ZERO, True), # 3^2
# Non-pure non-Nash eq:
(games.create_2x2x2_nfg(), [["1/2", "1/2"], [1, 0], [1, 0]], "33/4", ZERO, True),
(games.create_2x2x2_nfg(), [[1, 0], ["1/2", "1/2"], [1, 0]], 4, ZERO, True),
(games.create_2x2x2_nfg(), [[1, 0], [1, 0], ["1/2", "1/2"]], "33/4", ZERO, True),
# Nash eq:
(games.create_2x2x2_nfg(), [[1, 0], [0, 1], [1, 0]], 0, ZERO, True),
(games.create_2x2x2_nfg(), [[0, 1], [1, 0], [0, 1]], 0, ZERO, True),
(games.create_2x2x2_nfg(), None, 0, ZERO, True), # uniform is Nash
]
)
def test_liapunov_value_reference(game: gbt.Game, profile_data: list,
liap_expected: float | str,
tol: float | gbt.Rational | int,
rational_flag: bool):
liap_expected = gbt.Rational(liap_expected) if rational_flag else liap_expected
def test_liap_value_reference(game: gbt.Game, profile_data: list, liap_exp: float | str,
tol: float | gbt.Rational | int, rational_flag: bool):
profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data)
liap_exp = gbt.Rational(liap_exp) if rational_flag else liap_exp
assert abs(profile.liap_value() - liap_exp) <= tol


@pytest.mark.parametrize(
"game,profile_data,player_regrets_exp,tol,rational_flag",
[
##############################################################################
# Zero matrix nfg, all liap_values are zero
(games.create_2x2_zero_nfg(), [["3/4", "1/4"], ["2/5", "3/5"]], [0]*2, ZERO, True),
(games.create_2x2_zero_nfg(), [["1/2", "1/2"], ["1/2", "1/2"]], [0]*2, ZERO, True),
(games.create_2x2_zero_nfg(), [[1, 0], [1, 0]], [0]*2, ZERO, True),
(games.create_2x2_zero_nfg(), [[1/4, 3/4], [2/5, 3/5]], [0]*2, TOL, False),
##############################################################################
# 4x4 coordination nfg
(games.create_coord_4x4_nfg(), None, [0]*2, ZERO, True),
(games.create_coord_4x4_nfg(), None, [0]*2, TOL, False),
(games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [1, 0, 0, 0]], [0]*2, ZERO, True),
(games.create_coord_4x4_nfg(), [[1, 0, 0, 0], [1, 0, 0, 0]], [0]*2, TOL, False),
(games.create_coord_4x4_nfg(),
[["1/3", "1/2", "1/12", "1/12"], ["3/8", "1/8", "1/4", "1/4"]],
["7/48", "13/48"], ZERO, True),
(games.create_coord_4x4_nfg(), [[1/3, 1/2, 1/12, 1/12], [3/8, 1/8, 1/4, 1/4]],
[7/48, 13/48], TOL, False),
(games.create_coord_4x4_nfg(),
[["1/3", 0, 0, "2/3"], [1, 0, 0, 0]], ["2/3", "1/3"], ZERO, True),
(games.create_coord_4x4_nfg(),
[[1/3, 0, 0, 2/3], [1, 0, 0, 0]], [2/3, 1/3], TOL, False),
##############################################################################
# El Farol bar game efg
(games.create_el_farol_bar_game_efg(),
[["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"], ["1/2", "1/2"]],
[0]*5, ZERO, True),
(games.create_el_farol_bar_game_efg(), [[1, 0], [1, 0], [0, 1], [0, 1], [0, 1]],
[0]*5, ZERO, True),
##############################################################################
# 2x2x2 nfg with 2 pure and 1 mixed eq
# Pure non-Nash
(games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], [1, 4, 1], ZERO, True), # 111
(games.create_2x2x2_nfg(), [[0, 1], [0, 1], [0, 1]], [1, 4, 1], ZERO, True), # 000
(games.create_2x2x2_nfg(), [[1, 0], [0, 1], [0, 1]], [0, 0, 3], ZERO, True), # 100
(games.create_2x2x2_nfg(), [[0, 1], [1, 0], [1, 0]], [0, 0, 3], ZERO, True), # 011
(games.create_2x2x2_nfg(), [[0, 1], [0, 1], [1, 0]], [3, 0, 0], ZERO, True), # 001
(games.create_2x2x2_nfg(), [[1, 0], [1, 0], [0, 1]], [3, 0, 0], ZERO, True), # 110
# Mixed non-Nash
(games.create_2x2x2_nfg(), [["1/2", "1/2"], [1, 0], [1, 0]], ["1/2", 2, 2], ZERO, True),
(games.create_2x2x2_nfg(), [[1, 0], ["1/2", "1/2"], [1, 0]], [0, 2, 0], ZERO, True),
(games.create_2x2x2_nfg(), [[1, 0], [1, 0], ["1/2", "1/2"]], [2, 2, "1/2"], ZERO, True),
# Nash eq:
(games.create_2x2x2_nfg(), [[1, 0], [0, 1], [1, 0]], [0]*3, ZERO, True), # 101
(games.create_2x2x2_nfg(), [[0, 1], [1, 0], [0, 1]], [0]*3, ZERO, True), # 010
(games.create_2x2x2_nfg(), None, [0]*3, ZERO, True), # uniform is Nash
]
)
def test_player_regret_max_regret_reference(game: gbt.Game, profile_data: list,
player_regrets_exp: list,
tol: float | gbt.Rational | int,
rational_flag: bool):
profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data)
assert abs(profile.liap_value() - liap_expected) <= tol
if rational_flag:
player_regrets_exp = [gbt.Rational(r) for r in player_regrets_exp]
for p, r in zip(game.players, player_regrets_exp, strict=True):
assert abs(profile.player_regret(p) - r) <= tol
assert abs(profile.max_regret() - max(player_regrets_exp)) <= tol


@pytest.mark.parametrize(
Expand Down Expand Up @@ -596,11 +669,11 @@ def test_strategy_regret_consistency(game: gbt.Game, rational_flag: bool):
#################################################################################
# Centipede with chance efg
(games.create_centipede_game_with_chance_efg(),
[["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", "0/1"]], ZERO, True),
[["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", 0]], ZERO, True),
(games.create_centipede_game_with_chance_efg(),
[[1/3, 1/3, 1/3, 0], [.10, 3/5, .3, 0]], TOL, False),
#################################################################################
# El Faor bar game efg
# El Farol bar game efg
(games.create_el_farol_bar_game_efg(),
[[1, 0], ["1/2", "1/2"], ["1/3", "2/3"], ["1/5", "4/5"], ["1/8", "7/8"]], ZERO, True),
(games.create_el_farol_bar_game_efg(),
Expand All @@ -613,9 +686,9 @@ def test_strategy_regret_consistency(game: gbt.Game, rational_flag: bool):
(games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], TOL, False),
]
)
def test_liapunov_value_consistency(game: gbt.Game, profile_data: list,
tol: float | gbt.Rational,
rational_flag: bool):
def test_liap_value_consistency(game: gbt.Game, profile_data: list,
tol: float | gbt.Rational,
rational_flag: bool):
profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data)

assert (
Expand All @@ -625,6 +698,48 @@ def test_liapunov_value_consistency(game: gbt.Game, profile_data: list,
)


@pytest.mark.parametrize(
"game,profile_data,tol,rational_flag",
[
#################################################################################
# 4x4 coordination nfg
(games.create_coord_4x4_nfg(),
[["1/5", "2/5", "0/5", "2/5"], ["3/8", "1/4", "3/8", "0/4"]], ZERO, True),
(games.create_coord_4x4_nfg(),
[[1/3, 1/3, 0/3, 1/3], [1/4, 1/4, 3/8, 1/8]], TOL, False),
#################################################################################
# Centipede with chance efg
(games.create_centipede_game_with_chance_efg(),
[["1/3", "1/3", "1/3", "0/1"], ["1/10", "3/5", "3/10", 0]], ZERO, True),
(games.create_centipede_game_with_chance_efg(),
[[1/3, 1/3, 1/3, 0], [.10, 3/5, .3, 0]], TOL, False),
#################################################################################
# El Farol bar game efg
(games.create_el_farol_bar_game_efg(),
[[1, 0], ["1/2", "1/2"], ["1/3", "2/3"], ["1/5", "4/5"], ["1/8", "7/8"]], ZERO, True),
(games.create_el_farol_bar_game_efg(),
[[1, 0], [1/2, 1/2], [1/3, 2/3], [1/5, 4/5], [1/8, 7/8]], TOL, False),
#################################################################################
# 2x2x2 nfg with 2 pure and 1 mixed eq
(games.create_2x2x2_nfg(), None, ZERO, True),
(games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], ZERO, True),
(games.create_2x2x2_nfg(), None, TOL, False),
(games.create_2x2x2_nfg(), [[1, 0], [1, 0], [1, 0]], TOL, False),
]
)
def test_player_regret_max_regret_consistency(game: gbt.Game, profile_data: list,
tol: float | gbt.Rational,
rational_flag: bool):
profile = game.mixed_strategy_profile(rational=rational_flag, data=profile_data)
player_regrets = []
for p in game.players:
p_regret = max([max(profile.strategy_value(strategy) - profile.payoff(p), 0)
for strategy in p.strategies])
player_regrets.append(p_regret)
assert abs(profile.player_regret(p) - p_regret) <= tol
assert abs(profile.max_regret() - max(player_regrets)) <= tol


@pytest.mark.parametrize(
"game,profile1,profile2,alpha,tol,rational_flag",
[
Expand Down Expand Up @@ -933,6 +1048,29 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2:
pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True,
lambda profile, y: profile.liap_value(), lambda x: [1],
id="liap_value_poker_rat"),
#################################################################################
# max_regret (of profile, hence [1] for objects_to_test, any singleton collection would do)
# 4x4 coordination nfg
pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_doub, PROBS_2A_doub, False,
lambda profile, y: profile.max_regret(), lambda x: [1],
id="max_regret_coord_doub"),
pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_rat, PROBS_2A_rat, True,
lambda profile, y: profile.max_regret(), lambda x: [1],
id="max_regret_coord_rat"),
# 2x2x2 nfg
pytest.param(games.create_2x2x2_nfg(), PROBS_1B_doub, PROBS_2B_doub, False,
lambda profile, y: profile.max_regret(), lambda x: [1],
id="max_regret_2x2x2_doub"),
pytest.param(games.create_2x2x2_nfg(), PROBS_1B_rat, PROBS_2B_rat, True,
lambda profile, y: profile.max_regret(), lambda x: [1],
id="max_regret_2x2x2_rat"),
# stripped-down poker
pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False,
lambda profile, y: profile.max_regret(), lambda x: [1],
id="max_regret_poker_doub"),
pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True,
lambda profile, y: profile.max_regret(), lambda x: [1],
id="max_regret_poker_rat"),
]
)
def test_profile_order_consistency(game: gbt.Game,
Expand Down