diff --git a/pyproject.toml b/pyproject.toml index 1b36d50ef..e2e4e55f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,6 +102,7 @@ markers = [ "nash_enumpure_strategy: tests of enumpure_solve in pure strategies", "nash_enumpure_agent: tests of enumpure_solve in pure behaviors", "nash_enummixed_strategy: tests of enummixed_solve in mixed strategies", + "nash_enumpoly_strategy: tests of enumpoly_solve in mixed strategies", "nash_enumpoly_behavior: tests of enumpoly_solve in mixed behaviors", "nash_lcp_strategy: tests of lcp_solve in mixed strategies", "nash_lcp_behavior: tests of lcp_solve in mixed behaviors", diff --git a/tests/test_games/2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg b/tests/test_games/2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg index f28f234f6..f1958f904 100644 --- a/tests/test_games/2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg +++ b/tests/test_games/2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg @@ -6,6 +6,7 @@ NFG 1 R "2x2x2 game with 2 pure and 1 mixed equilibrium" - Pure strategies {a,b} encode if respective player is on left or right of the cut - The payoff to a player is the sum of their incident edges across the implied cut - Pure equilibrium iff local max cuts; in addition, uniform mixture is an equilibrium +- In the mixed equilibrium all players mix strategies with equal probability (0.5, 0.5) - Equilibrium analysis for pure profiles: a a a: 0 0 0 -- Not Nash (regrets: 1, 4, 1) b a a: 1 2 -1 -- Not Nash (regrets: 0, 0, 3) diff --git a/tests/test_nash.py b/tests/test_nash.py index a3b171d0f..6b105abd5 100644 --- a/tests/test_nash.py +++ b/tests/test_nash.py @@ -411,6 +411,82 @@ class QREquilibriumTestCase: ] +ENUMPOLY_STRATEGY_CASES = [ + # 2x2x2 strategic form game based on local max cut -- 2 pure and 1 mixed + pytest.param( + EquilibriumTestCase( + factory=functools.partial( + games.read_from_file, "2x2x2_nfg_from_local_max_cut_2_pure_1_mixed_eq.nfg" + ), + solver=functools.partial(gbt.nash.enumpoly_solve, stop_after=None), + expected=[ + [d(1, 0), d(0, 1), d(1, 0)], + [d(0, 1), d(1, 0), d(0, 1)], + [d("1/2", "1/2"), d("1/2", "1/2"), d("1/2", "1/2")], + ], + prob_tol=TOL, + regret_tol=TOL, + ), + marks=pytest.mark.nash_enumpoly_strategy, + id="test_enumpoly_strategy_1", + ), + # coordination game with 3 pure and 4 mixed equilibria + pytest.param( + EquilibriumTestCase( + factory=functools.partial(games.create_EFG_for_nxn_bimatrix_coordination_game, n=3), + solver=functools.partial(gbt.nash.enumpoly_solve, stop_after=None, use_strategic=True), + expected=[ + [d(1, 0, 0), d(1, 0, 0)], + [d(0, 1, 0), d(0, 1, 0)], + [d(0, 0, 1), d(0, 0, 1)], + [d("1/2", "1/2", 0), d("1/2", "1/2", 0)], + [d("1/2", 0, "1/2"), d("1/2", 0, "1/2")], + [d(0, "1/2", "1/2"), d(0, "1/2", "1/2")], + [d("1/3", "1/3", "1/3"), d("1/3", "1/3", "1/3")], + ], + prob_tol=TOL, + regret_tol=TOL, + ), + marks=pytest.mark.nash_enumpoly_strategy, + id="test_enumpoly_strategy_2", + ), + # A three-player game with a unique Nash equilibrium in irrational mixed strategies + # (nau2004 sec4 catalog game) + pytest.param( + EquilibriumTestCase( + factory=functools.partial(gbt.catalog.load, "journals/ijgt/nau2004/sec4"), + solver=functools.partial(gbt.nash.enumpoly_solve, stop_after=None), + expected=[ + [d(0.6192325794725537, 0.3807674205274463), + d(0.4798042226776053, 0.5201957773223946), + d(0.3788253360656313, 0.6211746639343687)], + ], + prob_tol=TOL, + regret_tol=TOL, + ), + marks=pytest.mark.nash_enumpoly_strategy, + id="test_enumpoly_strategy_3", + ), + # A three-player 2x2x2 game with 3 pure, 2 incompletely mixed, and a continuum of + # completely mixed Nash equilibria (nau2004 sec5 catalog game) + pytest.param( + EquilibriumTestCase( + factory=functools.partial(gbt.catalog.load, "journals/ijgt/nau2004/sec5"), + solver=functools.partial(gbt.nash.enumpoly_solve, stop_after=None), + expected=[ + [d(1, 0), d(0, 1), d(1, 0)], + [d(0, 1), d(1, 0), d(1, 0)], + [d(0, 1), d(0, 1), d(0, 1)], + ], + prob_tol=TOL, + regret_tol=TOL, + ), + marks=pytest.mark.nash_enumpoly_strategy, + id="test_enumpoly_strategy_4", + ), +] + + LP_STRATEGY_RATIONAL_CASES = [ pytest.param( EquilibriumTestCase( @@ -945,6 +1021,7 @@ class QREquilibriumTestCase: CASES += ENUMPURE_CASES CASES += ENUMMIXED_RATIONAL_CASES CASES += ENUMMIXED_DOUBLE_CASES +CASES += ENUMPOLY_STRATEGY_CASES CASES += LP_STRATEGY_RATIONAL_CASES CASES += LP_STRATEGY_DOUBLE_CASES CASES += LCP_STRATEGY_RATIONAL_CASES