@@ -95,42 +95,118 @@ def test_enummixed_rational():
9595 [[[["1/4" , "1/4" , "1/4" , "1/4" ]], [["1/4" , "1/4" , "1/4" , "1/4" ]]]],
9696 1 ,
9797 ),
98+ # 3-player game
99+ # (
100+ # games.create_mixed_behav_game_efg(),
101+ # [
102+ # [[["1/2", "1/2"]], [["2/5", "3/5"]], [["1/4", "3/4"]]],
103+ # [[["2/5", "3/5"]], [["1/2", "1/2"]], [["1/3", "2/3"]]],
104+ # ],
105+ # 2, # 9 in total found by enumpoly (see unordered test)
106+ # ),
107+ ],
108+ )
109+ def test_enumpoly_ordered_behavior (
110+ game : gbt .Game , mixed_behav_prof_data : list , stop_after : typing .Union [None , int ]
111+ ):
112+ """Test calls of enumpoly for mixed behavior equilibria,
113+ using max_regret (internal consistency); and comparison to a set of previously
114+ computed equilibria using this function (regression test).
115+ This set will be the full set of all computed equilibria if stop_after is None,
116+ else the first stop_after-many equilibria.
117+
118+ This is the "ordered" version where we test for the outputs coming in a specific
119+ order; there is also an "unordered" version. The game 2x2x2.nfg, for example,
120+ has a point at which the Jacobian is singular. As a result, the order in which it
121+ returns the two totally-mixed equilbria is system-dependent due, essentially,
122+ to inherent numerical instability near that point.
123+ """
124+ if stop_after :
125+ result = gbt .nash .enumpoly_solve (
126+ game , use_strategic = False , stop_after = stop_after , maxregret = 0.00001
127+ )
128+ assert len (result .equilibria ) == stop_after
129+ else :
130+ # compute all
131+ result = gbt .nash .enumpoly_solve (game , use_strategic = False )
132+ assert len (result .equilibria ) == len (mixed_behav_prof_data )
133+ for eq , exp in zip (result .equilibria , mixed_behav_prof_data ):
134+ assert abs (eq .max_regret ()) <= TOL
135+ expected = game .mixed_behavior_profile (rational = True , data = exp )
136+ for p in game .players :
137+ for i in p .infosets :
138+ for a in i .actions :
139+ assert abs (eq [p ][i ][a ] - expected [p ][i ][a ]) <= TOL
140+
141+
142+ @pytest .mark .nash
143+ @pytest .mark .nash_enumpoly_behavior
144+ @pytest .mark .parametrize (
145+ "game,mixed_behav_prof_data,stop_after" ,
146+ [
98147 # 3-player game
99148 (
100149 games .create_mixed_behav_game_efg (),
101150 [
102- [[["1/2" , "1/2" ]], [["2/5" , "3/5" ]], [["1/4" , "3/4" ]]],
103151 [[["2/5" , "3/5" ]], [["1/2" , "1/2" ]], [["1/3" , "2/3" ]]],
152+ [[["1/2" , "1/2" ]], [["2/5" , "3/5" ]], [["1/4" , "3/4" ]]],
153+ [[["1/2" , "1/2" ]], [["1/2" , "1/2" ]], [[1 , 0 ]]],
154+ [[["1/3" , "2/3" ]], [[1 , 0 ]], [["1/4" , "3/4" ]]],
155+ [[[1 , 0 ]], [[1 , 0 ]], [[1 , 0 ]]],
156+ [[[1 , 0 ]], [[0 , 1 ]], [[0 , 1 ]]],
157+ [[[0 , 1 ]], [["1/4" , "3/4" ]], [["1/3" , "2/3" ]]],
158+ [[[0 , 1 ]], [[1 , 0 ]], [[0 , 1 ]]],
159+ [[[0 , 1 ]], [[0 , 1 ]], [[1 , 0 ]]],
104160 ],
105- 2 , # 9 in total found by enumpoly
161+ 9 ,
106162 ),
107163 ],
108164)
109- def test_enumpoly_behavior_rational (
165+ def test_enumpoly_unordered_behavior (
110166 game : gbt .Game , mixed_behav_prof_data : list , stop_after : typing .Union [None , int ]
111167):
112- """Test calls of enumpoly for mixed behavior equilibria, rational precision,
168+ """Test calls of enumpoly for mixed behavior equilibria,
113169 using max_regret (internal consistency); and comparison to a set of previously
114170 computed equilibria using this function (regression test).
171+
115172 This set will be the full set of all computed equilibria if stop_after is None,
116173 else the first stop_after-many equilibria.
174+
175+ This is the "unordered" version where we test for the outputs belong to a set
176+ of expected output; there is also an "unordered" that expects the outputs in a specific order.
177+
178+ In this unordered version, once something from the expected set is found it is removed,
179+ so we are checking for no duplicate outputs.
117180 """
118181 if stop_after :
119182 result = gbt .nash .enumpoly_solve (
120- game , use_strategic = False , stop_after = stop_after
183+ game , use_strategic = False , stop_after = stop_after , maxregret = 0.00001
121184 )
122185 assert len (result .equilibria ) == stop_after
123186 else :
124187 # compute all
125188 result = gbt .nash .enumpoly_solve (game , use_strategic = False )
189+
126190 assert len (result .equilibria ) == len (mixed_behav_prof_data )
127- for eq , exp in zip (result .equilibria , mixed_behav_prof_data ):
128- assert abs (eq .max_regret ()) <= TOL
129- expected = game .mixed_behavior_profile (rational = True , data = exp )
191+
192+ def are_the_same (game , found , candidate ):
130193 for p in game .players :
131194 for i in p .infosets :
132195 for a in i .actions :
133- assert abs (eq [p ][i ][a ] - expected [p ][i ][a ]) <= TOL
196+ if not abs (found [p ][i ][a ] - candidate [p ][i ][a ]) <= TOL :
197+ return False
198+ return True
199+
200+ for eq in result .equilibria :
201+ assert abs (eq .max_regret ()) <= TOL
202+ found = False
203+ for exp in mixed_behav_prof_data [:]:
204+ expected = game .mixed_behavior_profile (rational = True , data = exp )
205+ if are_the_same (game , eq , expected ):
206+ mixed_behav_prof_data .remove (exp )
207+ found = True
208+ break
209+ assert found
134210
135211
136212def test_lcp_strategy_double ():
@@ -261,7 +337,6 @@ def test_lp_behavior_double():
261337
262338
263339@pytest .mark .nash
264- @pytest .mark .slow
265340@pytest .mark .nash_lp_behavior
266341@pytest .mark .parametrize (
267342 "game,mixed_behav_prof_data" ,
0 commit comments