Skip to content

Commit fef1ab1

Browse files
committed
tests for max_regret/infoset regret on mixed behavior profiles
1 parent 0db7bef commit fef1ab1

File tree

3 files changed

+172
-21
lines changed

3 files changed

+172
-21
lines changed

tests/test_behav.py

Lines changed: 136 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -798,9 +798,11 @@ def test_action_value_by_label_reference(game: gbt.Game, label: str,
798798
(games.create_mixed_behav_game_efg(), True),
799799
(games.create_stripped_down_poker_efg(), False),
800800
(games.create_stripped_down_poker_efg(), True),
801+
(games.create_kuhn_poker_efg(), False),
802+
(games.create_kuhn_poker_efg(), True),
801803
]
802804
)
803-
def test_regret_consistency(game: gbt.Game, rational_flag: bool):
805+
def test_action_regret_consistency(game: gbt.Game, rational_flag: bool):
804806
profile = game.mixed_behavior_profile(rational=rational_flag)
805807
for player in game.players:
806808
for infoset in player.infosets:
@@ -812,6 +814,60 @@ def test_regret_consistency(game: gbt.Game, rational_flag: bool):
812814
)
813815

814816

817+
@pytest.mark.parametrize(
818+
"game,rational_flag",
819+
[(games.create_mixed_behav_game_efg(), False),
820+
(games.create_mixed_behav_game_efg(), True),
821+
(games.create_stripped_down_poker_efg(), False),
822+
(games.create_stripped_down_poker_efg(), True),
823+
(games.create_kuhn_poker_efg(), False),
824+
(games.create_kuhn_poker_efg(), True),
825+
]
826+
)
827+
def test_infoset_regret_consistency(game: gbt.Game, rational_flag: bool):
828+
profile = game.mixed_behavior_profile(rational=rational_flag)
829+
for player in game.players:
830+
for infoset in player.infosets:
831+
assert (
832+
profile.infoset_regret(infoset) ==
833+
max(profile.action_value(a) for a in infoset.actions) -
834+
profile.infoset_value(infoset)
835+
)
836+
837+
838+
@pytest.mark.parametrize(
839+
"game,rational_flag",
840+
[(games.create_mixed_behav_game_efg(), False),
841+
(games.create_mixed_behav_game_efg(), True),
842+
(games.create_stripped_down_poker_efg(), False),
843+
(games.create_stripped_down_poker_efg(), True),
844+
(games.create_kuhn_poker_efg(), False),
845+
(games.create_kuhn_poker_efg(), True),
846+
]
847+
)
848+
def test_max_regret_consistency(game: gbt.Game, rational_flag: bool):
849+
profile = game.mixed_behavior_profile(rational=rational_flag)
850+
assert profile.max_regret() == profile.as_strategy().max_regret()
851+
852+
853+
@pytest.mark.parametrize(
854+
"game,rational_flag",
855+
[(games.create_mixed_behav_game_efg(), False),
856+
(games.create_mixed_behav_game_efg(), True),
857+
(games.create_stripped_down_poker_efg(), False),
858+
(games.create_stripped_down_poker_efg(), True),
859+
(games.create_kuhn_poker_efg(), False),
860+
(games.create_kuhn_poker_efg(), True),
861+
]
862+
)
863+
def test_agent_max_regret_consistency(game: gbt.Game, rational_flag: bool):
864+
profile = game.mixed_behavior_profile(rational=rational_flag)
865+
assert (
866+
profile.agent_max_regret() ==
867+
max([profile.infoset_regret(infoset) for infoset in game.infosets])
868+
)
869+
870+
815871
@pytest.mark.parametrize(
816872
"game,player_idx,infoset_idx,action_idx,action_probs,rational_flag,tol,value",
817873
[
@@ -823,14 +879,14 @@ def test_regret_consistency(game: gbt.Game, rational_flag: bool):
823879
(games.create_mixed_behav_game_efg(), 2, 0, 0, None, False, TOL, 0),
824880
(games.create_mixed_behav_game_efg(), 2, 0, 1, None, False, TOL, 0.5), # 3.5 - 3
825881
# U1 U2 U3
826-
(games.create_mixed_behav_game_efg(), 0, 0, 0, [1.0, 0.0, 1.0, 0.0, 1.0, 0.0], False, TOL, 0),
827-
(games.create_mixed_behav_game_efg(), 0, 0, 0, ["1", "0", "1", "0", "1", "0"], True, ZERO, 0),
828-
(games.create_mixed_behav_game_efg(), 0, 0, 1, [1.0, 0.0, 1.0, 0.0, 1.0, 0.0], False, TOL, 9),
829-
(games.create_mixed_behav_game_efg(), 0, 0, 1, ["1", "0", "1", "0", "1", "0"], True, ZERO, 9),
830-
(games.create_mixed_behav_game_efg(), 1, 0, 0, [1.0, 0.0, 1.0, 0.0, 1.0, 0.0], False, TOL, 0),
831-
(games.create_mixed_behav_game_efg(), 1, 0, 0, ["1", "0", "1", "0", "1", "0"], True, ZERO, 0),
832-
(games.create_mixed_behav_game_efg(), 1, 0, 1, [1.0, 0.0, 1.0, 0.0, 1.0, 0.0], False, TOL, 8),
833-
(games.create_mixed_behav_game_efg(), 1, 0, 1, ["1", "0", "1", "0", "1", "0"], True, ZERO, 8),
882+
(games.create_mixed_behav_game_efg(), 0, 0, 0, [1, 0, 1, 0, 1, 0], False, TOL, 0),
883+
(games.create_mixed_behav_game_efg(), 0, 0, 0, [1, 0, 1, 0, 1, 0], True, ZERO, 0),
884+
(games.create_mixed_behav_game_efg(), 0, 0, 1, [1, 0, 1, 0, 1, 0], False, TOL, 9),
885+
(games.create_mixed_behav_game_efg(), 0, 0, 1, [1, 0, 1, 0, 1, 0], True, ZERO, 9),
886+
(games.create_mixed_behav_game_efg(), 1, 0, 0, [1, 0, 1, 0, 1, 0], False, TOL, 0),
887+
(games.create_mixed_behav_game_efg(), 1, 0, 0, [1, 0, 1, 0, 1, 0], True, ZERO, 0),
888+
(games.create_mixed_behav_game_efg(), 1, 0, 1, [1, 0, 1, 0, 1, 0], False, TOL, 8),
889+
(games.create_mixed_behav_game_efg(), 1, 0, 1, [1, 0, 1, 0, 1, 0], True, ZERO, 8),
834890
# Mixed Nash equilibrium
835891
(games.create_mixed_behav_game_efg(), 0, 0, 0, ["2/5", "3/5", "1/2", "1/2", "1/3", "2/3"],
836892
True, ZERO, 0),
@@ -858,9 +914,9 @@ def test_regret_consistency(game: gbt.Game, rational_flag: bool):
858914
True, ZERO, "8/3"), # (2/3*2 + 1/3*1) - (-1)
859915
]
860916
)
861-
def test_regret_reference(game: gbt.Game, player_idx: int, infoset_idx: int, action_idx: int,
862-
action_probs: None | list, rational_flag: bool,
863-
tol: gbt.Rational | float, value: str | float):
917+
def test_action_regret_reference(game: gbt.Game, player_idx: int, infoset_idx: int,
918+
action_idx: int, action_probs: None | list, rational_flag: bool,
919+
tol: gbt.Rational | float, value: str | float):
864920
action = game.players[player_idx].infosets[infoset_idx].actions[action_idx]
865921
profile = game.mixed_behavior_profile(rational=rational_flag)
866922
if action_probs:
@@ -955,6 +1011,41 @@ def test_agent_liap_value_reference(game: gbt.Game, action_probs: None | list,
9551011
)
9561012

9571013

1014+
@pytest.mark.parametrize(
1015+
"game,action_probs,rational_flag,max_regret,agent_max_regret,liap_value,agent_liap_value",
1016+
[
1017+
# uniform (non-Nash):
1018+
(games.create_mixed_behav_game_efg(), None, True, "1/4", "1/4", "1/16", "1/16"),
1019+
(games.create_mixed_behav_game_efg(), None, False, 0.25, 0.25, 0.0625, 0.0625),
1020+
# Myerson fig 2.4
1021+
pytest.param(
1022+
games.read_from_file("myerson_fig_4_2.efg"), [0, 1, 0, 1, 1, 0], True, 1, 0, 1, 0,
1023+
marks=pytest.mark.xfail(reason="Needs to be fixed now")
1024+
),
1025+
]
1026+
)
1027+
def test_agent_max_regret_versus_non_agent(game: gbt.Game, action_probs: None | list,
1028+
rational_flag: bool,
1029+
max_regret: str | float,
1030+
agent_max_regret: str | float,
1031+
agent_liap_value: str | float,
1032+
liap_value: str | float,
1033+
):
1034+
profile = game.mixed_behavior_profile(rational=rational_flag)
1035+
if action_probs:
1036+
_set_action_probs(profile, action_probs, rational_flag)
1037+
assert (profile.max_regret() == (gbt.Rational(max_regret) if rational_flag else max_regret))
1038+
assert (
1039+
profile.agent_max_regret() == (gbt.Rational(agent_max_regret)
1040+
if rational_flag else agent_max_regret)
1041+
)
1042+
assert (profile.liap_value() == (gbt.Rational(liap_value) if rational_flag else liap_value))
1043+
assert (
1044+
profile.agent_liap_value() == (gbt.Rational(agent_liap_value)
1045+
if rational_flag else agent_liap_value)
1046+
)
1047+
1048+
9581049
@pytest.mark.parametrize(
9591050
"game,tol,probs,infoset_idx,member_idx,value,rational_flag",
9601051
[(games.create_mixed_behav_game_efg(), TOL, [0.8, 0.2, 0.4, 0.6, 0.0, 1.0], 0, 0, 1.0, False),
@@ -1157,6 +1248,39 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2:
11571248
lambda x, y: x.agent_liap_value(), lambda x: [1]),
11581249
(games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True,
11591250
lambda x, y: x.agent_liap_value(), lambda x: [1]),
1251+
######################################################################################
1252+
# liap_value (of profile, hence [1] for objects_to_test,
1253+
# any singleton collection would do)
1254+
(games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False,
1255+
lambda x, y: x.liap_value(), lambda x: [1]),
1256+
(games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True,
1257+
lambda x, y: x.liap_value(), lambda x: [1]),
1258+
(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False,
1259+
lambda x, y: x.liap_value(), lambda x: [1]),
1260+
(games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True,
1261+
lambda x, y: x.liap_value(), lambda x: [1]),
1262+
######################################################################################
1263+
# agent_max_regret (of profile, hence [1] for objects_to_test,
1264+
# any singleton collection would do)
1265+
(games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False,
1266+
lambda x, y: x.agent_max_regret(), lambda x: [1]),
1267+
(games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True,
1268+
lambda x, y: x.agent_max_regret(), lambda x: [1]),
1269+
(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False,
1270+
lambda x, y: x.agent_max_regret(), lambda x: [1]),
1271+
(games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True,
1272+
lambda x, y: x.agent_max_regret(), lambda x: [1]),
1273+
######################################################################################
1274+
# max_regret (of profile, hence [1] for objects_to_test,
1275+
# any singleton collection would do)
1276+
(games.create_mixed_behav_game_efg(), PROBS_1A_doub, PROBS_2A_doub, False,
1277+
lambda x, y: x.max_regret(), lambda x: [1]),
1278+
(games.create_mixed_behav_game_efg(), PROBS_1A_rat, PROBS_2A_rat, True,
1279+
lambda x, y: x.max_regret(), lambda x: [1]),
1280+
(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False,
1281+
lambda x, y: x.max_regret(), lambda x: [1]),
1282+
(games.create_stripped_down_poker_efg(), PROBS_1A_rat, PROBS_2A_rat, True,
1283+
lambda x, y: x.max_regret(), lambda x: [1]),
11601284
]
11611285
)
11621286
def test_profile_order_consistency(game: gbt.Game,

tests/test_mixed.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,7 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2:
795795
PROBS_2A_doub = (0.5, 0, 0.5, 0)
796796
PROBS_1A_rat = ("1/4", "1/4", "1/4", "1/4")
797797
PROBS_2A_rat = ("1/2", "0", "1/2", "0")
798-
# For 2x2x2 nfg and Myserson 2-card poker efg (both have 6 strategies in total):
798+
# For 2x2x2 nfg and stripped_down_poker efg (both have 6 strategies in total):
799799
PROBS_1B_doub = (0.5, 0.5, 0.5, 0.5, 0.5, 0.5)
800800
PROBS_2B_doub = (1.0, 0.0, 1.0, 0.0, 1.0, 0.0)
801801
PROBS_1B_rat = ("1/2", "1/2", "1/2", "1/2", "1/2", "1/2")
@@ -933,6 +933,29 @@ def _get_and_check_answers(game: gbt.Game, action_probs1: tuple, action_probs2:
933933
pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True,
934934
lambda profile, y: profile.liap_value(), lambda x: [1],
935935
id="liap_value_poker_rat"),
936+
#################################################################################
937+
# max_regret (of profile, hence [1] for objects_to_test, any singleton collection would do)
938+
# 4x4 coordination nfg
939+
pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_doub, PROBS_2A_doub, False,
940+
lambda profile, y: profile.max_regret(), lambda x: [1],
941+
id="max_regret_coord_doub"),
942+
pytest.param(games.create_coord_4x4_nfg(), PROBS_1A_rat, PROBS_2A_rat, True,
943+
lambda profile, y: profile.max_regret(), lambda x: [1],
944+
id="max_regret_coord_rat"),
945+
# 2x2x2 nfg
946+
pytest.param(games.create_2x2x2_nfg(), PROBS_1B_doub, PROBS_2B_doub, False,
947+
lambda profile, y: profile.max_regret(), lambda x: [1],
948+
id="max_regret_2x2x2_doub"),
949+
pytest.param(games.create_2x2x2_nfg(), PROBS_1B_rat, PROBS_2B_rat, True,
950+
lambda profile, y: profile.max_regret(), lambda x: [1],
951+
id="max_regret_2x2x2_rat"),
952+
# stripped-down poker
953+
pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_doub, PROBS_2B_doub, False,
954+
lambda profile, y: profile.max_regret(), lambda x: [1],
955+
id="max_regret_poker_doub"),
956+
pytest.param(games.create_stripped_down_poker_efg(), PROBS_1B_rat, PROBS_2B_rat, True,
957+
lambda profile, y: profile.max_regret(), lambda x: [1],
958+
id="max_regret_poker_rat"),
936959
]
937960
)
938961
def test_profile_order_consistency(game: gbt.Game,

tests/test_nash.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ def test_enumpoly_ordered_behavior(
141141
game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int
142142
):
143143
"""Test calls of enumpoly for mixed behavior equilibria,
144-
using max_regret (internal consistency); and comparison to a set of previously
145-
computed equilibria using this function (regression test).
144+
using max_regret and agent_max_regret (internal consistency); and
145+
comparison to a set of previously computed equilibria with this function (regression test).
146146
This set will be the full set of all computed equilibria if stop_after is None,
147147
else the first stop_after-many equilibria.
148148
@@ -162,6 +162,7 @@ def test_enumpoly_ordered_behavior(
162162
result = gbt.nash.enumpoly_solve(game, use_strategic=False)
163163
assert len(result.equilibria) == len(mixed_behav_prof_data)
164164
for eq, exp in zip(result.equilibria, mixed_behav_prof_data, strict=True):
165+
assert abs(eq.max_regret()) <= TOL
165166
assert abs(eq.agent_max_regret()) <= TOL
166167
expected = game.mixed_behavior_profile(rational=True, data=exp)
167168
for p in game.players:
@@ -197,8 +198,8 @@ def test_enumpoly_unordered_behavior(
197198
game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int
198199
):
199200
"""Test calls of enumpoly for mixed behavior equilibria,
200-
using max_regret (internal consistency); and comparison to a set of previously
201-
computed equilibria using this function (regression test).
201+
using max_regret and agent_max_regret (internal consistency); and
202+
comparison to a set of previously computed equilibria using this function (regression test).
202203
203204
This set will be the full set of all computed equilibria if stop_after is None,
204205
else the first stop_after-many equilibria.
@@ -229,6 +230,7 @@ def are_the_same(game, found, candidate):
229230
return True
230231

231232
for eq in result.equilibria:
233+
assert abs(eq.max_regret()) <= TOL
232234
assert abs(eq.agent_max_regret()) <= TOL
233235
found = False
234236
for exp in mixed_behav_prof_data[:]:
@@ -423,12 +425,13 @@ def test_lcp_behavior_double():
423425
def test_lcp_behavior_rational(game: gbt.Game, mixed_behav_prof_data: list):
424426
"""Test calls of LCP for mixed behavior equilibria, rational precision.
425427
426-
using max_regret (internal consistency); and comparison to a previously
427-
computed equilibrium using this function (regression test)
428+
using max_regret and agent_max_regret (internal consistency); and
429+
comparison to a previously computed equilibrium using this function (regression test).
428430
"""
429431
result = gbt.nash.lcp_solve(game, use_strategic=False, rational=True)
430432
assert len(result.equilibria) == 1
431433
eq = result.equilibria[0]
434+
assert eq.max_regret() == 0
432435
assert eq.agent_max_regret() == 0
433436
expected = game.mixed_behavior_profile(rational=True, data=mixed_behav_prof_data)
434437
assert eq == expected
@@ -552,12 +555,13 @@ def test_lp_behavior_double():
552555
)
553556
def test_lp_behavior_rational(game: gbt.Game, mixed_behav_prof_data: list):
554557
"""Test calls of LP for mixed behavior equilibria, rational precision,
555-
using max_regret (internal consistency); and comparison to a previously
556-
computed equilibrium using this function (regression test)
558+
using max_regret and agent_max_regret (internal consistency); and
559+
comparison to a previously computed equilibrium using this function (regression test).
557560
"""
558561
result = gbt.nash.lp_solve(game, use_strategic=False, rational=True)
559562
assert len(result.equilibria) == 1
560563
eq = result.equilibria[0]
564+
assert eq.max_regret() == 0
561565
assert eq.agent_max_regret() == 0
562566
expected = game.mixed_behavior_profile(rational=True, data=mixed_behav_prof_data)
563567
assert eq == expected

0 commit comments

Comments
 (0)