Skip to content

Commit dc66cce

Browse files
committed
test_nash_behavior_solver wip
1 parent 8b84d0f commit dc66cce

1 file changed

Lines changed: 165 additions & 114 deletions

File tree

tests/test_nash.py

Lines changed: 165 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class EquilibriumTestCase:
3939
prob_tol: float | gbt.Rational = Q(0)
4040

4141

42+
##################################################################################################
43+
# NASH SOLVER IN MIXED STRATEGIES
44+
##################################################################################################
45+
4246
ENUMPURE_CASES = [
4347
# Zero-sum games
4448
pytest.param(
@@ -248,6 +252,153 @@ def test_nash_strategy_solver(test_case: EquilibriumTestCase, subtests) -> None:
248252
for strategy in player.strategies:
249253
assert abs(eq[strategy] - expected[strategy]) <= test_case.prob_tol
250254

255+
##################################################################################################
256+
# NASH SOLVER IN MIXED BEHAVIORS
257+
##################################################################################################
258+
259+
# games.read_from_file("two_player_perfect_info_win_lose.efg"),
260+
# [[[0, 1], [1, 0]], [[1, 0], [1, 0]]],
261+
262+
# games.read_from_file("two_player_perfect_info_win_lose_with_nonterm_outcomes.efg"),
263+
# [[[0, 1], [1, 0]], [[1, 0], [1, 0]]],
264+
265+
# games.create_2x2_zero_sum_efg(missing_term_outcome=False),
266+
# [[["1/2", "1/2"]], [["1/2", "1/2"]]],
267+
268+
# games.create_2x2_zero_sum_efg(missing_term_outcome=True),
269+
# [[["1/2", "1/2"]], [["1/2", "1/2"]]],
270+
271+
# games.create_matching_pennies_efg(with_neutral_outcome=False),
272+
# [[["1/2", "1/2"]], [["1/2", "1/2"]]],
273+
274+
# games.create_matching_pennies_efg(with_neutral_outcome=True),
275+
# [[["1/2", "1/2"]], [["1/2", "1/2"]]],
276+
277+
# games.create_stripped_down_poker_efg(),
278+
# [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]],
279+
280+
# games.create_stripped_down_poker_efg(nonterm_outcomes=True),
281+
# [[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]],
282+
283+
# games.create_kuhn_poker_efg(),
284+
# [
285+
# [[1, 0], [1, 0], [1, 0], ["2/3", "1/3"], [1, 0], [0, 1]],
286+
# [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]],
287+
# ],
288+
289+
# games.create_kuhn_poker_efg(nonterm_outcomes=True),
290+
# [
291+
# [
292+
# [1, 0],
293+
# [1, 0],
294+
# [1, 0],
295+
# ["2/3", "1/3"],
296+
# [1, 0],
297+
# [0, 1],
298+
# ],
299+
# [[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]],
300+
# ],
301+
302+
# games.read_from_file("zerosum_efg_from_sequence_form_STOC94_paper.efg"),
303+
# [
304+
# [[0, 1], ["2/3", "1/3"], ["1/3", "2/3"]],
305+
# [["5/6", "1/6"], ["5/9", "4/9"]],
306+
# ],
307+
308+
# games.read_from_file("perfect_info_with_chance.efg"),
309+
# [[[0, 1]], [[1, 0], [1, 0]]],
310+
311+
# games.read_from_file("2_player_chance.efg"),
312+
# [
313+
# [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]],
314+
# [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]],
315+
# ],
316+
317+
# games.read_from_file("2_player_chance_nonterm_outcomes_and_missing_term_outcomes.efg"),
318+
# [
319+
# [["1/3", 0, "2/3"], ["2/3", 0, "1/3"]],
320+
# [["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]],
321+
# ],
322+
323+
# games.read_from_file("large_payoff_game.efg"),
324+
# [
325+
# [[1, 0], [1, 0]],
326+
# [[0, 1], ["9999999999999999999/10000000000000000000", "1/10000000000000000000"]],
327+
# ],
328+
329+
# games.read_from_file("chance_in_middle.efg"),
330+
# [[["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]]],
331+
332+
# games.read_from_file("chance_in_middle_with_nonterm_outcomes.efg"),
333+
# [[["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]]],
334+
335+
336+
LP_BEHAVIOR_RATIONAL_CASES = [
337+
pytest.param(
338+
EquilibriumTestCase(
339+
factory=functools.partial(
340+
games.read_from_file, "chance_in_middle_with_nonterm_outcomes.efg"
341+
),
342+
solver=gbt.nash.lp_solve,
343+
expected=[
344+
[
345+
[d("3/11", "8/11"), d(1, 0), d(1, 0), d(1, 0), d(1, 0)],
346+
[d(1, 0), d("6/11", "5/11")]
347+
]
348+
],
349+
),
350+
marks=pytest.mark.nash_enumpure_strategy,
351+
id="test1_TODO",
352+
),
353+
]
354+
355+
CASES = []
356+
CASES += LP_BEHAVIOR_RATIONAL_CASES
357+
358+
359+
@pytest.mark.nash
360+
@pytest.mark.parametrize("test_case", CASES, ids=lambda c: c.label)
361+
def test_nash_behavior_solver(test_case: EquilibriumTestCase, subtests) -> None:
362+
"""Test calls of Nash solvers in mixed behaviors
363+
364+
Subtests:
365+
- Max regret no more than `test_case.regret_tol`
366+
- Agent max regret no more than max regret (+ `test_case.regret_tol`)
367+
- Equilibria are output in the expected order. Equilibria are deemed to match if the maximum
368+
difference in probabilities is no more than `test_case.prob_tol`
369+
"""
370+
game = test_case.factory()
371+
result = test_case.solver(game)
372+
with subtests.test("number of equilibria found"):
373+
assert len(result.equilibria) == len(test_case.expected)
374+
for i, (eq, exp) in enumerate(zip(result.equilibria, test_case.expected, strict=True)):
375+
with subtests.test(eq=i, check="max_regret"):
376+
assert eq.max_regret() <= test_case.regret_tol
377+
with subtests.test(eq=i, check="max_regret"):
378+
assert eq.agent_max_regret() <= eq.max_regret() + test_case.regret_tol
379+
with subtests.test(eq=i, check="strategy_profile"):
380+
expected = game.mixed_behavior_profile(rational=True, data=exp)
381+
for player in game.players:
382+
for action in player.actions:
383+
assert abs(eq[action] - expected[action]) <= test_case.prob_tol
384+
385+
# def test_lp_behavior_rational(game: gbt.Game, mixed_behav_prof_data: list):
386+
# """Test calls of LP for mixed behavior equilibria, rational precision,
387+
# using max_regret and agent_max_regret (internal consistency); and
388+
# comparison to a previously computed equilibrium using this function (regression test).
389+
# """
390+
# result = gbt.nash.lp_solve(game, use_strategic=False, rational=True)
391+
# assert len(result.equilibria) == 1
392+
# eq = result.equilibria[0]
393+
# assert eq.max_regret() == 0
394+
# assert eq.agent_max_regret() == 0
395+
# expected = game.mixed_behavior_profile(rational=True, data=mixed_behav_prof_data)
396+
# assert eq == expected
397+
398+
##################################################################################################
399+
# AGENTS NASH SOLVERS (IN MIXED BEHAVIORS
400+
##################################################################################################
401+
251402

252403
ENUMPURE_AGENT_CASES = [
253404
# #############################################################
@@ -515,6 +666,7 @@ def test_nash_strategy_solver(test_case: EquilibriumTestCase, subtests) -> None:
515666
# ),
516667
# ##############################################################################
517668

669+
518670
AGENT_CASES = []
519671
AGENT_CASES += ENUMPURE_AGENT_CASES
520672
AGENT_CASES += ENUMPOLY_AGENT_CASES
@@ -547,6 +699,10 @@ def test_nash_agent_solver(test_case: EquilibriumTestCase, subtests) -> None:
547699
assert abs(eq[action] - expected[action]) <= test_case.prob_tol
548700

549701

702+
##################################################################################################
703+
# TEMP FOR ISSUE 660
704+
##################################################################################################
705+
550706
ENUMPOLY_ISSUE_660_CASES = [
551707
# 2-player non-zero-sum games
552708
pytest.param(
@@ -607,6 +763,10 @@ def test_nash_agent_solver_no_subtests_only_profile(test_case: EquilibriumTestCa
607763
assert abs(eq[action] - expected[action]) <= test_case.prob_tol
608764

609765

766+
##################################################################################################
767+
# AGENT UNORDERED
768+
##################################################################################################
769+
610770
ENUMPOLY_AGENT_UNORDERED_CASES = [
611771
pytest.param(
612772
EquilibriumTestCase(
@@ -671,6 +831,11 @@ def are_the_same(game, found, candidate):
671831
assert found
672832

673833

834+
##################################################################################################
835+
# STILL TODO........
836+
##################################################################################################
837+
838+
674839
def test_lcp_strategy_double():
675840
"""Test calls of LCP for mixed strategy equilibria, floating-point."""
676841
game = games.read_from_file("stripped_down_poker.efg")
@@ -958,120 +1123,6 @@ def test_lp_behavior_double():
9581123
# For floating-point results are not exact, so we skip testing exact values for now
9591124

9601125

961-
@pytest.mark.nash
962-
@pytest.mark.nash_lp_behavior
963-
@pytest.mark.parametrize(
964-
"game,mixed_behav_prof_data",
965-
[
966-
(
967-
games.read_from_file("two_player_perfect_info_win_lose.efg"),
968-
[[[0, 1], [1, 0]], [[1, 0], [1, 0]]],
969-
),
970-
(
971-
games.read_from_file("two_player_perfect_info_win_lose_with_nonterm_outcomes.efg"),
972-
[[[0, 1], [1, 0]], [[1, 0], [1, 0]]],
973-
),
974-
(
975-
games.create_2x2_zero_sum_efg(missing_term_outcome=False),
976-
[[["1/2", "1/2"]], [["1/2", "1/2"]]],
977-
),
978-
(
979-
games.create_2x2_zero_sum_efg(missing_term_outcome=True),
980-
[[["1/2", "1/2"]], [["1/2", "1/2"]]],
981-
),
982-
(
983-
games.create_matching_pennies_efg(with_neutral_outcome=False),
984-
[[["1/2", "1/2"]], [["1/2", "1/2"]]],
985-
),
986-
(
987-
games.create_matching_pennies_efg(with_neutral_outcome=True),
988-
[[["1/2", "1/2"]], [["1/2", "1/2"]]],
989-
),
990-
(
991-
games.create_stripped_down_poker_efg(),
992-
[[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]],
993-
),
994-
(
995-
games.create_stripped_down_poker_efg(nonterm_outcomes=True),
996-
[[[1, 0], ["1/3", "2/3"]], [["2/3", "1/3"]]],
997-
),
998-
(
999-
games.create_kuhn_poker_efg(),
1000-
[
1001-
[[1, 0], [1, 0], [1, 0], ["2/3", "1/3"], [1, 0], [0, 1]],
1002-
[[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]],
1003-
],
1004-
),
1005-
(
1006-
games.create_kuhn_poker_efg(nonterm_outcomes=True),
1007-
[
1008-
[
1009-
[1, 0],
1010-
[1, 0],
1011-
[1, 0],
1012-
["2/3", "1/3"],
1013-
[1, 0],
1014-
[0, 1],
1015-
],
1016-
[[1, 0], ["2/3", "1/3"], [0, 1], [0, 1], ["2/3", "1/3"], [1, 0]],
1017-
],
1018-
),
1019-
(
1020-
games.read_from_file("zerosum_efg_from_sequence_form_STOC94_paper.efg"),
1021-
[
1022-
[[0, 1], ["2/3", "1/3"], ["1/3", "2/3"]],
1023-
[["5/6", "1/6"], ["5/9", "4/9"]],
1024-
],
1025-
),
1026-
(
1027-
games.read_from_file("perfect_info_with_chance.efg"),
1028-
[[[0, 1]], [[1, 0], [1, 0]]],
1029-
),
1030-
(
1031-
games.read_from_file("2_player_chance.efg"),
1032-
[
1033-
[["1/3", 0, "2/3"], ["2/3", 0, "1/3"]],
1034-
[["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]],
1035-
],
1036-
),
1037-
(
1038-
games.read_from_file("2_player_chance_nonterm_outcomes_and_missing_term_outcomes.efg"),
1039-
[
1040-
[["1/3", 0, "2/3"], ["2/3", 0, "1/3"]],
1041-
[["2/3", "1/3"], ["2/3", "1/3"], ["1/3", "2/3"]],
1042-
],
1043-
),
1044-
(
1045-
games.read_from_file("large_payoff_game.efg"),
1046-
[
1047-
[[1, 0], [1, 0]],
1048-
[[0, 1], ["9999999999999999999/10000000000000000000", "1/10000000000000000000"]],
1049-
],
1050-
),
1051-
(
1052-
games.read_from_file("chance_in_middle.efg"),
1053-
[[["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]]],
1054-
),
1055-
(
1056-
games.read_from_file("chance_in_middle_with_nonterm_outcomes.efg"),
1057-
[[["3/11", "8/11"], [1, 0], [1, 0], [1, 0], [1, 0]], [[1, 0], ["6/11", "5/11"]]],
1058-
),
1059-
],
1060-
)
1061-
def test_lp_behavior_rational(game: gbt.Game, mixed_behav_prof_data: list):
1062-
"""Test calls of LP for mixed behavior equilibria, rational precision,
1063-
using max_regret and agent_max_regret (internal consistency); and
1064-
comparison to a previously computed equilibrium using this function (regression test).
1065-
"""
1066-
result = gbt.nash.lp_solve(game, use_strategic=False, rational=True)
1067-
assert len(result.equilibria) == 1
1068-
eq = result.equilibria[0]
1069-
assert eq.max_regret() == 0
1070-
assert eq.agent_max_regret() == 0
1071-
expected = game.mixed_behavior_profile(rational=True, data=mixed_behav_prof_data)
1072-
assert eq == expected
1073-
1074-
10751126
def test_liap_strategy():
10761127
"""Test calls of liap for mixed strategy equilibria."""
10771128
game = games.read_from_file("stripped_down_poker.efg")

0 commit comments

Comments
 (0)