From 9ee99f804cae8ce2ce3e1a6adad66d9d6c2c26f2 Mon Sep 17 00:00:00 2001 From: Rahul Savani Date: Fri, 5 Dec 2025 11:48:18 +0000 Subject: [PATCH 1/4] new xfail tests that capture the problem with enumpoly in issue 660 --- tests/games.py | 20 ++++++++++++++++---- tests/test_nash.py | 19 ++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/tests/games.py b/tests/games.py index c08e6497dd..62592639af 100644 --- a/tests/games.py +++ b/tests/games.py @@ -458,12 +458,19 @@ def kuhn_poker_lcp_first_mixed_strategy_prof(): return [alice, bob] -def create_one_shot_trust_efg() -> gbt.Game: +def create_one_shot_trust_efg(unique_NE_variant=False) -> gbt.Game: """ Returns ------- Game One-shot trust game, after Kreps (1990) + + The unique_NE_variant makes Trust a dominant strategy, replacing the + non-singleton equilibrium component from the standard version of the game + where the Buyer plays "Not Trust" and the seller can play any mixture < 0.5 probability + on Honor with a unique NE where the Buyer plays Trust and the Seller plays Abuse. + + This is not a standard variant but is useful for testing enumpoly_solve. """ g = gbt.Game.new_tree( players=["Buyer", "Seller"], title="One-shot trust game, after Kreps (1990)" @@ -473,9 +480,14 @@ def create_one_shot_trust_efg() -> gbt.Game: g.set_outcome( g.root.children[0].children[0], g.add_outcome([1, 1], label="Trustworthy") ) - g.set_outcome( - g.root.children[0].children[1], g.add_outcome([-1, 2], label="Untrustworthy") - ) + if unique_NE_variant: + g.set_outcome( + g.root.children[0].children[1], g.add_outcome(["1/2", 2], label="Untrustworthy") + ) + else: + g.set_outcome( + g.root.children[0].children[1], g.add_outcome([-1, 2], label="Untrustworthy") + ) g.set_outcome(g.root.children[1], g.add_outcome([0, 0], label="Opt-out")) return g diff --git a/tests/test_nash.py b/tests/test_nash.py index 375169db85..c3d9afda7d 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -95,11 +95,20 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): None, ), # 2-player non-zero-sum games - ( + pytest.param( games.create_one_shot_trust_efg(), - [[[[0, 1]], [["1/2", "1/2"]]], [[[0, 1]], [[0, 0]]]], - 2, - ), # Note all zero probs at iset + [[[[0, 1]], [["1/2", "1/2"]]], [[[0, 1]], [[0, 1]]]], + # second entry assumes we extend to Nash using only pure behaviors + # currently we get [[0, 1]], [[0, 0]]] as a second eq + None, + marks=pytest.mark.xfail(reason="Problem with enumpoly, as per issue #660") + ), + pytest.param( + games.create_one_shot_trust_efg(unique_NE_variant=True), + [[[[1, 0]], [[0, 1]]]], # currently we get [[0, 1]], [[0, 0]]] as a second eq + None, + marks=pytest.mark.xfail(reason="Problem with enumpoly, as per issue #660") + ), ( games.create_EFG_for_nxn_bimatrix_coordination_game(3), [ @@ -154,7 +163,7 @@ def test_enumpoly_ordered_behavior( result = gbt.nash.enumpoly_solve(game, use_strategic=False) assert len(result.equilibria) == len(mixed_behav_prof_data) for eq, exp in zip(result.equilibria, mixed_behav_prof_data): - assert abs(eq.max_regret()) <= TOL + # assert abs(eq.max_regret()) <= TOL expected = game.mixed_behavior_profile(rational=True, data=exp) for p in game.players: for i in p.infosets: From a6dd6abeb2fac8313633f1b2bd7a4dbf68ebd6c2 Mon Sep 17 00:00:00 2001 From: Rahul Savani Date: Fri, 5 Dec 2025 11:50:53 +0000 Subject: [PATCH 2/4] reinstate max_regret aspect of test_enumpoly_ordered_behavior --- tests/test_nash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_nash.py b/tests/test_nash.py index c3d9afda7d..388600157f 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -163,7 +163,7 @@ def test_enumpoly_ordered_behavior( result = gbt.nash.enumpoly_solve(game, use_strategic=False) assert len(result.equilibria) == len(mixed_behav_prof_data) for eq, exp in zip(result.equilibria, mixed_behav_prof_data): - # assert abs(eq.max_regret()) <= TOL + assert abs(eq.max_regret()) <= TOL expected = game.mixed_behavior_profile(rational=True, data=exp) for p in game.players: for i in p.infosets: From 0d92d7ff9f8ba9d8019319cbaa978ed19bf7bfaa Mon Sep 17 00:00:00 2001 From: Rahul Savani Date: Fri, 5 Dec 2025 11:52:20 +0000 Subject: [PATCH 3/4] typo in comments --- tests/games.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/games.py b/tests/games.py index 62592639af..784ce4a889 100644 --- a/tests/games.py +++ b/tests/games.py @@ -467,8 +467,9 @@ def create_one_shot_trust_efg(unique_NE_variant=False) -> gbt.Game: The unique_NE_variant makes Trust a dominant strategy, replacing the non-singleton equilibrium component from the standard version of the game - where the Buyer plays "Not Trust" and the seller can play any mixture < 0.5 probability - on Honor with a unique NE where the Buyer plays Trust and the Seller plays Abuse. + where the Buyer plays "Not Trust" and the seller can play any mixture with + < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and + the Seller plays Abuse. This is not a standard variant but is useful for testing enumpoly_solve. """ From e76fadfddfba22fb49aad02b079ca9a7cabe3189 Mon Sep 17 00:00:00 2001 From: Ted Turocy Date: Fri, 5 Dec 2025 15:29:40 +0000 Subject: [PATCH 4/4] Update games.py --- tests/games.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/tests/games.py b/tests/games.py index 784ce4a889..e34591d4d8 100644 --- a/tests/games.py +++ b/tests/games.py @@ -100,7 +100,7 @@ def create_coord_4x4_nfg(outcome_version: bool = False) -> gbt.Game: # Extensive-form games (efg) -def create_2x2_zero_sum_efg(missing_term_outcome=False) -> gbt.Game: +def create_2x2_zero_sum_efg(missing_term_outcome: bool = False) -> gbt.Game: """ EFG corresponding to 2x2 zero-sum game (I,-I). If missing_term_outcome, the terminal node after "T" then "r" does not have an outcome. @@ -123,7 +123,7 @@ def create_2x2_zero_sum_efg(missing_term_outcome=False) -> gbt.Game: return g -def create_matching_pennies_efg(with_neutral_outcome=False) -> gbt.Game: +def create_matching_pennies_efg(with_neutral_outcome: bool = False) -> gbt.Game: """ The version with_neutral_outcome adds a (0,0) payoff outcomes at a non-terminal node. """ @@ -156,7 +156,7 @@ def create_mixed_behav_game_efg() -> gbt.Game: return read_from_file("mixed_behavior_game.efg") -def create_stripped_down_poker_efg(nonterm_outcomes=False) -> gbt.Game: +def create_stripped_down_poker_efg(nonterm_outcomes: bool = False) -> gbt.Game: """ Returns ------- @@ -404,7 +404,7 @@ def get_path(node): return g -def create_kuhn_poker_efg(nonterm_outcomes=False) -> gbt.Game: +def create_kuhn_poker_efg(nonterm_outcomes: bool = False) -> gbt.Game: """ Returns ------- @@ -458,20 +458,15 @@ def kuhn_poker_lcp_first_mixed_strategy_prof(): return [alice, bob] -def create_one_shot_trust_efg(unique_NE_variant=False) -> gbt.Game: +def create_one_shot_trust_efg(unique_NE_variant: bool = False) -> gbt.Game: """ - Returns - ------- - Game - One-shot trust game, after Kreps (1990) - - The unique_NE_variant makes Trust a dominant strategy, replacing the - non-singleton equilibrium component from the standard version of the game - where the Buyer plays "Not Trust" and the seller can play any mixture with - < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and - the Seller plays Abuse. + One-shot trust game, after Kreps (1990) - This is not a standard variant but is useful for testing enumpoly_solve. + The unique_NE_variant makes Trust a dominant strategy, replacing the + non-singleton equilibrium component from the standard version of the game + where the Buyer plays "Not Trust" and the seller can play any mixture with + < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and + the Seller plays Abuse. """ g = gbt.Game.new_tree( players=["Buyer", "Seller"], title="One-shot trust game, after Kreps (1990)"