@@ -42,6 +42,19 @@ class EquilibriumTestCase:
4242 prob_tol : float | gbt .Rational = Q (0 )
4343
4444
45+ @dataclasses .dataclass
46+ class EquilibriumTestCaseWithStart :
47+ """Summarising the data relevant for a test fixture of a call to an equilibrium solver
48+ that needs a starting profile."""
49+
50+ factory : typing .Callable [[], gbt .Game ]
51+ solver : typing .Callable [[gbt .Game ], gbt .nash .NashComputationResult ]
52+ start_data : None | list
53+ expected : list
54+ regret_tol : float | gbt .Rational = Q (0 )
55+ prob_tol : float | gbt .Rational = Q (0 )
56+
57+
4558@dataclasses .dataclass
4659class QREquilibriumTestCase :
4760 """Summarising the data relevant for a test fixture of a call to an QRE solver."""
@@ -831,6 +844,76 @@ def test_nash_strategy_solver(test_case: EquilibriumTestCase, subtests) -> None:
831844 assert abs (eq [strategy ] - expected [strategy ]) <= test_case .prob_tol
832845
833846
847+ ##################################################################################################
848+ # NASH SOLVERS WITH START PROFILES
849+ ##################################################################################################
850+
851+ LIAP_STRATEGY_CASES = [
852+ pytest .param (
853+ EquilibriumTestCaseWithStart (
854+ factory = functools .partial (games .read_from_file , "stripped_down_poker.efg" ),
855+ solver = gbt .nash .liap_solve ,
856+ start_data = dict (data = None , rational = False ),
857+ expected = [],
858+ regret_tol = TOL_LARGE ,
859+ prob_tol = TOL ,
860+ ),
861+ marks = pytest .mark .nash_simpdiv ,
862+ id = "test_simpdiv_1" ,
863+ ),
864+ ]
865+
866+
867+ SIMPDIV_CASES = [
868+ pytest .param (
869+ EquilibriumTestCaseWithStart (
870+ factory = functools .partial (games .read_from_file , "stripped_down_poker.efg" ),
871+ solver = functools .partial (gbt .nash .simpdiv_solve ),
872+ start_data = dict (data = None , rational = True ),
873+ expected = [
874+ [
875+ d (Q (174763 , 524288 ), Q (349525 , 524288 ), 0 , 0 ),
876+ d (Q (699051 , 1048576 ), Q (349525 , 1048576 )),
877+ ]
878+ ],
879+ regret_tol = TOL_LARGE ,
880+ prob_tol = TOL ,
881+ ),
882+ marks = pytest .mark .nash_simpdiv ,
883+ id = "test_simpdiv_1" ,
884+ ),
885+ ]
886+
887+ CASES = []
888+ CASES += LIAP_STRATEGY_CASES
889+ CASES += SIMPDIV_CASES
890+
891+
892+ @pytest .mark .nash
893+ @pytest .mark .parametrize ("test_case" , CASES , ids = lambda c : c .label )
894+ def test_nash_strategy_solver_w_start (test_case : EquilibriumTestCaseWithStart , subtests ) -> None :
895+ """Test calls of Nash solvers that start a starting profile.
896+
897+ Subtests:
898+ - Max regret no more than `test_case.regret_tol`
899+ - Equilibria are output in the expected order. Equilibria are deemed to match if the maximum
900+ difference in probabilities is no more than `test_case.prob_tol`
901+ """
902+ game = test_case .factory ()
903+ start = game .mixed_strategy_profile (** test_case .start_data )
904+ result = test_case .solver (start )
905+ with subtests .test ("number of equilibria found" ):
906+ assert len (result .equilibria ) == len (test_case .expected )
907+ for i , (eq , exp ) in enumerate (zip (result .equilibria , test_case .expected , strict = True )):
908+ with subtests .test (eq = i , check = "max_regret" ):
909+ assert eq .max_regret () <= test_case .regret_tol
910+ with subtests .test (eq = i , check = "strategy_profile" ):
911+ expected = game .mixed_strategy_profile (rational = True , data = exp )
912+ for player in game .players :
913+ for strategy in player .strategies :
914+ assert abs (eq [strategy ] - expected [strategy ]) <= test_case .prob_tol
915+
916+
834917##################################################################################################
835918# NASH SOLVER IN MIXED BEHAVIORS
836919##################################################################################################
@@ -2375,8 +2458,6 @@ def test_nash_agent_solver(test_case: EquilibriumTestCase, subtests) -> None:
23752458
23762459##################################################################################################
23772460# TODO:
2378- # The below all take a start argument that depends on the game, which doesn't immediately
2379- # work with our current implementation of EquilibriumTestClass
23802461##################################################################################################
23812462
23822463
@@ -2396,27 +2477,6 @@ def test_liap_agent():
23962477 assert abs (eq [action ] - exp [action ]) <= TOL_LARGE
23972478
23982479
2399- def test_liap_strategy ():
2400- """Test calls of liap for mixed strategy equilibria."""
2401- game = games .read_from_file ("stripped_down_poker.efg" )
2402- _ = gbt .nash .liap_solve (game .mixed_strategy_profile ())
2403-
2404- # NashComputationResult(method='liap', rational=False, use_strategic=True, equilibria=[],
2405- # parameters={'start': [[0.25, 0.25, 0.25, 0.25], [0.5, 0.5]],
2406- # 'maxregret': 0.0001, 'maxiter': 1000})
2407- # Nothing found!
2408-
2409-
2410- def test_simpdiv_strategy ():
2411- """Test calls of simplicial subdivision for mixed strategy equilibria."""
2412- game = games .read_from_file ("stripped_down_poker.efg" )
2413- result = gbt .nash .simpdiv_solve (game .mixed_strategy_profile (rational = True ))
2414- assert len (result .equilibria ) == 1
2415-
2416- # [[[Rational(174763, 524288), Rational(349525, 524288), Rational(0, 1), Rational(0, 1)],
2417- # [Rational(699051, 1048576), Rational(349525, 1048576)]]]
2418-
2419-
24202480##################################################################################################
24212481# QRE solvers
24222482##################################################################################################
0 commit comments