From e7c177784c1646043fda3accdb141538d42ffacd Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Tue, 16 Dec 2025 12:36:23 +0000 Subject: [PATCH 01/18] Added tests for enumpoly, LP and LCP --- tests/games.py | 236 ++++++++++++++++++++++++- tests/test_nash.py | 420 ++++++++++++++++++++++++++++++--------------- 2 files changed, 509 insertions(+), 147 deletions(-) diff --git a/tests/games.py b/tests/games.py index e34591d4d8..bbf093c2bc 100644 --- a/tests/games.py +++ b/tests/games.py @@ -19,7 +19,7 @@ def read_from_file(fn: str) -> gbt.Game: def create_efg_corresponding_to_bimatrix_game( - A: np.ndarray, B: np.ndarray, title: str + A: np.ndarray, B: np.ndarray, title: str ) -> gbt.Game: """ There is no direct pygambit method to create an EFG from a stategic-form game. @@ -123,6 +123,230 @@ def create_2x2_zero_sum_efg(missing_term_outcome: bool = False) -> gbt.Game: return g +def create_perfect_info_with_chance_efg() -> gbt.Game: + # Tests case in which sequence profile probabilities don't sum to 1 + g = gbt.Game.new_tree(players=["1", "2"], title="2 player perfect info with chance") + g.append_move(g.root, "1", ["a", "b"]) + g.append_move(g.root.children[0], g.players.chance, ["L", "R"]) + g.append_move(g.root.children[0].children[0], "2", ["A", "B"]) + g.append_move(g.root.children[0].children[1], "2", ["C", "D"]) + g.set_outcome( + g.root.children[0].children[0].children[0], g.add_outcome([-2, 2], label="aLA") + ) + g.set_outcome( + g.root.children[0].children[0].children[1], g.add_outcome([-2, 2], label="aLB") + ) + g.set_outcome( + g.root.children[0].children[1].children[0], g.add_outcome([-2, 2], label="aRC") + ) + g.set_outcome( + g.root.children[0].children[1].children[1], g.add_outcome([-2, 2], label="aRD") + ) + g.set_outcome(g.root.children[1], g.add_outcome([-1, 1], label="b")) + return g + + +def create_one_card_poker_lacking_outcome_efg() -> gbt.Game: + g = gbt.Game.new_tree(players=["Bob", "Alice"], + title="One card poker game, after Myerson (1991)") + g.append_move(g.root, g.players.chance, ["King", "Queen"]) + for node in g.root.children: + g.append_move(node, "Alice", ["Raise", "Fold"]) + g.append_move(g.root.children[0].children[0], "Bob", ["Meet", "Pass"]) + g.append_infoset(g.root.children[1].children[0], + g.root.children[0].children[0].infoset) + alice_winsbig = g.add_outcome([-1, 1], label="Alice wins big") + bob_winsbig = g.add_outcome([3, -3], label="Bob wins big") + bob_wins = g.add_outcome([2, -2], label="Bob wins") + g.set_outcome(g.root.children[0].children[0].children[0], alice_winsbig) + g.set_outcome(g.root.children[0].children[1], bob_wins) + g.set_outcome(g.root.children[1].children[0].children[0], bob_winsbig) + g.set_outcome(g.root.children[1].children[1], bob_wins) + return g + + +def create_perfect_info_internal_outcomes_efg() -> gbt.Game: + g = gbt.Game.new_tree(players=["1", "2"], title="2 player perfect info win lose") + g.append_move(g.root, "2", ["a", "b"]) + g.append_move(g.root.children[0], "1", ["L", "R"]) + g.append_move(g.root.children[1], "1", ["L", "R"]) + g.append_move(g.root.children[0].children[0], "2", ["l", "r"]) + g.set_outcome(g.root.children[0], g.add_outcome([-100, 50], label="a")) + g.set_outcome( + g.root.children[0].children[0].children[0], g.add_outcome([101, -51], label="aLl") + ) + g.set_outcome( + g.root.children[0].children[0].children[1], g.add_outcome([99, -49], label="aLr") + ) + g.set_outcome(g.root.children[0].children[1], g.add_outcome([101, -51], label="aR")) + g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) + g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) + return g + + +def create_three_action_internal_outcomes_efg() -> gbt.Game: + # Test 3 actions at infoset, internal outcomes and missing some outcomes at leaves + g = gbt.Game.new_tree(players=["1", "2"], + title="3 action, internal outcomes, lacking terminal outcomes") + g.append_move(g.root, g.players.chance, ["H", "L"]) + for i in range(2): + g.append_move(g.root.children[i], "1", ["A", "B", "C"]) + for i in range(3): + g.append_move(g.root.children[0].children[i], "2", ["X", "Y"]) + g.append_infoset(g.root.children[1].children[i], g.root.children[0].children[i].infoset) + o_1 = g.add_outcome([1, -1], label="1") + o_m1 = g.add_outcome([-1, 1], label="-1") + o_2 = g.add_outcome([2, -2], label="2") + o_m2 = g.add_outcome([-2, 2], label="-2") + g.set_outcome(g.root.children[0].children[0], o_1) + g.set_outcome(g.root.children[1].children[2], o_m1) + g.set_outcome(g.root.children[0].children[0].children[1], o_m2) + g.set_outcome(g.root.children[0].children[1].children[0], o_m1) + g.set_outcome(g.root.children[0].children[1].children[1], o_1) + g.set_outcome(g.root.children[0].children[2].children[0], o_1) + g.set_outcome(g.root.children[1].children[0].children[1], o_1) + g.set_outcome(g.root.children[1].children[1].children[0], o_1) + g.set_outcome(g.root.children[1].children[1].children[1], o_m1) + g.set_outcome(g.root.children[1].children[2].children[1], o_2) + return g + + +def create_entry_accomodation_efg() -> gbt.Game: + g = gbt.Game.new_tree(players=["1", "2"], + title="Entry-accomodation game with internal outcomes") + g.append_move(g.root, "1", ["S", "T"]) + g.append_move(g.root.children[0], "2", ["E", "O"]) + g.append_infoset(g.root.children[1], g.root.children[0].infoset) + g.append_move(g.root.children[0].children[0], "1", ["A", "F"]) + g.append_move(g.root.children[1].children[0], "1", ["A", "F"]) + g.set_outcome(g.root.children[0], g.add_outcome([3, 2])) + g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([-3, -1])) + g.set_outcome(g.root.children[0].children[1], g.add_outcome([-2, 1])) + g.set_outcome(g.root.children[1].children[0].children[0], g.add_outcome([2, 3])) + g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([1, 0])) + g.set_outcome(g.root.children[1].children[1], g.add_outcome([3, 1])) + return g + + +def create_non_zero_sum_lacking_outcome_efg() -> gbt.Game: + g = gbt.Game.new_tree(players=["1", "2"], title="Non constant-sum game lacking outcome") + g.append_move(g.root, g.players.chance, ["H", "T"]) + g.set_chance_probs(g.root.infoset, ["1/2", "1/2"]) + g.append_move(g.root.children[0], "1", ["A", "B"]) + g.append_infoset(g.root.children[1], g.root.children[0].infoset) + g.append_move(g.root.children[0].children[0], "2", ["X", "Y"]) + g.append_infoset(g.root.children[0].children[1], g.root.children[0].children[0].infoset) + g.append_infoset(g.root.children[1].children[0], g.root.children[0].children[0].infoset) + g.append_infoset(g.root.children[1].children[1], g.root.children[0].children[0].infoset) + g.set_outcome(g.root.children[0].children[0].children[0], g.add_outcome([2, 1])) + g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([-1, 2])) + g.set_outcome(g.root.children[0].children[1].children[0], g.add_outcome([1, -1])) + g.set_outcome(g.root.children[1].children[0].children[0], g.add_outcome([1, 0])) + g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([0, 1])) + g.set_outcome(g.root.children[1].children[1].children[0], g.add_outcome([-1, 1])) + g.set_outcome(g.root.children[1].children[1].children[1], g.add_outcome([2, -1])) + return g + + +def create_chance_in_middle_efg() -> gbt.Game: + g = gbt.Game.new_tree(players=["1", "2"], + title="Chance in middle game") + g.append_move(g.root, "1", ["A", "B"]) + g.append_move(g.root.children[0], g.players.chance, ["H", "L"]) + g.set_chance_probs(g.root.children[0].infoset, ["1/5", "4/5"]) + g.append_move(g.root.children[1], g.players.chance, ["H", "L"]) + g.set_chance_probs(g.root.children[1].infoset, ["7/10", "3/10"]) + g.set_outcome(g.root.children[0].children[0], g.add_outcome([-1, 1], label="a")) + for i in range(2): + g.append_move(g.root.children[0].children[i], "2", ["X", "Y"]) + ist = g.root.children[0].children[i].infoset + g.append_infoset(g.root.children[1].children[i], ist) + for i in range(2): + for j in range(2): + g.append_move(g.root.children[i].children[0].children[j], "1", ["C", "D"]) + ist = g.root.children[i].children[0].children[j].infoset + g.append_infoset(g.root.children[i].children[1].children[j], ist) + o_1 = g.add_outcome([1, -1], label="1") + o_m1 = g.add_outcome([-1, 1], label="-1") + o_h = g.add_outcome(["1/2", "-1/2"], label="0.5") + o_mh = g.add_outcome(["-1/2", "1/2"], label="-0.5") + g.set_outcome(g.root.children[0].children[0].children[0].children[0], o_1) + g.set_outcome(g.root.children[0].children[0].children[0].children[1], o_m1) + g.set_outcome(g.root.children[0].children[0].children[1].children[0], o_h) + g.set_outcome(g.root.children[0].children[0].children[1].children[1], o_mh) + g.set_outcome(g.root.children[0].children[1].children[0].children[0], o_h) + g.set_outcome(g.root.children[0].children[1].children[0].children[1], o_mh) + g.set_outcome(g.root.children[0].children[1].children[1].children[0], o_1) + g.set_outcome(g.root.children[0].children[1].children[1].children[1], o_m1) + g.set_outcome(g.root.children[1].children[0].children[0].children[0], o_h) + g.set_outcome(g.root.children[1].children[0].children[0].children[1], o_mh) + g.set_outcome(g.root.children[1].children[0].children[1].children[0], o_1) + g.set_outcome(g.root.children[1].children[0].children[1].children[1], o_m1) + g.set_outcome(g.root.children[1].children[1].children[0].children[0], o_1) + g.set_outcome(g.root.children[1].children[1].children[0].children[1], o_m1) + g.set_outcome(g.root.children[1].children[1].children[1].children[0], o_h) + g.set_outcome(g.root.children[1].children[1].children[1].children[1], o_mh) + return g + + +def create_large_payoff_game_efg() -> gbt.Game: + g = gbt.Game.new_tree(players=["1", "2"], title="Large payoff game") + g.append_move(g.root, g.players.chance, ["L", "R"]) + for i in range(2): + g.append_move(g.root.children[i], "1", ["A", "B"]) + for i in range(2): + g.append_move(g.root.children[0].children[i], "2", ["X", "Y"]) + g.append_infoset(g.root.children[1].children[i], g.root.children[0].children[i].infoset) + o_large = g.add_outcome([10000000000000000000, -10000000000000000000], label="large payoff") + o_1 = g.add_outcome([1, -1], label="1") + o_m1 = g.add_outcome([-1, 1], label="-1") + o_zero = g.add_outcome([0, 0], label="0") + g.set_outcome(g.root.children[0].children[0].children[0], o_large) + g.set_outcome(g.root.children[0].children[0].children[1], o_1) + g.set_outcome(g.root.children[0].children[1].children[0], o_m1) + g.set_outcome(g.root.children[0].children[1].children[1], o_zero) + g.set_outcome(g.root.children[1].children[0].children[0], o_m1) + g.set_outcome(g.root.children[1].children[0].children[1], o_1) + g.set_outcome(g.root.children[1].children[1].children[0], o_zero) + g.set_outcome(g.root.children[1].children[1].children[1], o_large) + return g + + +def create_3_player_with_internal_outcomes_efg() -> gbt.Game: + g = gbt.Game.new_tree(players=["1", "2", "3"], title="3 player game with internal outcomes") + g.append_move(g.root, g.players.chance, ["H", "T"]) + g.set_chance_probs(g.root.infoset, ["1/2", "1/2"]) + g.append_move(g.root.children[0], "1", ["a", "b"]) + g.append_move(g.root.children[1], "1", ["c", "d"]) + g.append_move(g.root.children[0].children[0], "2", ["A", "B"]) + g.append_infoset(g.root.children[1].children[0], g.root.children[0].children[0].infoset) + g.append_move(g.root.children[0].children[1], "3", ["W", "X"]) + g.append_infoset(g.root.children[1].children[1], g.root.children[0].children[1].infoset) + g.append_move(g.root.children[0].children[0].children[0], "3", ["Y", "Z"]) + iset = g.root.children[0].children[0].children[0].infoset + g.append_infoset(g.root.children[0].children[0].children[1], iset) + g.append_move(g.root.children[0].children[1].children[1], "2", ["C", "D"]) + o = g.add_outcome([1, 2, 3]) + g.set_outcome(g.root.children[1], o) + o = g.add_outcome([3, 1, 4]) + g.set_outcome(g.root.children[0].children[0].children[0].children[0], o) + o = g.add_outcome([4, 0, 1]) + g.set_outcome(g.root.children[0].children[0].children[0].children[1], o) + o = g.add_outcome([1, 0, 1]) + g.set_outcome(g.root.children[1].children[0].children[0], o) + o = g.add_outcome([2, -1, -2]) + g.set_outcome(g.root.children[1].children[0].children[1], o) + o = g.add_outcome([1, 3, 2]) + g.set_outcome(g.root.children[0].children[1].children[0], o) + o = g.add_outcome([2, 4, 1]) + g.set_outcome(g.root.children[0].children[1].children[1].children[0], o) + o = g.add_outcome([4, 1, 3]) + g.set_outcome(g.root.children[0].children[1].children[1].children[1], o) + o = g.add_outcome([-1, 2, -1]) + g.set_outcome(g.root.children[1].children[1].children[0], o) + return g + + 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. @@ -990,14 +1214,14 @@ def _redu_strats(self, player, level): first_half = tmp[:n_half] second_half = tmp[n_half:] n_stars = ( - self.get_n_infosets(level)[1] - self.get_n_infosets(level - 1)[1] - 1 + self.get_n_infosets(level)[1] - self.get_n_infosets(level - 1)[1] - 1 ) stars = "*" * n_stars return ( - ["11" + t + stars for t in first_half] - + ["12" + t + stars for t in second_half] - + ["21" + stars + t for t in first_half] - + ["22" + stars + t for t in second_half] + ["11" + t + stars for t in first_half] + + ["12" + t + stars for t in second_half] + + ["21" + stars + t for t in first_half] + + ["22" + stars + t for t in second_half] ) diff --git a/tests/test_nash.py b/tests/test_nash.py index b436b79a96..8c20ab42f9 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -48,23 +48,23 @@ def test_enummixed_double(): (games.create_one_shot_trust_efg(), [[[0, 1], ["1/2", "1/2"]], [[0, 1], [0, 1]]]), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(3), - [ - [[1, 0, 0], [1, 0, 0]], - [["1/2", "1/2", 0], ["1/2", "1/2", 0]], - [["1/3", "1/3", "1/3"], ["1/3", "1/3", "1/3"]], - [["1/2", 0, "1/2"], ["1/2", 0, "1/2"]], - [[0, 1, 0], [0, 1, 0]], - [[0, "1/2", "1/2"], [0, "1/2", "1/2"]], - [[0, 0, 1], [0, 0, 1]], - ], + games.create_EFG_for_nxn_bimatrix_coordination_game(3), + [ + [[1, 0, 0], [1, 0, 0]], + [["1/2", "1/2", 0], ["1/2", "1/2", 0]], + [["1/3", "1/3", "1/3"], ["1/3", "1/3", "1/3"]], + [["1/2", 0, "1/2"], ["1/2", 0, "1/2"]], + [[0, 1, 0], [0, 1, 0]], + [[0, "1/2", "1/2"], [0, "1/2", "1/2"]], + [[0, 0, 1], [0, 0, 1]], + ], ), ( - games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), - [ - [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"], - ["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], - ], + games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), + [ + [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"], + ["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], + ], ), ] ) @@ -89,9 +89,9 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): [ # 2-player zero-sum games ( - games.create_stripped_down_poker_efg(), - [[[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]]], - None, + games.create_stripped_down_poker_efg(), + [[[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]]], + None, ), # 2-player non-zero-sum games pytest.param( @@ -109,22 +109,22 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): marks=pytest.mark.xfail(reason="Problem with enumpoly, as per issue #660") ), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(3), - [ - [[["1/3", "1/3", "1/3"]], [["1/3", "1/3", "1/3"]]], - [[["1/2", "1/2", 0]], [["1/2", "1/2", 0]]], - [[["1/2", 0, "1/2"]], [["1/2", 0, "1/2"]]], - [[[1, 0, 0]], [[1, 0, 0]]], - [[[0, "1/2", "1/2"]], [[0, "1/2", "1/2"]]], - [[[0, 1, 0]], [[0, 1, 0]]], - [[[0, 0, 1]], [[0, 0, 1]]], - ], - None, + games.create_EFG_for_nxn_bimatrix_coordination_game(3), + [ + [[["1/3", "1/3", "1/3"]], [["1/3", "1/3", "1/3"]]], + [[["1/2", "1/2", 0]], [["1/2", "1/2", 0]]], + [[["1/2", 0, "1/2"]], [["1/2", 0, "1/2"]]], + [[[1, 0, 0]], [[1, 0, 0]]], + [[[0, "1/2", "1/2"]], [[0, "1/2", "1/2"]]], + [[[0, 1, 0]], [[0, 1, 0]]], + [[[0, 0, 1]], [[0, 0, 1]]], + ], + None, ), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(4), - [[[["1/4", "1/4", "1/4", "1/4"]], [["1/4", "1/4", "1/4", "1/4"]]]], - 1, + games.create_EFG_for_nxn_bimatrix_coordination_game(4), + [[[["1/4", "1/4", "1/4", "1/4"]], [["1/4", "1/4", "1/4", "1/4"]]]], + 1, ), # 3-player game # ( @@ -135,10 +135,62 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): # ], # 2, # 9 in total found by enumpoly (see unordered test) # ), + ( + games.create_3_player_with_internal_outcomes_efg(), + [ + [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.5, 0.5]], [[0.0, 1.0], [1.0, 0.0]]], + [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 1.0]], + [[0.3333333333333333, 0.6666666666666667], [1.0, 0.0]]]], + 2, + ), + ( + games.create_entry_accomodation_efg(), + [ + [[[0.6666666666666666, 0.33333333333333337], [1.0, 0.0], [1.0, 0.0]], + [[0.6666666666666666, 0.33333333333333337]]], + [[[0.0, 1.0], [0.0, 0.0], [0.3333333333333333, 0.6666666666666667]], + [[0.0, 1.0]]], + [[[0.0, 1.0], [0.0, 0.0], [1.0, 0.0]], [[1.0, 0.0]]], + [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0]], [[0.0, 1.0]]]], + 4, + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(), + [[[[0.33333333333333337, 0.6666666666666666]], [[0.5, 0.5]]]], + 1, + ), + pytest.param( + games.create_large_payoff_game_efg(), + [[[[1.0, 0.0], [1.0, 0.0]], [[0.0, 1.0], [1.0, 0.0]]], + [[[1.0, 0.0], [1.0, 0.0]], [[0.0, 1.0], [0.0, 0.0]]], + [[[1.0, 0.0], [0.0, 1.0]], [[0.0, 1.0], [1.0, 0.0]]], + [[[0.0, 1.0], [1.0, 0.0]], [[0.0, 1.0], [1.0, 0.0]]], + [[[0.0, 1.0], [1.0, 0.0]], [[0.0, 1.0], [1.0, 0.0]]], + [[[0.0, 1.0], [0.0, 1.0]], [[0.0, 0.0], [1.0, 0.0]]]], + 6, + marks=pytest.mark.xfail(reason="True equilibrium has tiny probabilities") + ), + ( + games.create_chance_in_middle_efg(), + [[[[0.27272727272727276, 0.7272727272727273], + [1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [1.0, 0.0]], + [[1.0, 0.0], [0.5454545454545455, 0.4545454545454545]]], + [[[1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [0.0, 0.0], [0.0, 0.0]], + [[0.0, 1.0], [1.0, 0.0]]], + [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0], [1.0, 0.0], [1.0, 0.0]], + [[1.0, 0.0], [0.0, 1.0]]]], + 3, + ), + ( + games.create_one_card_poker_lacking_outcome_efg(), + [[[[0.6666666666666666, 0.33333333333333337]], + [[1.0, 0.0], [0.3333333333333333, 0.6666666666666667]]]], + 1, + ), ], ) def test_enumpoly_ordered_behavior( - game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int + game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int ): """Test calls of enumpoly for mixed behavior equilibria, using max_regret (internal consistency); and comparison to a set of previously @@ -177,24 +229,24 @@ def test_enumpoly_ordered_behavior( [ # 3-player game ( - games.create_mixed_behav_game_efg(), - [ - [[["2/5", "3/5"]], [["1/2", "1/2"]], [["1/3", "2/3"]]], - [[["1/2", "1/2"]], [["2/5", "3/5"]], [["1/4", "3/4"]]], - [[["1/2", "1/2"]], [["1/2", "1/2"]], [[1, 0]]], - [[["1/3", "2/3"]], [[1, 0]], [["1/4", "3/4"]]], - [[[1, 0]], [[1, 0]], [[1, 0]]], - [[[1, 0]], [[0, 1]], [[0, 1]]], - [[[0, 1]], [["1/4", "3/4"]], [["1/3", "2/3"]]], - [[[0, 1]], [[1, 0]], [[0, 1]]], - [[[0, 1]], [[0, 1]], [[1, 0]]], - ], - 9, + games.create_mixed_behav_game_efg(), + [ + [[["2/5", "3/5"]], [["1/2", "1/2"]], [["1/3", "2/3"]]], + [[["1/2", "1/2"]], [["2/5", "3/5"]], [["1/4", "3/4"]]], + [[["1/2", "1/2"]], [["1/2", "1/2"]], [[1, 0]]], + [[["1/3", "2/3"]], [[1, 0]], [["1/4", "3/4"]]], + [[[1, 0]], [[1, 0]], [[1, 0]]], + [[[1, 0]], [[0, 1]], [[0, 1]]], + [[[0, 1]], [["1/4", "3/4"]], [["1/3", "2/3"]]], + [[[0, 1]], [[1, 0]], [[0, 1]]], + [[[0, 1]], [[0, 1]], [[1, 0]]], + ], + 9, ), ], ) def test_enumpoly_unordered_behavior( - game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int + game: gbt.Game, mixed_behav_prof_data: list, stop_after: None | int ): """Test calls of enumpoly for mixed behavior equilibria, using max_regret (internal consistency); and comparison to a set of previously @@ -255,54 +307,54 @@ def test_lcp_strategy_double(): [ # Zero-sum games ( - games.create_2x2_zero_sum_efg(), - [[["1/2", "1/2"], ["1/2", "1/2"]]], - None + games.create_2x2_zero_sum_efg(), + [[["1/2", "1/2"], ["1/2", "1/2"]]], + None ), ( - games.create_2x2_zero_sum_efg(missing_term_outcome=True), - [[["1/2", "1/2"], ["1/2", "1/2"]]], - None + games.create_2x2_zero_sum_efg(missing_term_outcome=True), + [[["1/2", "1/2"], ["1/2", "1/2"]]], + None ), (games.create_stripped_down_poker_efg(), [[["1/3", "2/3", 0, 0], ["2/3", "1/3"]]], None), ( - games.create_stripped_down_poker_efg(nonterm_outcomes=True), - [[["1/3", "2/3", 0, 0], ["2/3", "1/3"]]], - None + games.create_stripped_down_poker_efg(nonterm_outcomes=True), + [[["1/3", "2/3", 0, 0], ["2/3", "1/3"]]], + None ), (games.create_kuhn_poker_efg(), [games.kuhn_poker_lcp_first_mixed_strategy_prof()], 1), ( - games.create_kuhn_poker_efg(nonterm_outcomes=True), - [games.kuhn_poker_lcp_first_mixed_strategy_prof()], - 1 + games.create_kuhn_poker_efg(nonterm_outcomes=True), + [games.kuhn_poker_lcp_first_mixed_strategy_prof()], + 1 ), # Non-zero-sum games (games.create_one_shot_trust_efg(), [[[0, 1], ["1/2", "1/2"]]], None), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(3), - [ - [[1, 0, 0], [1, 0, 0]], - [["1/2", "1/2", 0], ["1/2", "1/2", 0]], - [[0, 1, 0], [0, 1, 0]], - [[0, "1/2", "1/2"], [0, "1/2", "1/2"]], - [["1/3", "1/3", "1/3"], ["1/3", "1/3", "1/3"]], - [["1/2", 0, "1/2"], ["1/2", 0, "1/2"]], - [[0, 0, 1], [0, 0, 1]], - ], - None, + games.create_EFG_for_nxn_bimatrix_coordination_game(3), + [ + [[1, 0, 0], [1, 0, 0]], + [["1/2", "1/2", 0], ["1/2", "1/2", 0]], + [[0, 1, 0], [0, 1, 0]], + [[0, "1/2", "1/2"], [0, "1/2", "1/2"]], + [["1/3", "1/3", "1/3"], ["1/3", "1/3", "1/3"]], + [["1/2", 0, "1/2"], ["1/2", 0, "1/2"]], + [[0, 0, 1], [0, 0, 1]], + ], + None, ), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(4), - [[[1, 0, 0, 0], [1, 0, 0, 0]]], - 1, + games.create_EFG_for_nxn_bimatrix_coordination_game(4), + [[[1, 0, 0, 0], [1, 0, 0, 0]]], + 1, ), ( - games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), - [ - [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"], - ["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], - ], - None + games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), + [ + [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"], + ["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], + ], + None ), ] ) @@ -345,40 +397,40 @@ def test_lcp_behavior_double(): [ # Zero-sum games (also tested with lp solve) ( - games.create_2x2_zero_sum_efg(), - [[["1/2", "1/2"]], [["1/2", "1/2"]]] + games.create_2x2_zero_sum_efg(), + [[["1/2", "1/2"]], [["1/2", "1/2"]]] ), pytest.param( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with missing terminal outcomes") ), (games.create_matching_pennies_efg(), - [[["1/2", "1/2"]], [["1/2", "1/2"]]]), + [[["1/2", "1/2"]], [["1/2", "1/2"]]]), pytest.param( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with nonterminal nodes in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with missing outcomes") ), (games.create_stripped_down_poker_efg(), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]]), pytest.param( games.create_stripped_down_poker_efg(nonterm_outcomes=True), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), ( - games.create_kuhn_poker_efg(), - [ + games.create_kuhn_poker_efg(), [ - ["2/3", "1/3"], - [1, 0], - [1, 0], - ["1/3", "2/3"], - [0, 1], - ["1/2", "1/2"], + [ + ["2/3", "1/3"], + [1, 0], + [1, 0], + ["1/3", "2/3"], + [0, 1], + ["1/2", "1/2"], + ], + [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], - [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], - ], ), pytest.param( games.create_kuhn_poker_efg(nonterm_outcomes=True), @@ -393,30 +445,78 @@ def test_lcp_behavior_double(): ], [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), # In the next test case: # 1/2-1/2 for l/r is determined by MixedBehaviorProfile.UndefinedToCentroid() ( - games.create_two_player_perfect_info_win_lose_efg(), - [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], + games.create_two_player_perfect_info_win_lose_efg(), + [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], + ), + ( + games.create_perfect_info_with_chance_efg(), + [[[0, 1]], [[0, 1], [0, 1]]], + ), + pytest.param( + games.create_one_card_poker_lacking_outcome_efg(), + [[["2/3", "1/3"]], [[1, 0], ["1/3", "2/3"]]], + marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") + ), + pytest.param( + games.create_perfect_info_internal_outcomes_efg(), + [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") + ), + pytest.param( + games.create_three_action_internal_outcomes_efg(), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], + ], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") + ), + ( + games.create_large_payoff_game_efg(), + [ + [[1, 0], [1, 0]], + [[0, 1], ["9999999999999999999/10000000000000000000", + "1/10000000000000000000"]], + ], + ), + pytest.param( + games.create_chance_in_middle_efg(), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), # Non-zero-sum games ( - games.create_reduction_both_players_payoff_ties_efg(), - [[[0, 0, 1, 0], [1, 0]], [[0, 1], [0, 1], [0, 1], [0, 1]]], + games.create_reduction_both_players_payoff_ties_efg(), + [[[0, 0, 1, 0], [1, 0]], [[0, 1], [0, 1], [0, 1], [0, 1]]], ), ( - games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), - [ - [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"]], - [["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], - ], + games.create_EFG_for_6x6_bimatrix_with_long_LH_paths_and_unique_eq(), + [ + [["1/30", "1/6", "3/10", "3/10", "1/6", "1/30"]], + [["1/6", "1/30", "3/10", "3/10", "1/30", "1/6"]], + ], ), (games.create_EFG_for_nxn_bimatrix_coordination_game(3), [[[0, 0, 1]], [[0, 0, 1]]]), ( - games.create_EFG_for_nxn_bimatrix_coordination_game(4), - [[[0, 0, 0, 1]], [[0, 0, 0, 1]]], + games.create_EFG_for_nxn_bimatrix_coordination_game(4), + [[[0, 0, 0, 1]], [[0, 0, 0, 1]]], + ), + pytest.param( + games.create_entry_accomodation_efg(), + [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") + ), + pytest.param( + games.create_non_zero_sum_lacking_outcome_efg(), + [[["1/3", "2/3"]], [["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") ), ], ) @@ -448,22 +548,22 @@ def test_lp_strategy_double(): "game,mixed_strategy_prof_data", [ ( - games.create_2x2_zero_sum_efg(), - [["1/2", "1/2"], ["1/2", "1/2"]], + games.create_2x2_zero_sum_efg(), + [["1/2", "1/2"], ["1/2", "1/2"]], ), ( - games.create_2x2_zero_sum_efg(missing_term_outcome=True), - [["1/2", "1/2"], ["1/2", "1/2"]], + games.create_2x2_zero_sum_efg(missing_term_outcome=True), + [["1/2", "1/2"], ["1/2", "1/2"]], ), (games.create_stripped_down_poker_efg(), [["1/3", "2/3", 0, 0], ["2/3", "1/3"]]), ( - games.create_stripped_down_poker_efg(nonterm_outcomes=True), - [["1/3", "2/3", 0, 0], ["2/3", "1/3"]] + games.create_stripped_down_poker_efg(nonterm_outcomes=True), + [["1/3", "2/3", 0, 0], ["2/3", "1/3"]] ), (games.create_kuhn_poker_efg(), games.kuhn_poker_lp_mixed_strategy_prof()), ( - games.create_kuhn_poker_efg(nonterm_outcomes=True), - games.kuhn_poker_lp_mixed_strategy_prof() + games.create_kuhn_poker_efg(nonterm_outcomes=True), + games.kuhn_poker_lp_mixed_strategy_prof() ), ], ) @@ -491,62 +591,100 @@ def test_lp_behavior_double(): "game,mixed_behav_prof_data", [ ( - games.create_two_player_perfect_info_win_lose_efg(), - [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], + games.create_two_player_perfect_info_win_lose_efg(), + [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], ), ( - games.create_2x2_zero_sum_efg(missing_term_outcome=False), - [[["1/2", "1/2"]], [["1/2", "1/2"]]] + games.create_2x2_zero_sum_efg(missing_term_outcome=False), + [[["1/2", "1/2"]], [["1/2", "1/2"]]] ), pytest.param( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") ), (games.create_matching_pennies_efg(with_neutral_outcome=False), - [[["1/2", "1/2"]], [["1/2", "1/2"]]]), + [[["1/2", "1/2"]], [["1/2", "1/2"]]]), pytest.param( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with nonterminal nodes in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with outcomes") ), ( - games.create_stripped_down_poker_efg(), - [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], + games.create_stripped_down_poker_efg(), + [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], ), pytest.param( games.create_stripped_down_poker_efg(nonterm_outcomes=True), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], - marks=pytest.mark.xfail(reason="Problem with nonterminal nodes in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-terminal outcomes") ), ( - games.create_kuhn_poker_efg(), - [ - [[1, 0], [1, 0], [1, 0], ["2/3", "1/3"], [1, 0], [0, 1]], - [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], - ], + games.create_kuhn_poker_efg(), + [ + [[1, 0], [1, 0], [1, 0], ["2/3", "1/3"], [1, 0], [0, 1]], + [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], + ], ), pytest.param( games.create_kuhn_poker_efg(nonterm_outcomes=True), [ [ - ["2/3", "1/3"], [1, 0], [1, 0], - ["1/3", "2/3"], + [1, 0], + ["2/3", "1/3"], + [1, 0], [0, 1], - ["1/2", "1/2"], ], [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], - marks=pytest.mark.xfail(reason="Problem with nonterminal nodes in LP/LCP") + marks=pytest.mark.xfail(reason="Problem with non-terminal outcomes") ), ( - games.create_seq_form_STOC_paper_zero_sum_2_player_efg(), - [ - [[0, 1], ["1/3", "2/3"], ["2/3", "1/3"]], - [["5/6", "1/6"], ["5/9", "4/9"]], - ], + games.create_seq_form_STOC_paper_zero_sum_2_player_efg(), + [ + [[0, 1], ["1/3", "2/3"], ["2/3", "1/3"]], + [["5/6", "1/6"], ["5/9", "4/9"]], + ], + ), + ( + games.create_perfect_info_with_chance_efg(), + [[[0, 1]], [[1, 0], [1, 0]]], + ), + pytest.param( + games.create_one_card_poker_lacking_outcome_efg(), + [[["2/3", "1/3"]], [[1, 0], ["1/3", "2/3"]]], + marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") + ), + pytest.param( + games.create_perfect_info_internal_outcomes_efg(), + [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") + ), + pytest.param( + games.create_three_action_internal_outcomes_efg(), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], + ], + marks=pytest.mark.xfail(reason="Problem with internal and missing outcomes") + ), + ( + games.create_large_payoff_game_efg(), + [ + [[1, 0], [1, 0]], + [[0, 1], ["9999999999999999999/10000000000000000000", + "1/10000000000000000000"]], + ], + ), + pytest.param( + games.create_chance_in_middle_efg(), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), ], ) @@ -637,7 +775,7 @@ def test_logit_solve_branch_error_with_invalid_max_accel(): def test_logit_solve_branch(): game = games.read_from_file("const_sum_game.nfg") assert len(gbt.qre.logit_solve_branch( - game=game, maxregret=0.2, first_step=0.2, max_accel=1)) > 0 + game=game, maxregret=0.2, first_step=0.2, max_accel=1)) > 0 def test_logit_solve_lambda_error_with_invalid_first_step(): @@ -659,7 +797,7 @@ def test_logit_solve_lambda_error_with_invalid_max_accel(): def test_logit_solve_lambda(): game = games.read_from_file("const_sum_game.nfg") assert len(gbt.qre.logit_solve_lambda( - game=game, lam=[1, 2, 3], first_step=0.2, max_accel=1)) > 0 + game=game, lam=[1, 2, 3], first_step=0.2, max_accel=1)) > 0 def test_kuhn(): From 5d6ac355b05823b4282eca767b3cd0fd141f9854 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 10:48:32 +0000 Subject: [PATCH 02/18] now both versions of chance_in_middle --- tests/games.py | 21 ++++++++++++++------ tests/test_nash.py | 49 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/tests/games.py b/tests/games.py index bbf093c2bc..2a2c3d6a56 100644 --- a/tests/games.py +++ b/tests/games.py @@ -248,7 +248,7 @@ def create_non_zero_sum_lacking_outcome_efg() -> gbt.Game: return g -def create_chance_in_middle_efg() -> gbt.Game: +def create_chance_in_middle_efg(nonterm_outcomes: bool = False) -> gbt.Game: g = gbt.Game.new_tree(players=["1", "2"], title="Chance in middle game") g.append_move(g.root, "1", ["A", "B"]) @@ -256,7 +256,6 @@ def create_chance_in_middle_efg() -> gbt.Game: g.set_chance_probs(g.root.children[0].infoset, ["1/5", "4/5"]) g.append_move(g.root.children[1], g.players.chance, ["H", "L"]) g.set_chance_probs(g.root.children[1].infoset, ["7/10", "3/10"]) - g.set_outcome(g.root.children[0].children[0], g.add_outcome([-1, 1], label="a")) for i in range(2): g.append_move(g.root.children[0].children[i], "2", ["X", "Y"]) ist = g.root.children[0].children[i].infoset @@ -268,12 +267,22 @@ def create_chance_in_middle_efg() -> gbt.Game: g.append_infoset(g.root.children[i].children[1].children[j], ist) o_1 = g.add_outcome([1, -1], label="1") o_m1 = g.add_outcome([-1, 1], label="-1") + o_m2 = g.add_outcome([-2, 2], label="-2") o_h = g.add_outcome(["1/2", "-1/2"], label="0.5") o_mh = g.add_outcome(["-1/2", "1/2"], label="-0.5") - g.set_outcome(g.root.children[0].children[0].children[0].children[0], o_1) - g.set_outcome(g.root.children[0].children[0].children[0].children[1], o_m1) - g.set_outcome(g.root.children[0].children[0].children[1].children[0], o_h) - g.set_outcome(g.root.children[0].children[0].children[1].children[1], o_mh) + o_z = g.add_outcome([0, 0], label="0") + o_m3o2 = g.add_outcome(["-3/2", "3/2"], label="-1.5") + if nonterm_outcomes: + g.set_outcome(g.root.children[0].children[0], g.add_outcome([-1, 1], label="a")) + g.set_outcome(g.root.children[0].children[0].children[0].children[0], o_1) + g.set_outcome(g.root.children[0].children[0].children[0].children[1], o_m1) + g.set_outcome(g.root.children[0].children[0].children[1].children[0], o_h) + g.set_outcome(g.root.children[0].children[0].children[1].children[1], o_mh) + else: + g.set_outcome(g.root.children[0].children[0].children[0].children[0], o_z) + g.set_outcome(g.root.children[0].children[0].children[0].children[1], o_m2) + g.set_outcome(g.root.children[0].children[0].children[1].children[0], o_mh) + g.set_outcome(g.root.children[0].children[0].children[1].children[1], o_m3o2) g.set_outcome(g.root.children[0].children[1].children[0].children[0], o_h) g.set_outcome(g.root.children[0].children[1].children[0].children[1], o_mh) g.set_outcome(g.root.children[0].children[1].children[1].children[0], o_1) diff --git a/tests/test_nash.py b/tests/test_nash.py index 8c20ab42f9..7e68419d09 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -181,6 +181,17 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): [[1.0, 0.0], [0.0, 1.0]]]], 3, ), + ( + games.create_chance_in_middle_efg(nonterm_outcomes=True), + [[[[0.27272727272727276, 0.7272727272727273], + [1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [1.0, 0.0]], + [[1.0, 0.0], [0.5454545454545455, 0.4545454545454545]]], + [[[1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [0.0, 0.0], [0.0, 0.0]], + [[0.0, 1.0], [1.0, 0.0]]], + [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0], [1.0, 0.0], [1.0, 0.0]], + [[1.0, 0.0], [0.0, 1.0]]]], + 3, + ), ( games.create_one_card_poker_lacking_outcome_efg(), [[[[0.6666666666666666, 0.33333333333333337]], @@ -483,13 +494,20 @@ def test_lcp_behavior_double(): "1/10000000000000000000"]], ], ), + ( + games.create_chance_in_middle_efg(), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ] + ), pytest.param( - games.create_chance_in_middle_efg(), - [ - [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], - [[1, 0], ["6/11", "5/11"]] - ], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") + games.create_chance_in_middle_efg(nonterm_outcomes=True), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), # Non-zero-sum games ( @@ -678,13 +696,20 @@ def test_lp_behavior_double(): "1/10000000000000000000"]], ], ), + ( + games.create_chance_in_middle_efg(), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ], + ), pytest.param( - games.create_chance_in_middle_efg(), - [ - [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], - [[1, 0], ["6/11", "5/11"]] - ], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") + games.create_chance_in_middle_efg(nonterm_outcomes=True), + [ + [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]] + ], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), ], ) From 7ad7718fb13c91262418fa46e643c01d6f17d721 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 11:03:22 +0000 Subject: [PATCH 03/18] now both versions of non_zero_sum_with_lacking_outcome --- tests/games.py | 4 +++- tests/test_nash.py | 27 ++++++++++++++++++--------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/tests/games.py b/tests/games.py index 2a2c3d6a56..20f28a0967 100644 --- a/tests/games.py +++ b/tests/games.py @@ -228,7 +228,7 @@ def create_entry_accomodation_efg() -> gbt.Game: return g -def create_non_zero_sum_lacking_outcome_efg() -> gbt.Game: +def create_non_zero_sum_lacking_outcome_efg(missing_term_outcome: bool = False) -> gbt.Game: g = gbt.Game.new_tree(players=["1", "2"], title="Non constant-sum game lacking outcome") g.append_move(g.root, g.players.chance, ["H", "T"]) g.set_chance_probs(g.root.infoset, ["1/2", "1/2"]) @@ -241,6 +241,8 @@ def create_non_zero_sum_lacking_outcome_efg() -> gbt.Game: g.set_outcome(g.root.children[0].children[0].children[0], g.add_outcome([2, 1])) g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([-1, 2])) g.set_outcome(g.root.children[0].children[1].children[0], g.add_outcome([1, -1])) + if not missing_term_outcome: + g.set_outcome(g.root.children[0].children[1].children[1], g.add_outcome([0, 0])) g.set_outcome(g.root.children[1].children[0].children[0], g.add_outcome([1, 0])) g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([0, 1])) g.set_outcome(g.root.children[1].children[1].children[0], g.add_outcome([-1, 1])) diff --git a/tests/test_nash.py b/tests/test_nash.py index 7e68419d09..82f6aa446d 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -155,9 +155,14 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): 4, ), ( - games.create_non_zero_sum_lacking_outcome_efg(), - [[[[0.33333333333333337, 0.6666666666666666]], [[0.5, 0.5]]]], - 1, + games.create_non_zero_sum_lacking_outcome_efg(), + [[[[0.33333333333333337, 0.6666666666666666]], [[0.5, 0.5]]]], + 1, + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), + [[[[0.33333333333333337, 0.6666666666666666]], [[0.5, 0.5]]]], + 1, ), pytest.param( games.create_large_payoff_game_efg(), @@ -527,14 +532,18 @@ def test_lcp_behavior_double(): [[[0, 0, 0, 1]], [[0, 0, 0, 1]]], ), pytest.param( - games.create_entry_accomodation_efg(), - [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") + games.create_entry_accomodation_efg(), + [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(), + [[["1/3", "2/3"]], [["1/2", "1/2"]]] ), pytest.param( - games.create_non_zero_sum_lacking_outcome_efg(), - [[["1/3", "2/3"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") + games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), + [[["1/3", "2/3"]], [["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") ), ], ) From d75a454c7d3335695b1e3c456257e269b543b6cb Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 11:15:04 +0000 Subject: [PATCH 04/18] now both versions of 3_player_with_internal_outcomes --- tests/games.py | 26 +++++++++++++++++--------- tests/test_nash.py | 20 ++++++++++++++------ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/tests/games.py b/tests/games.py index 20f28a0967..aedbcdd23b 100644 --- a/tests/games.py +++ b/tests/games.py @@ -323,7 +323,7 @@ def create_large_payoff_game_efg() -> gbt.Game: return g -def create_3_player_with_internal_outcomes_efg() -> gbt.Game: +def create_3_player_with_internal_outcomes_efg(nonterm_outcomes: bool = False) -> gbt.Game: g = gbt.Game.new_tree(players=["1", "2", "3"], title="3 player game with internal outcomes") g.append_move(g.root, g.players.chance, ["H", "T"]) g.set_chance_probs(g.root.infoset, ["1/2", "1/2"]) @@ -337,24 +337,32 @@ def create_3_player_with_internal_outcomes_efg() -> gbt.Game: iset = g.root.children[0].children[0].children[0].infoset g.append_infoset(g.root.children[0].children[0].children[1], iset) g.append_move(g.root.children[0].children[1].children[1], "2", ["C", "D"]) - o = g.add_outcome([1, 2, 3]) - g.set_outcome(g.root.children[1], o) o = g.add_outcome([3, 1, 4]) g.set_outcome(g.root.children[0].children[0].children[0].children[0], o) o = g.add_outcome([4, 0, 1]) g.set_outcome(g.root.children[0].children[0].children[0].children[1], o) - o = g.add_outcome([1, 0, 1]) - g.set_outcome(g.root.children[1].children[0].children[0], o) - o = g.add_outcome([2, -1, -2]) - g.set_outcome(g.root.children[1].children[0].children[1], o) o = g.add_outcome([1, 3, 2]) g.set_outcome(g.root.children[0].children[1].children[0], o) o = g.add_outcome([2, 4, 1]) g.set_outcome(g.root.children[0].children[1].children[1].children[0], o) o = g.add_outcome([4, 1, 3]) g.set_outcome(g.root.children[0].children[1].children[1].children[1], o) - o = g.add_outcome([-1, 2, -1]) - g.set_outcome(g.root.children[1].children[1].children[0], o) + if nonterm_outcomes: + o = g.add_outcome([1, 2, 3]) + g.set_outcome(g.root.children[1], o) + o = g.add_outcome([1, 0, 1]) + g.set_outcome(g.root.children[1].children[0].children[0], o) + o = g.add_outcome([2, -1, -2]) + g.set_outcome(g.root.children[1].children[0].children[1], o) + o = g.add_outcome([-1, 2, -1]) + g.set_outcome(g.root.children[1].children[1].children[0], o) + else: + o = g.add_outcome([2, 2, 4]) + g.set_outcome(g.root.children[1].children[0].children[0], o) + o = g.add_outcome([3, 1, 3]) + g.set_outcome(g.root.children[1].children[0].children[1], o) + o = g.add_outcome([0, 4, 2]) + g.set_outcome(g.root.children[1].children[1].children[0], o) return g diff --git a/tests/test_nash.py b/tests/test_nash.py index 82f6aa446d..108e658705 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -136,12 +136,20 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): # 2, # 9 in total found by enumpoly (see unordered test) # ), ( - games.create_3_player_with_internal_outcomes_efg(), - [ - [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.5, 0.5]], [[0.0, 1.0], [1.0, 0.0]]], - [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 1.0]], - [[0.3333333333333333, 0.6666666666666667], [1.0, 0.0]]]], - 2, + games.create_3_player_with_internal_outcomes_efg(), + [ + [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.5, 0.5]], [[0.0, 1.0], [1.0, 0.0]]], + [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 1.0]], + [[0.3333333333333333, 0.6666666666666667], [1.0, 0.0]]]], + 2, + ), + ( + games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True), + [ + [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.5, 0.5]], [[0.0, 1.0], [1.0, 0.0]]], + [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 1.0]], + [[0.3333333333333333, 0.6666666666666667], [1.0, 0.0]]]], + 2, ), ( games.create_entry_accomodation_efg(), From 23f4f8c1661cea7f28f0854da15d173f68c0292c Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 11:53:42 +0000 Subject: [PATCH 05/18] now both versions of entry_accomodation --- tests/games.py | 13 +++++++++---- tests/test_nash.py | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/games.py b/tests/games.py index aedbcdd23b..dc87f49523 100644 --- a/tests/games.py +++ b/tests/games.py @@ -211,7 +211,7 @@ def create_three_action_internal_outcomes_efg() -> gbt.Game: return g -def create_entry_accomodation_efg() -> gbt.Game: +def create_entry_accomodation_efg(nonterm_outcomes: bool = False) -> gbt.Game: g = gbt.Game.new_tree(players=["1", "2"], title="Entry-accomodation game with internal outcomes") g.append_move(g.root, "1", ["S", "T"]) @@ -219,9 +219,14 @@ def create_entry_accomodation_efg() -> gbt.Game: g.append_infoset(g.root.children[1], g.root.children[0].infoset) g.append_move(g.root.children[0].children[0], "1", ["A", "F"]) g.append_move(g.root.children[1].children[0], "1", ["A", "F"]) - g.set_outcome(g.root.children[0], g.add_outcome([3, 2])) - g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([-3, -1])) - g.set_outcome(g.root.children[0].children[1], g.add_outcome([-2, 1])) + if nonterm_outcomes: + g.set_outcome(g.root.children[0], g.add_outcome([3, 2])) + g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([-3, -1])) + g.set_outcome(g.root.children[0].children[1], g.add_outcome([-2, 1])) + else: + g.set_outcome(g.root.children[0].children[0].children[0], g.add_outcome([3, 2])) + g.set_outcome(g.root.children[0].children[0].children[1], g.add_outcome([0, 1])) + g.set_outcome(g.root.children[0].children[1], g.add_outcome([1, 3])) g.set_outcome(g.root.children[1].children[0].children[0], g.add_outcome([2, 3])) g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([1, 0])) g.set_outcome(g.root.children[1].children[1], g.add_outcome([3, 1])) diff --git a/tests/test_nash.py b/tests/test_nash.py index 108e658705..ac160941d6 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -162,6 +162,17 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0]], [[0.0, 1.0]]]], 4, ), + ( + games.create_entry_accomodation_efg(nonterm_outcomes=True), + [ + [[[0.6666666666666666, 0.33333333333333337], [1.0, 0.0], [1.0, 0.0]], + [[0.6666666666666666, 0.33333333333333337]]], + [[[0.0, 1.0], [0.0, 0.0], [0.3333333333333333, 0.6666666666666667]], + [[0.0, 1.0]]], + [[[0.0, 1.0], [0.0, 0.0], [1.0, 0.0]], [[1.0, 0.0]]], + [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0]], [[0.0, 1.0]]]], + 4, + ), ( games.create_non_zero_sum_lacking_outcome_efg(), [[[[0.33333333333333337, 0.6666666666666666]], [[0.5, 0.5]]]], @@ -539,8 +550,12 @@ def test_lcp_behavior_double(): games.create_EFG_for_nxn_bimatrix_coordination_game(4), [[[0, 0, 0, 1]], [[0, 0, 0, 1]]], ), - pytest.param( + ( games.create_entry_accomodation_efg(), + [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]] + ), + pytest.param( + games.create_entry_accomodation_efg(nonterm_outcomes=True), [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]], marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), From 58abb83dd9d0f35b3e48845494fb706bf28c7820 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 13:27:47 +0000 Subject: [PATCH 06/18] now both versions of 3 action internal outcomes game --- tests/games.py | 39 ++++++++++++++++++++++++++++----------- tests/test_nash.py | 38 ++++++++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/tests/games.py b/tests/games.py index dc87f49523..5737d62497 100644 --- a/tests/games.py +++ b/tests/games.py @@ -184,7 +184,7 @@ def create_perfect_info_internal_outcomes_efg() -> gbt.Game: return g -def create_three_action_internal_outcomes_efg() -> gbt.Game: +def create_three_action_internal_outcomes_efg(nonterm_outcomes: bool = False) -> gbt.Game: # Test 3 actions at infoset, internal outcomes and missing some outcomes at leaves g = gbt.Game.new_tree(players=["1", "2"], title="3 action, internal outcomes, lacking terminal outcomes") @@ -198,16 +198,33 @@ def create_three_action_internal_outcomes_efg() -> gbt.Game: o_m1 = g.add_outcome([-1, 1], label="-1") o_2 = g.add_outcome([2, -2], label="2") o_m2 = g.add_outcome([-2, 2], label="-2") - g.set_outcome(g.root.children[0].children[0], o_1) - g.set_outcome(g.root.children[1].children[2], o_m1) - g.set_outcome(g.root.children[0].children[0].children[1], o_m2) - g.set_outcome(g.root.children[0].children[1].children[0], o_m1) - g.set_outcome(g.root.children[0].children[1].children[1], o_1) - g.set_outcome(g.root.children[0].children[2].children[0], o_1) - g.set_outcome(g.root.children[1].children[0].children[1], o_1) - g.set_outcome(g.root.children[1].children[1].children[0], o_1) - g.set_outcome(g.root.children[1].children[1].children[1], o_m1) - g.set_outcome(g.root.children[1].children[2].children[1], o_2) + o_z = g.add_outcome([0, 0], label="0") + if nonterm_outcomes: + g.set_outcome(g.root.children[0].children[0], o_1) + g.set_outcome(g.root.children[1].children[2], o_m1) + g.set_outcome(g.root.children[0].children[0].children[1], o_m2) + g.set_outcome(g.root.children[0].children[1].children[0], o_m1) + g.set_outcome(g.root.children[0].children[1].children[1], o_1) + g.set_outcome(g.root.children[0].children[2].children[0], o_1) + g.set_outcome(g.root.children[1].children[0].children[1], o_1) + g.set_outcome(g.root.children[1].children[1].children[0], o_1) + g.set_outcome(g.root.children[1].children[1].children[1], o_m1) + g.set_outcome(g.root.children[1].children[2].children[1], o_2) + else: + g.set_outcome(g.root.children[0].children[0].children[0], o_1) + g.set_outcome(g.root.children[0].children[0].children[1], o_m1) + g.set_outcome(g.root.children[0].children[1].children[0], o_m1) + g.set_outcome(g.root.children[0].children[1].children[1], o_1) + g.set_outcome(g.root.children[0].children[2].children[0], o_1) + g.set_outcome(g.root.children[0].children[2].children[1], o_z) + + g.set_outcome(g.root.children[1].children[0].children[0], o_z) + g.set_outcome(g.root.children[1].children[0].children[1], o_1) + g.set_outcome(g.root.children[1].children[1].children[0], o_1) + g.set_outcome(g.root.children[1].children[1].children[1], o_m1) + g.set_outcome(g.root.children[1].children[2].children[0], o_m1) + g.set_outcome(g.root.children[1].children[2].children[1], o_1) + return g diff --git a/tests/test_nash.py b/tests/test_nash.py index ac160941d6..b9b958ad14 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -502,13 +502,20 @@ def test_lcp_behavior_double(): [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), + ( + games.create_three_action_internal_outcomes_efg(), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], + ] + ), pytest.param( - games.create_three_action_internal_outcomes_efg(), - [ - [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], - [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], - ], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") + games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], + ], + marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), ( games.create_large_payoff_game_efg(), @@ -712,13 +719,20 @@ def test_lp_behavior_double(): [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), + ( + games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], + ] + ), pytest.param( - games.create_three_action_internal_outcomes_efg(), - [ - [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], - [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], - ], - marks=pytest.mark.xfail(reason="Problem with internal and missing outcomes") + games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), + [ + [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], + [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], + ], + marks=pytest.mark.xfail(reason="Problem with internal and missing outcomes") ), ( games.create_large_payoff_game_efg(), From f8a6885d2ecb082062c5c37cc780224333565c37 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 14:03:29 +0000 Subject: [PATCH 07/18] now both versions of perfect-info game --- tests/games.py | 32 ++++++++++++++++++++++---------- tests/test_nash.py | 20 ++++++++++++-------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/tests/games.py b/tests/games.py index 5737d62497..1e0ce2fa8b 100644 --- a/tests/games.py +++ b/tests/games.py @@ -966,21 +966,33 @@ def create_seq_form_STOC_paper_zero_sum_2_player_efg() -> gbt.Game: return g -def create_two_player_perfect_info_win_lose_efg() -> gbt.Game: +def create_two_player_perfect_info_win_lose_efg(nonterm_outcomes: bool = False) -> gbt.Game: g = gbt.Game.new_tree(players=["1", "2"], title="2 player perfect info win lose") g.append_move(g.root, "2", ["a", "b"]) g.append_move(g.root.children[0], "1", ["L", "R"]) g.append_move(g.root.children[1], "1", ["L", "R"]) g.append_move(g.root.children[0].children[0], "2", ["l", "r"]) - g.set_outcome( - g.root.children[0].children[0].children[0], g.add_outcome([1, -1], label="aLl") - ) - g.set_outcome( - g.root.children[0].children[0].children[1], g.add_outcome([-1, 1], label="aLr") - ) - g.set_outcome(g.root.children[0].children[1], g.add_outcome([1, -1], label="aR")) - g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) - g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) + if not nonterm_outcomes: + g.set_outcome( + g.root.children[0].children[0].children[0], g.add_outcome([1, -1], label="aLl") + ) + g.set_outcome( + g.root.children[0].children[0].children[1], g.add_outcome([-1, 1], label="aLr") + ) + g.set_outcome(g.root.children[0].children[1], g.add_outcome([1, -1], label="aR")) + g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) + g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) + else: + g.set_outcome(g.root.children[0], g.add_outcome([-100, 50], label="a")) + g.set_outcome( + g.root.children[0].children[0].children[0], g.add_outcome([101, -51], label="aLl") + ) + g.set_outcome( + g.root.children[0].children[0].children[1], g.add_outcome([99, -49], label="aLr") + ) + g.set_outcome(g.root.children[0].children[1], g.add_outcome([101, -51], label="aR")) + g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) + g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) return g diff --git a/tests/test_nash.py b/tests/test_nash.py index b9b958ad14..f5ddc6e9de 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -497,10 +497,14 @@ def test_lcp_behavior_double(): [[["2/3", "1/3"]], [[1, 0], ["1/3", "2/3"]]], marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") ), - pytest.param( - games.create_perfect_info_internal_outcomes_efg(), + ( + games.create_two_player_perfect_info_win_lose_efg(), [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") + ), + pytest.param( + games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True), + [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with non-terminal outcomes") ), ( games.create_three_action_internal_outcomes_efg(), @@ -651,6 +655,11 @@ def test_lp_behavior_double(): games.create_two_player_perfect_info_win_lose_efg(), [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], ), + pytest.param( + games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True), + [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], + marks=pytest.mark.xfail(reason="Problem with non-terminal outcomes") + ), ( games.create_2x2_zero_sum_efg(missing_term_outcome=False), [[["1/2", "1/2"]], [["1/2", "1/2"]]] @@ -714,11 +723,6 @@ def test_lp_behavior_double(): [[["2/3", "1/3"]], [[1, 0], ["1/3", "2/3"]]], marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") ), - pytest.param( - games.create_perfect_info_internal_outcomes_efg(), - [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") - ), ( games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), [ From 865ac2444dc60ad78acc232ca09aeb4b17749f60 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 14:40:42 +0000 Subject: [PATCH 08/18] removed redundant code from tests --- tests/games.py | 38 -------------------------------------- tests/test_nash.py | 15 ++------------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/tests/games.py b/tests/games.py index 1e0ce2fa8b..ddb367a66c 100644 --- a/tests/games.py +++ b/tests/games.py @@ -146,44 +146,6 @@ def create_perfect_info_with_chance_efg() -> gbt.Game: return g -def create_one_card_poker_lacking_outcome_efg() -> gbt.Game: - g = gbt.Game.new_tree(players=["Bob", "Alice"], - title="One card poker game, after Myerson (1991)") - g.append_move(g.root, g.players.chance, ["King", "Queen"]) - for node in g.root.children: - g.append_move(node, "Alice", ["Raise", "Fold"]) - g.append_move(g.root.children[0].children[0], "Bob", ["Meet", "Pass"]) - g.append_infoset(g.root.children[1].children[0], - g.root.children[0].children[0].infoset) - alice_winsbig = g.add_outcome([-1, 1], label="Alice wins big") - bob_winsbig = g.add_outcome([3, -3], label="Bob wins big") - bob_wins = g.add_outcome([2, -2], label="Bob wins") - g.set_outcome(g.root.children[0].children[0].children[0], alice_winsbig) - g.set_outcome(g.root.children[0].children[1], bob_wins) - g.set_outcome(g.root.children[1].children[0].children[0], bob_winsbig) - g.set_outcome(g.root.children[1].children[1], bob_wins) - return g - - -def create_perfect_info_internal_outcomes_efg() -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2"], title="2 player perfect info win lose") - g.append_move(g.root, "2", ["a", "b"]) - g.append_move(g.root.children[0], "1", ["L", "R"]) - g.append_move(g.root.children[1], "1", ["L", "R"]) - g.append_move(g.root.children[0].children[0], "2", ["l", "r"]) - g.set_outcome(g.root.children[0], g.add_outcome([-100, 50], label="a")) - g.set_outcome( - g.root.children[0].children[0].children[0], g.add_outcome([101, -51], label="aLl") - ) - g.set_outcome( - g.root.children[0].children[0].children[1], g.add_outcome([99, -49], label="aLr") - ) - g.set_outcome(g.root.children[0].children[1], g.add_outcome([101, -51], label="aR")) - g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) - g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) - return g - - def create_three_action_internal_outcomes_efg(nonterm_outcomes: bool = False) -> gbt.Game: # Test 3 actions at infoset, internal outcomes and missing some outcomes at leaves g = gbt.Game.new_tree(players=["1", "2"], diff --git a/tests/test_nash.py b/tests/test_nash.py index f5ddc6e9de..18930ca37a 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -492,19 +492,13 @@ def test_lcp_behavior_double(): games.create_perfect_info_with_chance_efg(), [[[0, 1]], [[0, 1], [0, 1]]], ), - pytest.param( - games.create_one_card_poker_lacking_outcome_efg(), - [[["2/3", "1/3"]], [[1, 0], ["1/3", "2/3"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") - ), ( games.create_two_player_perfect_info_win_lose_efg(), [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], ), - pytest.param( + ( games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True), [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with non-terminal outcomes") ), ( games.create_three_action_internal_outcomes_efg(), @@ -718,13 +712,8 @@ def test_lp_behavior_double(): games.create_perfect_info_with_chance_efg(), [[[0, 1]], [[1, 0], [1, 0]]], ), - pytest.param( - games.create_one_card_poker_lacking_outcome_efg(), - [[["2/3", "1/3"]], [[1, 0], ["1/3", "2/3"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") - ), ( - games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), + games.create_three_action_internal_outcomes_efg(), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], From 4db44ab9d5e8778611074c40bd7676079f4e52f0 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 14:58:06 +0000 Subject: [PATCH 09/18] removed redundant code from tests --- tests/test_nash.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/test_nash.py b/tests/test_nash.py index 18930ca37a..ad23d5bb80 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -216,12 +216,6 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): [[1.0, 0.0], [0.0, 1.0]]]], 3, ), - ( - games.create_one_card_poker_lacking_outcome_efg(), - [[[[0.6666666666666666, 0.33333333333333337]], - [[1.0, 0.0], [0.3333333333333333, 0.6666666666666667]]]], - 1, - ), ], ) def test_enumpoly_ordered_behavior( From 44e652a0d8f7968ceb58b0e4954dd80bef7e046f Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Wed, 17 Dec 2025 18:40:09 +0000 Subject: [PATCH 10/18] fixed 3_player_with_internal_outcomes_efg --- tests/games.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/games.py b/tests/games.py index ddb367a66c..4be100ea51 100644 --- a/tests/games.py +++ b/tests/games.py @@ -343,10 +343,12 @@ def create_3_player_with_internal_outcomes_efg(nonterm_outcomes: bool = False) - else: o = g.add_outcome([2, 2, 4]) g.set_outcome(g.root.children[1].children[0].children[0], o) - o = g.add_outcome([3, 1, 3]) + o = g.add_outcome([3, 1, 1]) g.set_outcome(g.root.children[1].children[0].children[1], o) o = g.add_outcome([0, 4, 2]) g.set_outcome(g.root.children[1].children[1].children[0], o) + o = g.add_outcome([1, 2, 3]) + g.set_outcome(g.root.children[1].children[1].children[1], o) return g From 4f5c4b3e16740236aa0df1e55d8de2373a0e9773 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Thu, 18 Dec 2025 10:04:22 +0000 Subject: [PATCH 11/18] changed enumpoly expected equilibria to rationals except for large payoff game --- tests/test_nash.py | 68 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/test_nash.py b/tests/test_nash.py index ad23d5bb80..35c1b0a5af 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -138,49 +138,49 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): ( games.create_3_player_with_internal_outcomes_efg(), [ - [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.5, 0.5]], [[0.0, 1.0], [1.0, 0.0]]], - [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 1.0]], - [[0.3333333333333333, 0.6666666666666667], [1.0, 0.0]]]], + [[[1, 0], [1, 0]], [[1, 0], ["1/2", "1/2"]], [[0, 1], [1, 0]]], + [[[1, 0], [1, 0]], [[1, 0], [0, 1]], + [["1/3", "2/3"], [1, 0]]]], 2, ), ( games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True), [ - [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.5, 0.5]], [[0.0, 1.0], [1.0, 0.0]]], - [[[1.0, 0.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 1.0]], - [[0.3333333333333333, 0.6666666666666667], [1.0, 0.0]]]], + [[[1, 0], [1, 0]], [[1, 0], ["1/2", "1/2"]], [[0, 1], [1, 0]]], + [[[1, 0], [1, 0]], [[1, 0], [0, 1]], + [["1/3", "2/3"], [1, 0]]]], 2, ), ( games.create_entry_accomodation_efg(), [ - [[[0.6666666666666666, 0.33333333333333337], [1.0, 0.0], [1.0, 0.0]], - [[0.6666666666666666, 0.33333333333333337]]], - [[[0.0, 1.0], [0.0, 0.0], [0.3333333333333333, 0.6666666666666667]], - [[0.0, 1.0]]], - [[[0.0, 1.0], [0.0, 0.0], [1.0, 0.0]], [[1.0, 0.0]]], - [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0]], [[0.0, 1.0]]]], + [[["2/3", "1/3"], [1, 0], [1, 0]], + [["2/3", "1/3"]]], + [[[0, 1], [0, 0], ["1/3", "2/3"]], + [[0, 1]]], + [[[0, 1], [0, 0], [1, 0]], [[1, 0]]], + [[[0, 1], [0, 0], [0, 0]], [[0, 1]]]], 4, ), ( games.create_entry_accomodation_efg(nonterm_outcomes=True), [ - [[[0.6666666666666666, 0.33333333333333337], [1.0, 0.0], [1.0, 0.0]], - [[0.6666666666666666, 0.33333333333333337]]], - [[[0.0, 1.0], [0.0, 0.0], [0.3333333333333333, 0.6666666666666667]], - [[0.0, 1.0]]], - [[[0.0, 1.0], [0.0, 0.0], [1.0, 0.0]], [[1.0, 0.0]]], - [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0]], [[0.0, 1.0]]]], + [[["2/3", "1/3"], [1, 0], [1, 0]], + [["2/3", "1/3"]]], + [[[0, 1], [0, 0], ["1/3", "2/3"]], + [[0, 1]]], + [[[0, 1], [0, 0], [1, 0]], [[1, 0]]], + [[[0, 1], [0, 0], [0, 0]], [[0, 1]]]], 4, ), ( games.create_non_zero_sum_lacking_outcome_efg(), - [[[[0.33333333333333337, 0.6666666666666666]], [[0.5, 0.5]]]], + [[[["1/3", "2/3"]], [["1/2", "1/2"]]]], 1, ), ( games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), - [[[[0.33333333333333337, 0.6666666666666666]], [[0.5, 0.5]]]], + [[[["1/3", "2/3"]], [["1/2", "1/2"]]]], 1, ), pytest.param( @@ -196,24 +196,24 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): ), ( games.create_chance_in_middle_efg(), - [[[[0.27272727272727276, 0.7272727272727273], - [1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [1.0, 0.0]], - [[1.0, 0.0], [0.5454545454545455, 0.4545454545454545]]], - [[[1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [0.0, 0.0], [0.0, 0.0]], - [[0.0, 1.0], [1.0, 0.0]]], - [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0], [1.0, 0.0], [1.0, 0.0]], - [[1.0, 0.0], [0.0, 1.0]]]], + [[[["3/11", "8/11"], + [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]]], + [[[1, 0], [1, 0], [1, 0], [0, 0], [0, 0]], + [[0, 1], [1, 0]]], + [[[0, 1], [0, 0], [0, 0], [1, 0], [1, 0]], + [[1, 0], [0, 1]]]], 3, ), ( games.create_chance_in_middle_efg(nonterm_outcomes=True), - [[[[0.27272727272727276, 0.7272727272727273], - [1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [1.0, 0.0]], - [[1.0, 0.0], [0.5454545454545455, 0.4545454545454545]]], - [[[1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [0.0, 0.0], [0.0, 0.0]], - [[0.0, 1.0], [1.0, 0.0]]], - [[[0.0, 1.0], [0.0, 0.0], [0.0, 0.0], [1.0, 0.0], [1.0, 0.0]], - [[1.0, 0.0], [0.0, 1.0]]]], + [[[["3/11", "8/11"], + [1, 0], [1, 0], [1, 0], [1, 0]], + [[1, 0], ["6/11", "5/11"]]], + [[[1, 0], [1, 0], [1, 0], [0, 0], [0, 0]], + [[0, 1], [1, 0]]], + [[[0, 1], [0, 0], [0, 0], [1, 0], [1, 0]], + [[1, 0], [0, 1]]]], 3, ), ], From c87d438135ff293ce18e80839a4af70adbc83fdf Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Thu, 18 Dec 2025 10:12:19 +0000 Subject: [PATCH 12/18] removed large payoff game from enumpoly tests --- tests/test_nash.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/test_nash.py b/tests/test_nash.py index 35c1b0a5af..2fcc930a08 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -183,17 +183,6 @@ def test_enummixed_rational(game: gbt.Game, mixed_strategy_prof_data: list): [[[["1/3", "2/3"]], [["1/2", "1/2"]]]], 1, ), - pytest.param( - games.create_large_payoff_game_efg(), - [[[[1.0, 0.0], [1.0, 0.0]], [[0.0, 1.0], [1.0, 0.0]]], - [[[1.0, 0.0], [1.0, 0.0]], [[0.0, 1.0], [0.0, 0.0]]], - [[[1.0, 0.0], [0.0, 1.0]], [[0.0, 1.0], [1.0, 0.0]]], - [[[0.0, 1.0], [1.0, 0.0]], [[0.0, 1.0], [1.0, 0.0]]], - [[[0.0, 1.0], [1.0, 0.0]], [[0.0, 1.0], [1.0, 0.0]]], - [[[0.0, 1.0], [0.0, 1.0]], [[0.0, 0.0], [1.0, 0.0]]]], - 6, - marks=pytest.mark.xfail(reason="True equilibrium has tiny probabilities") - ), ( games.create_chance_in_middle_efg(), [[[["3/11", "8/11"], From 147fa8381f3d9ace73d29f5adb4bb10016062e13 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Thu, 18 Dec 2025 12:09:06 +0000 Subject: [PATCH 13/18] added test_games_match --- tests/test_nash.py | 75 +++++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/tests/test_nash.py b/tests/test_nash.py index 2fcc930a08..f886ea5bf9 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -847,27 +847,54 @@ def test_logit_solve_lambda(): game=game, lam=[1, 2, 3], first_step=0.2, max_accel=1)) > 0 -def test_kuhn(): - """ - TEMPORARY - - Check that the reduced strategic forms match for the versions with and without - nonterminal nodes - """ - old = games.create_kuhn_poker_efg(nonterm_outcomes=False) - new = games.create_kuhn_poker_efg(nonterm_outcomes=True) - for i in [0, 1]: - assert (old.to_arrays()[i] == new.to_arrays()[i]).all() - - -def test_stripped(): - """ - TEMPORARY - - Check that the reduced strategic forms match for the versions with and without - nonterminal nodes - """ - old = games.create_stripped_down_poker_efg() - new = games.create_stripped_down_poker_efg(nonterm_outcomes=True) - for i in [0, 1]: - assert (old.to_arrays()[i] == new.to_arrays()[i]).all() +@pytest.mark.parametrize( + "standard, modified", + [ + ( + games.create_two_player_perfect_info_win_lose_efg(), + games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True) + ), + ( + games.create_3_player_with_internal_outcomes_efg(), + games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True) + ), + ( + games.create_chance_in_middle_efg(), + games.create_chance_in_middle_efg(nonterm_outcomes=True) + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(), + games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True) + ), + ( + games.create_entry_accomodation_efg(), + games.create_entry_accomodation_efg(nonterm_outcomes=True) + ), + ( + games.create_three_action_internal_outcomes_efg(), + games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True) + ), + ( + games.create_kuhn_poker_efg(), + games.create_kuhn_poker_efg(nonterm_outcomes=True) + ), + ( + games.create_stripped_down_poker_efg(), + games.create_stripped_down_poker_efg(nonterm_outcomes=True) + ), + ( + games.create_2x2_zero_sum_efg(), + games.create_2x2_zero_sum_efg(missing_term_outcome=True) + ), + ( + games.create_matching_pennies_efg(), + games.create_matching_pennies_efg(with_neutral_outcome=True) + ), + ], +) +def test_games_match(standard: gbt.Game, modified: gbt.Game): + arrays_standard = standard.to_arrays() + arrays_modified = modified.to_arrays() + assert (len(arrays_standard) == len(arrays_modified)) + for i in range(len(arrays_standard)): + assert (arrays_standard[i] == arrays_modified[i]).all() From 307a4dcd5c1a8767713ddba94482e7166ea9d91c Mon Sep 17 00:00:00 2001 From: Rahul Savani Date: Thu, 18 Dec 2025 13:05:31 +0000 Subject: [PATCH 14/18] test_reduced_strategy_form_nonterminal_outcomes_consistency in test_extensive --- tests/games.py | 22 +++++++++------ tests/test_extensive.py | 60 +++++++++++++++++++++++++++++++++++++++++ tests/test_nash.py | 53 ------------------------------------ 3 files changed, 74 insertions(+), 61 deletions(-) diff --git a/tests/games.py b/tests/games.py index 4be100ea51..1d20e8ae49 100644 --- a/tests/games.py +++ b/tests/games.py @@ -120,6 +120,7 @@ def create_2x2_zero_sum_efg(missing_term_outcome: bool = False) -> gbt.Game: if not missing_term_outcome: g.set_outcome(g.root.children["T"].children["r"], draw) + g.to_efg(f"2x2_zero_sum_efg_{missing_term_outcome}.efg") return g @@ -147,9 +148,10 @@ def create_perfect_info_with_chance_efg() -> gbt.Game: def create_three_action_internal_outcomes_efg(nonterm_outcomes: bool = False) -> gbt.Game: - # Test 3 actions at infoset, internal outcomes and missing some outcomes at leaves - g = gbt.Game.new_tree(players=["1", "2"], - title="3 action, internal outcomes, lacking terminal outcomes") + """ + with nonterm_outcomes there are nonterminal outcomes, and missing outcomes at some leaves + """ + g = gbt.Game.new_tree(players=["1", "2"], title="") g.append_move(g.root, g.players.chance, ["H", "L"]) for i in range(2): g.append_move(g.root.children[i], "1", ["A", "B", "C"]) @@ -186,13 +188,13 @@ def create_three_action_internal_outcomes_efg(nonterm_outcomes: bool = False) -> g.set_outcome(g.root.children[1].children[1].children[1], o_m1) g.set_outcome(g.root.children[1].children[2].children[0], o_m1) g.set_outcome(g.root.children[1].children[2].children[1], o_1) - + g.to_efg(f"three_action_internal_outcomes_efg_{nonterm_outcomes}.efg") return g def create_entry_accomodation_efg(nonterm_outcomes: bool = False) -> gbt.Game: g = gbt.Game.new_tree(players=["1", "2"], - title="Entry-accomodation game with internal outcomes") + title="Entry-accomodation game") g.append_move(g.root, "1", ["S", "T"]) g.append_move(g.root.children[0], "2", ["E", "O"]) g.append_infoset(g.root.children[1], g.root.children[0].infoset) @@ -209,6 +211,7 @@ def create_entry_accomodation_efg(nonterm_outcomes: bool = False) -> gbt.Game: g.set_outcome(g.root.children[1].children[0].children[0], g.add_outcome([2, 3])) g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([1, 0])) g.set_outcome(g.root.children[1].children[1], g.add_outcome([3, 1])) + g.to_efg(f"entry_accomodation_efg_{nonterm_outcomes}.efg") return g @@ -231,6 +234,7 @@ def create_non_zero_sum_lacking_outcome_efg(missing_term_outcome: bool = False) g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([0, 1])) g.set_outcome(g.root.children[1].children[1].children[0], g.add_outcome([-1, 1])) g.set_outcome(g.root.children[1].children[1].children[1], g.add_outcome([2, -1])) + g.to_efg(f"non_zero_sum_lacking_outcome_efg_{missing_term_outcome}.efg") return g @@ -281,6 +285,7 @@ def create_chance_in_middle_efg(nonterm_outcomes: bool = False) -> gbt.Game: g.set_outcome(g.root.children[1].children[1].children[0].children[1], o_m1) g.set_outcome(g.root.children[1].children[1].children[1].children[0], o_h) g.set_outcome(g.root.children[1].children[1].children[1].children[1], o_mh) + g.to_efg(f"chance_in_middle_efg_{nonterm_outcomes}.efg") return g @@ -308,7 +313,7 @@ def create_large_payoff_game_efg() -> gbt.Game: def create_3_player_with_internal_outcomes_efg(nonterm_outcomes: bool = False) -> gbt.Game: - g = gbt.Game.new_tree(players=["1", "2", "3"], title="3 player game with internal outcomes") + g = gbt.Game.new_tree(players=["1", "2", "3"], title="3 player game") g.append_move(g.root, g.players.chance, ["H", "T"]) g.set_chance_probs(g.root.infoset, ["1/2", "1/2"]) g.append_move(g.root.children[0], "1", ["a", "b"]) @@ -349,6 +354,7 @@ def create_3_player_with_internal_outcomes_efg(nonterm_outcomes: bool = False) - g.set_outcome(g.root.children[1].children[1].children[0], o) o = g.add_outcome([1, 2, 3]) g.set_outcome(g.root.children[1].children[1].children[1], o) + g.to_efg(f"3_player_with_internal_outcomes_{nonterm_outcomes}.efg") return g @@ -369,6 +375,7 @@ def create_matching_pennies_efg(with_neutral_outcome: bool = False) -> gbt.Game: g.set_outcome(g.root.children["T"].children["t"], alice_win) g.set_outcome(g.root.children["H"].children["t"], alice_lose) g.set_outcome(g.root.children["T"].children["h"], alice_lose) + g.to_efg(f"matching_pennies_efg_{with_neutral_outcome}.efg") return g @@ -440,8 +447,6 @@ def create_stripped_down_poker_efg(nonterm_outcomes: bool = False) -> gbt.Game: g.set_outcome(bob_calls_and_loses_node, bob_calls_and_loses_outcome) bob_calls_and_wins_node = g.root.children["Queen"].children["Bet"].children["Call"] g.set_outcome(bob_calls_and_wins_node, bob_calls_and_wins_outcome) - # g.to_efg("stripped_down_poker_nonterminal_outcomes.efg") - return g @@ -957,6 +962,7 @@ def create_two_player_perfect_info_win_lose_efg(nonterm_outcomes: bool = False) g.set_outcome(g.root.children[0].children[1], g.add_outcome([101, -51], label="aR")) g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) + g.to_efg(f"two_player_perfect_info_win_lose_efg_{nonterm_outcomes}.efg") return g diff --git a/tests/test_extensive.py b/tests/test_extensive.py index 7b84702853..75ad60993f 100644 --- a/tests/test_extensive.py +++ b/tests/test_extensive.py @@ -409,3 +409,63 @@ def test_reduced_strategic_form( # convert strings to rationals exp_array = games.vectorized_make_rational(np_arrays_of_rsf[i]) assert (arrays[i] == exp_array).all() + + +@pytest.mark.parametrize( + "standard,modified", + [ + ( + games.create_two_player_perfect_info_win_lose_efg(), + games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True) + ), + ( + games.create_3_player_with_internal_outcomes_efg(), + games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True) + ), + ( + games.create_chance_in_middle_efg(), + games.create_chance_in_middle_efg(nonterm_outcomes=True) + ), + ( + games.create_non_zero_sum_lacking_outcome_efg(), + games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True) + ), + ( + games.create_entry_accomodation_efg(), + games.create_entry_accomodation_efg(nonterm_outcomes=True) + ), + ( + games.create_three_action_internal_outcomes_efg(), + games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True) + ), + ( + games.create_kuhn_poker_efg(), + games.create_kuhn_poker_efg(nonterm_outcomes=True) + ), + ( + games.create_stripped_down_poker_efg(), + games.create_stripped_down_poker_efg(nonterm_outcomes=True) + ), + ( + games.create_2x2_zero_sum_efg(), + games.create_2x2_zero_sum_efg(missing_term_outcome=True) + ), + ( + games.create_matching_pennies_efg(), + games.create_matching_pennies_efg(with_neutral_outcome=True) + ), + ], +) +def test_reduced_strategy_form_nonterminal_outcomes_consistency(standard: gbt.Game, + modified: gbt.Game): + """ + standard: game uses only non-terminal outcomes, with all non-terminal nodes having outcomes + modified: is payoff equivalent, but with non-terminal outcomes or missing terminal outcomes + + The test checks that the corresponding reduced strategic forms match. + """ + arrays_s = standard.to_arrays() + arrays_m = modified.to_arrays() + assert (len(arrays_s) == len(arrays_m)) + for array_s, array_m in zip(arrays_s, arrays_m, strict=True): + assert (array_s == array_m).all() diff --git a/tests/test_nash.py b/tests/test_nash.py index f886ea5bf9..3f4cdfe499 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -845,56 +845,3 @@ def test_logit_solve_lambda(): game = games.read_from_file("const_sum_game.nfg") assert len(gbt.qre.logit_solve_lambda( game=game, lam=[1, 2, 3], first_step=0.2, max_accel=1)) > 0 - - -@pytest.mark.parametrize( - "standard, modified", - [ - ( - games.create_two_player_perfect_info_win_lose_efg(), - games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True) - ), - ( - games.create_3_player_with_internal_outcomes_efg(), - games.create_3_player_with_internal_outcomes_efg(nonterm_outcomes=True) - ), - ( - games.create_chance_in_middle_efg(), - games.create_chance_in_middle_efg(nonterm_outcomes=True) - ), - ( - games.create_non_zero_sum_lacking_outcome_efg(), - games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True) - ), - ( - games.create_entry_accomodation_efg(), - games.create_entry_accomodation_efg(nonterm_outcomes=True) - ), - ( - games.create_three_action_internal_outcomes_efg(), - games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True) - ), - ( - games.create_kuhn_poker_efg(), - games.create_kuhn_poker_efg(nonterm_outcomes=True) - ), - ( - games.create_stripped_down_poker_efg(), - games.create_stripped_down_poker_efg(nonterm_outcomes=True) - ), - ( - games.create_2x2_zero_sum_efg(), - games.create_2x2_zero_sum_efg(missing_term_outcome=True) - ), - ( - games.create_matching_pennies_efg(), - games.create_matching_pennies_efg(with_neutral_outcome=True) - ), - ], -) -def test_games_match(standard: gbt.Game, modified: gbt.Game): - arrays_standard = standard.to_arrays() - arrays_modified = modified.to_arrays() - assert (len(arrays_standard) == len(arrays_modified)) - for i in range(len(arrays_standard)): - assert (arrays_standard[i] == arrays_modified[i]).all() From 6da8e85d3685a60b4726f0773fc70b42cb5252d5 Mon Sep 17 00:00:00 2001 From: Rahul Savani Date: Thu, 18 Dec 2025 13:13:49 +0000 Subject: [PATCH 15/18] removed to_efg calls --- tests/games.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/games.py b/tests/games.py index 1d20e8ae49..93b7b78325 100644 --- a/tests/games.py +++ b/tests/games.py @@ -120,7 +120,6 @@ def create_2x2_zero_sum_efg(missing_term_outcome: bool = False) -> gbt.Game: if not missing_term_outcome: g.set_outcome(g.root.children["T"].children["r"], draw) - g.to_efg(f"2x2_zero_sum_efg_{missing_term_outcome}.efg") return g @@ -188,7 +187,6 @@ def create_three_action_internal_outcomes_efg(nonterm_outcomes: bool = False) -> g.set_outcome(g.root.children[1].children[1].children[1], o_m1) g.set_outcome(g.root.children[1].children[2].children[0], o_m1) g.set_outcome(g.root.children[1].children[2].children[1], o_1) - g.to_efg(f"three_action_internal_outcomes_efg_{nonterm_outcomes}.efg") return g @@ -211,7 +209,6 @@ def create_entry_accomodation_efg(nonterm_outcomes: bool = False) -> gbt.Game: g.set_outcome(g.root.children[1].children[0].children[0], g.add_outcome([2, 3])) g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([1, 0])) g.set_outcome(g.root.children[1].children[1], g.add_outcome([3, 1])) - g.to_efg(f"entry_accomodation_efg_{nonterm_outcomes}.efg") return g @@ -234,7 +231,6 @@ def create_non_zero_sum_lacking_outcome_efg(missing_term_outcome: bool = False) g.set_outcome(g.root.children[1].children[0].children[1], g.add_outcome([0, 1])) g.set_outcome(g.root.children[1].children[1].children[0], g.add_outcome([-1, 1])) g.set_outcome(g.root.children[1].children[1].children[1], g.add_outcome([2, -1])) - g.to_efg(f"non_zero_sum_lacking_outcome_efg_{missing_term_outcome}.efg") return g @@ -285,7 +281,6 @@ def create_chance_in_middle_efg(nonterm_outcomes: bool = False) -> gbt.Game: g.set_outcome(g.root.children[1].children[1].children[0].children[1], o_m1) g.set_outcome(g.root.children[1].children[1].children[1].children[0], o_h) g.set_outcome(g.root.children[1].children[1].children[1].children[1], o_mh) - g.to_efg(f"chance_in_middle_efg_{nonterm_outcomes}.efg") return g @@ -354,7 +349,6 @@ def create_3_player_with_internal_outcomes_efg(nonterm_outcomes: bool = False) - g.set_outcome(g.root.children[1].children[1].children[0], o) o = g.add_outcome([1, 2, 3]) g.set_outcome(g.root.children[1].children[1].children[1], o) - g.to_efg(f"3_player_with_internal_outcomes_{nonterm_outcomes}.efg") return g @@ -375,7 +369,6 @@ def create_matching_pennies_efg(with_neutral_outcome: bool = False) -> gbt.Game: g.set_outcome(g.root.children["T"].children["t"], alice_win) g.set_outcome(g.root.children["H"].children["t"], alice_lose) g.set_outcome(g.root.children["T"].children["h"], alice_lose) - g.to_efg(f"matching_pennies_efg_{with_neutral_outcome}.efg") return g @@ -962,7 +955,6 @@ def create_two_player_perfect_info_win_lose_efg(nonterm_outcomes: bool = False) g.set_outcome(g.root.children[0].children[1], g.add_outcome([101, -51], label="aR")) g.set_outcome(g.root.children[1].children[0], g.add_outcome([1, -1], label="bL")) g.set_outcome(g.root.children[1].children[1], g.add_outcome([-1, 1], label="bR")) - g.to_efg(f"two_player_perfect_info_win_lose_efg_{nonterm_outcomes}.efg") return g From 5dc29509223547415b34b7b4a5223b0a18100b10 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Thu, 18 Dec 2025 16:34:37 +0000 Subject: [PATCH 16/18] fixed standard form of create_3_player_with_internal_outcomes --- tests/games.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/games.py b/tests/games.py index 93b7b78325..3325a49402 100644 --- a/tests/games.py +++ b/tests/games.py @@ -349,6 +349,9 @@ def create_3_player_with_internal_outcomes_efg(nonterm_outcomes: bool = False) - g.set_outcome(g.root.children[1].children[1].children[0], o) o = g.add_outcome([1, 2, 3]) g.set_outcome(g.root.children[1].children[1].children[1], o) + o = g.add_outcome([0, 0, 0]) + g.set_outcome(g.root.children[0].children[0].children[1].children[0], o) + g.set_outcome(g.root.children[0].children[0].children[1].children[1], o) return g From 7cc1361834c37f91ccd2dd0bef6a62cb5e45bef2 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Thu, 18 Dec 2025 16:41:53 +0000 Subject: [PATCH 17/18] removed xfail markers --- tests/test_nash.py | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/tests/test_nash.py b/tests/test_nash.py index 3f4cdfe499..b4e1f69e7d 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -418,23 +418,20 @@ def test_lcp_behavior_double(): games.create_2x2_zero_sum_efg(), [[["1/2", "1/2"]], [["1/2", "1/2"]]] ), - pytest.param( + ( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcomes") ), (games.create_matching_pennies_efg(), [[["1/2", "1/2"]], [["1/2", "1/2"]]]), - pytest.param( + ( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing outcomes") ), (games.create_stripped_down_poker_efg(), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]]), - pytest.param( + ( games.create_stripped_down_poker_efg(nonterm_outcomes=True), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), ( games.create_kuhn_poker_efg(), @@ -450,7 +447,7 @@ def test_lcp_behavior_double(): [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], ), - pytest.param( + ( games.create_kuhn_poker_efg(nonterm_outcomes=True), [ [ @@ -463,7 +460,6 @@ def test_lcp_behavior_double(): ], [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), # In the next test case: # 1/2-1/2 for l/r is determined by MixedBehaviorProfile.UndefinedToCentroid() @@ -490,13 +486,12 @@ def test_lcp_behavior_double(): [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], ] ), - pytest.param( + ( games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], ], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), ( games.create_large_payoff_game_efg(), @@ -513,13 +508,12 @@ def test_lcp_behavior_double(): [[1, 0], ["6/11", "5/11"]] ] ), - pytest.param( + ( games.create_chance_in_middle_efg(nonterm_outcomes=True), [ [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]] ], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), # Non-zero-sum games ( @@ -542,19 +536,17 @@ def test_lcp_behavior_double(): games.create_entry_accomodation_efg(), [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]] ), - pytest.param( + ( games.create_entry_accomodation_efg(nonterm_outcomes=True), [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), ( games.create_non_zero_sum_lacking_outcome_efg(), [[["1/3", "2/3"]], [["1/2", "1/2"]]] ), - pytest.param( + ( games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), [[["1/3", "2/3"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") ), ], ) @@ -632,35 +624,31 @@ def test_lp_behavior_double(): games.create_two_player_perfect_info_win_lose_efg(), [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], ), - pytest.param( + ( games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True), [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], - marks=pytest.mark.xfail(reason="Problem with non-terminal outcomes") ), ( games.create_2x2_zero_sum_efg(missing_term_outcome=False), [[["1/2", "1/2"]], [["1/2", "1/2"]]] ), - pytest.param( + ( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with missing terminal outcome") ), (games.create_matching_pennies_efg(with_neutral_outcome=False), [[["1/2", "1/2"]], [["1/2", "1/2"]]]), - pytest.param( + ( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], - marks=pytest.mark.xfail(reason="Problem with outcomes") ), ( games.create_stripped_down_poker_efg(), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], ), - pytest.param( + ( games.create_stripped_down_poker_efg(nonterm_outcomes=True), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], - marks=pytest.mark.xfail(reason="Problem with non-terminal outcomes") ), ( games.create_kuhn_poker_efg(), @@ -669,7 +657,7 @@ def test_lp_behavior_double(): [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], ), - pytest.param( + ( games.create_kuhn_poker_efg(nonterm_outcomes=True), [ [ @@ -682,7 +670,6 @@ def test_lp_behavior_double(): ], [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], - marks=pytest.mark.xfail(reason="Problem with non-terminal outcomes") ), ( games.create_seq_form_STOC_paper_zero_sum_2_player_efg(), @@ -702,13 +689,12 @@ def test_lp_behavior_double(): [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], ] ), - pytest.param( + ( games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], ], - marks=pytest.mark.xfail(reason="Problem with internal and missing outcomes") ), ( games.create_large_payoff_game_efg(), @@ -725,13 +711,12 @@ def test_lp_behavior_double(): [[1, 0], ["6/11", "5/11"]] ], ), - pytest.param( + ( games.create_chance_in_middle_efg(nonterm_outcomes=True), [ [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]] ], - marks=pytest.mark.xfail(reason="Problem with internal outcomes") ), ], ) From 456cd07fb552a91ca722e3c78ec02c83471eb6e3 Mon Sep 17 00:00:00 2001 From: StephenPasteris Date: Thu, 18 Dec 2025 17:46:41 +0000 Subject: [PATCH 18/18] added back xfail markers --- tests/test_nash.py | 49 ++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/tests/test_nash.py b/tests/test_nash.py index b4e1f69e7d..1f3e072813 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -418,20 +418,23 @@ def test_lcp_behavior_double(): games.create_2x2_zero_sum_efg(), [[["1/2", "1/2"]], [["1/2", "1/2"]]] ), - ( + pytest.param( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), (games.create_matching_pennies_efg(), [[["1/2", "1/2"]], [["1/2", "1/2"]]]), - ( + pytest.param( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), (games.create_stripped_down_poker_efg(), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]]), - ( + pytest.param( games.create_stripped_down_poker_efg(nonterm_outcomes=True), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( games.create_kuhn_poker_efg(), @@ -447,7 +450,7 @@ def test_lcp_behavior_double(): [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], ), - ( + pytest.param( games.create_kuhn_poker_efg(nonterm_outcomes=True), [ [ @@ -460,13 +463,10 @@ def test_lcp_behavior_double(): ], [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), # In the next test case: # 1/2-1/2 for l/r is determined by MixedBehaviorProfile.UndefinedToCentroid() - ( - games.create_two_player_perfect_info_win_lose_efg(), - [[[0, 1], [1, 0]], [[0, 1], ["1/2", "1/2"]]], - ), ( games.create_perfect_info_with_chance_efg(), [[[0, 1]], [[0, 1], [0, 1]]], @@ -486,12 +486,13 @@ def test_lcp_behavior_double(): [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], ] ), - ( + pytest.param( games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["1/3", "2/3"], ["1/3", "2/3"]], ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( games.create_large_payoff_game_efg(), @@ -508,12 +509,13 @@ def test_lcp_behavior_double(): [[1, 0], ["6/11", "5/11"]] ] ), - ( + pytest.param( games.create_chance_in_middle_efg(nonterm_outcomes=True), [ [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]] ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), # Non-zero-sum games ( @@ -536,17 +538,19 @@ def test_lcp_behavior_double(): games.create_entry_accomodation_efg(), [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]] ), - ( + pytest.param( games.create_entry_accomodation_efg(nonterm_outcomes=True), [[["2/3", "1/3"], [1, 0], [1, 0]], [["2/3", "1/3"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( games.create_non_zero_sum_lacking_outcome_efg(), [[["1/3", "2/3"]], [["1/2", "1/2"]]] ), - ( + pytest.param( games.create_non_zero_sum_lacking_outcome_efg(missing_term_outcome=True), [[["1/3", "2/3"]], [["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ], ) @@ -624,31 +628,35 @@ def test_lp_behavior_double(): games.create_two_player_perfect_info_win_lose_efg(), [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], ), - ( + pytest.param( games.create_two_player_perfect_info_win_lose_efg(nonterm_outcomes=True), [[[0, 1], [1, 0]], [[1, 0], [1, 0]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( games.create_2x2_zero_sum_efg(missing_term_outcome=False), [[["1/2", "1/2"]], [["1/2", "1/2"]]] ), - ( + pytest.param( games.create_2x2_zero_sum_efg(missing_term_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), (games.create_matching_pennies_efg(with_neutral_outcome=False), [[["1/2", "1/2"]], [["1/2", "1/2"]]]), - ( + pytest.param( games.create_matching_pennies_efg(with_neutral_outcome=True), [[["1/2", "1/2"]], [["1/2", "1/2"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( games.create_stripped_down_poker_efg(), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], ), - ( + pytest.param( games.create_stripped_down_poker_efg(nonterm_outcomes=True), [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( games.create_kuhn_poker_efg(), @@ -657,7 +665,7 @@ def test_lp_behavior_double(): [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], ), - ( + pytest.param( games.create_kuhn_poker_efg(nonterm_outcomes=True), [ [ @@ -670,6 +678,7 @@ def test_lp_behavior_double(): ], [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]], ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( games.create_seq_form_STOC_paper_zero_sum_2_player_efg(), @@ -689,12 +698,13 @@ def test_lp_behavior_double(): [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], ] ), - ( + pytest.param( games.create_three_action_internal_outcomes_efg(nonterm_outcomes=True), [ [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]], [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]], ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ( games.create_large_payoff_game_efg(), @@ -711,12 +721,13 @@ def test_lp_behavior_double(): [[1, 0], ["6/11", "5/11"]] ], ), - ( + pytest.param( games.create_chance_in_middle_efg(nonterm_outcomes=True), [ [["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]] ], + marks=pytest.mark.xfail(reason="Problem with non-standard outcomes") ), ], )