@@ -607,15 +607,12 @@ def test_nash_agent_solver_no_subtests_only_profile(test_case: EquilibriumTestCa
607607 assert abs (eq [action ] - expected [action ]) <= test_case .prob_tol
608608
609609
610- @pytest .mark .nash
611- @pytest .mark .nash_enumpoly_behavior
612- @pytest .mark .parametrize (
613- "game,mixed_behav_prof_data,stop_after" ,
614- [
615- # 3-player game
616- (
617- games .read_from_file ("mixed_behavior_game.efg" ),
618- [
610+ ENUMPOLY_AGENT_UNORDERED_CASES = [
611+ pytest .param (
612+ EquilibriumTestCase (
613+ factory = functools .partial (games .read_from_file , "mixed_behavior_game.efg" ),
614+ solver = functools .partial (gbt .nash .enumpoly_solve , stop_after = 9 ),
615+ expected = [
619616 [[["2/5" , "3/5" ]], [["1/2" , "1/2" ]], [["1/3" , "2/3" ]]],
620617 [[["1/2" , "1/2" ]], [["2/5" , "3/5" ]], [["1/4" , "3/4" ]]],
621618 [[["1/2" , "1/2" ]], [["1/2" , "1/2" ]], [[1 , 0 ]]],
@@ -626,61 +623,52 @@ def test_nash_agent_solver_no_subtests_only_profile(test_case: EquilibriumTestCa
626623 [[[0 , 1 ]], [[1 , 0 ]], [[0 , 1 ]]],
627624 [[[0 , 1 ]], [[0 , 1 ]], [[1 , 0 ]]],
628625 ],
629- 9 ,
626+ regret_tol = TOL ,
627+ prob_tol = TOL ,
630628 ),
631- ],
632- )
633- # This is the "ordered" version where we test for the outputs coming in a specific
634- # order; there is also an "unordered" version. The game 2x2x2.nfg, for example,
635- # has a point at which the Jacobian is singular. As a result, the order in which it
636- # returns the two totally-mixed equilbria is system-dependent due, essentially,
637- # to inherent numerical instability near that point.
638- def test_enumpoly_unordered_behavior (
639- game : gbt .Game , mixed_behav_prof_data : list , stop_after : None | int
640- ):
641- """Test calls of enumpoly for mixed behavior equilibria,
642- using max_regret and agent_max_regret (internal consistency); and
643- comparison to a set of previously computed equilibria using this function (regression test).
629+ marks = pytest .mark .nash_enumpoly_behavior ,
630+ id = "test5_TODO" ,
631+ ),
632+ ]
644633
645- This set will be the full set of all computed equilibria if stop_after is None,
646- else the first stop_after-many equilibria.
647634
648- This is the "unordered" version where we test for the outputs belong to a set
649- of expected output; there is also an "unordered" that expects the outputs in a specific order.
635+ @pytest .mark .nash
636+ @pytest .mark .parametrize ("test_case" , ENUMPOLY_AGENT_UNORDERED_CASES , ids = lambda c : c .label )
637+ def test_nash_agent_solver_unordered (test_case : EquilibriumTestCase , subtests ) -> None :
638+ """Test calls of Nash solvers in EFGs using "agent" versions.
650639
651- In this unordered version, once something from the expected set is found it is removed,
652- so we are checking for no duplicate outputs.
640+ Subtests:
641+ - Agent max regret no more than `test_case.regret_tol`
642+ - Agent max regret no more than max regret (+ `test_case.regret_tol`)
643+ - Equilibria that are output are distinct and all appear in the expected set
644+ Equilibria are deemed to match if the maximum difference in probabilities is no more
645+ than `test_case.prob_tol`
653646 """
654- if stop_after :
655- result = gbt .nash .enumpoly_solve (
656- game , use_strategic = False , stop_after = stop_after , maxregret = 0.00001
657- )
658- assert len (result .equilibria ) == stop_after
659- else :
660- # compute all
661- result = gbt .nash .enumpoly_solve (game , use_strategic = False )
662-
663- assert len (result .equilibria ) == len (mixed_behav_prof_data )
664-
665647 def are_the_same (game , found , candidate ):
666648 for p in game .players :
667- for i in p .infosets :
668- for a in i .actions :
669- if not abs (found [p ][i ][a ] - candidate [p ][i ][a ]) <= TOL :
670- return False
649+ for a in p .actions :
650+ if not abs (found [a ] - candidate [a ]) <= TOL :
651+ return False
671652 return True
672653
673- for eq in result .equilibria :
674- assert abs (eq .max_regret ()) <= TOL
675- assert abs (eq .agent_max_regret ()) <= TOL
676- found = False
677- for exp in mixed_behav_prof_data [:]:
678- expected = game .mixed_behavior_profile (rational = True , data = exp )
679- if are_the_same (game , eq , expected ):
680- mixed_behav_prof_data .remove (exp )
681- found = True
682- break
683- assert found
654+ game = test_case .factory ()
655+ result = test_case .solver (game )
656+ with subtests .test ("number of equilibria found" ):
657+ assert len (result .equilibria ) == len (test_case .expected )
658+ for i , eq in enumerate (result .equilibria ):
659+ with subtests .test (eq = i , check = "agent_max_regret" ):
660+ assert eq .agent_max_regret () <= test_case .regret_tol
661+ with subtests .test (eq = i , check = "max_regret" ):
662+ assert eq .agent_max_regret () <= eq .max_regret () + test_case .regret_tol
663+ with subtests .test (eq = i , check = "strategy_profile" ):
664+ found = False
665+ for exp in test_case .expected [:]:
666+ expected = game .mixed_behavior_profile (rational = True , data = exp )
667+ if are_the_same (game , eq , expected ):
668+ test_case .expected .remove (exp )
669+ found = True
670+ break
671+ assert found
684672
685673
686674def test_lcp_strategy_double ():
0 commit comments