From 11160a65913f9e24617d9fc426b80abae49007e8 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:14:55 +0330 Subject: [PATCH] Import tests: Shayan1/Prod/tests/test_solver Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- Shayan1/Prod/tests/test_solver.py | 145 ++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Shayan1/Prod/tests/test_solver.py diff --git a/Shayan1/Prod/tests/test_solver.py b/Shayan1/Prod/tests/test_solver.py new file mode 100644 index 0000000..a9f0ca8 --- /dev/null +++ b/Shayan1/Prod/tests/test_solver.py @@ -0,0 +1,145 @@ +import numpy as np +import pytest + +from nash import solver +from nash.game import Game + +PRICING = Game.load_from_json("games/pricing.json") +MATCHING_PENNIES = Game.load_from_json("games/matching_pennies.json") + + +def test_pricing_has_one_pure_equilibrium_low_low(): + equilibria = solver.find_pure_nash_equilibria(PRICING) + assert equilibria == [(1, 1)] + + +def test_matching_pennies_has_no_pure_equilibrium(): + equilibria = solver.find_pure_nash_equilibria(MATCHING_PENNIES) + assert equilibria == [] + + +def test_pricing_mixed_equilibrium_matches_pure_equilibrium(): + equilibria = solver.find_mixed_nash_equilibria(PRICING) + assert len(equilibria) == 1 + x, y = equilibria[0] + assert np.allclose(x, [0, 1], atol=1e-6) + assert np.allclose(y, [0, 1], atol=1e-6) + + +def test_matching_pennies_mixed_equilibrium_is_fifty_fifty(): + equilibria = solver.find_mixed_nash_equilibria(MATCHING_PENNIES) + assert len(equilibria) == 1 + x, y = equilibria[0] + assert np.allclose(x, [0.5, 0.5], atol=1e-6) + assert np.allclose(y, [0.5, 0.5], atol=1e-6) + + +def test_mixed_solver_rejects_more_than_two_players(): + game = Game( + players=["A", "B", "C"], + strategies=[["x", "y"], ["x", "y"], ["x", "y"]], + payoffs=[np.zeros((2, 2, 2))] * 3, + ) + with pytest.raises(NotImplementedError): + solver.find_mixed_nash_equilibria(game) + + +def test_pure_equilibrium_finder_supports_n_players(): + # Simple 3-player coordination game: everyone matching strategy 0 is the + # unique pure Nash equilibrium (payoff 1 if all match, 0 otherwise). + payoff = np.zeros((2, 2, 2)) + payoff[0, 0, 0] = 1 + game = Game( + players=["A", "B", "C"], + strategies=[["x", "y"], ["x", "y"], ["x", "y"]], + payoffs=[payoff, payoff, payoff], + ) + equilibria = solver.find_pure_nash_equilibria(game) + assert (0, 0, 0) in equilibria + +def test_pricing_has_dominant_strategies(): + result = solver.find_dominant_strategies(PRICING) + # Both companies have "Low" (index 1) as strictly dominant strategy: + # Company A: Low beats High vs any B strategy (10>8, 5>2) + # Company B: Low beats High vs any A strategy (10>8, 5>2) + assert result == {0: 1, 1: 1} + + +def test_matching_pennies_has_no_dominant_strategies(): + result = solver.find_dominant_strategies(MATCHING_PENNIES) + assert result == {0: None, 1: None} + + +def test_dominant_strategies_supports_n_players(): + # 3-player game: players 0,1 have strictly dominant strategy 0; + # player 2 has no dominant strategy. + payoffs_0 = np.zeros((2, 2, 2)) + payoffs_1 = np.zeros((2, 2, 2)) + payoffs_2 = np.zeros((2, 2, 2)) + for profile in np.ndindex(2, 2, 2): + payoffs_0[profile] = 10.0 if profile[0] == 0 else 1.0 + payoffs_1[profile] = 10.0 if profile[1] == 0 else 1.0 + payoffs_2[profile] = float(profile[0] + profile[1]) + game = Game( + players=["A", "B", "C"], + strategies=[["x", "y"], ["x", "y"], ["x", "y"]], + payoffs=[payoffs_0, payoffs_1, payoffs_2], + ) + result = solver.find_dominant_strategies(game) + assert result[0] == 0 + assert result[1] == 0 + assert result[2] is None + + +def test_dominant_strategies_one_strategy_player(): + # Player 0 has 1 strategy, Player 1 has 2 strategies + payoffs_0 = np.array([[5.0, 5.0]]) # 1x2 matrix + payoffs_1 = np.array([[10.0, 1.0]]) # 1x2 matrix + game = Game( + players=["A", "B"], + strategies=[["only"], ["x", "y"]], + payoffs=[payoffs_0, payoffs_1], + ) + result = solver.find_dominant_strategies(game) + # Player 0 has no dominant strategy (None) because they only have 1 strategy. + # Player 1 has dominant strategy 0 (10.0 > 1.0). + assert result == {0: None, 1: 0} + + +def test_reduce_game_pricing(): + reduced, logs = solver.reduce_game(PRICING) + assert len(logs) == 2 + assert "Player 'Company A' eliminated dominated strategy 'High'" in logs[0] + assert "Player 'Company B' eliminated dominated strategy 'High'" in logs[1] + assert reduced.strategies == [["Low"], ["Low"]] + assert np.allclose(reduced.payoffs[0], [[5]]) + assert np.allclose(reduced.payoffs[1], [[5]]) + + +def test_reduce_game_matching_pennies(): + reduced, logs = solver.reduce_game(MATCHING_PENNIES) + assert len(logs) == 0 + assert reduced.strategies == MATCHING_PENNIES.strategies + assert np.allclose(reduced.payoffs[0], MATCHING_PENNIES.payoffs[0]) + assert np.allclose(reduced.payoffs[1], MATCHING_PENNIES.payoffs[1]) + + +def test_reduce_game_n_players(): + payoffs_0 = np.zeros((2, 2, 2)) + payoffs_1 = np.zeros((2, 2, 2)) + payoffs_2 = np.zeros((2, 2, 2)) + payoffs_0[0, :, :] = 1.0 + payoffs_0[1, :, :] = 2.0 + payoffs_1[:, 0, :] = 1.0 + payoffs_1[:, 1, :] = 2.0 + payoffs_2[:, :, 0] = 1.0 + payoffs_2[:, :, 1] = 2.0 + game = Game( + players=["P0", "P1", "P2"], + strategies=[["A", "B"], ["C", "D"], ["E", "F"]], + payoffs=[payoffs_0, payoffs_1, payoffs_2] + ) + reduced, logs = solver.reduce_game(game) + assert len(logs) == 3 + assert reduced.strategies == [["B"], ["D"], ["F"]] + assert np.allclose(reduced.payoffs[0], [[[2.0]]]) \ No newline at end of file