X-Git-Url: https://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=src%2Fdunshire%2Fgames.py;h=65fc791b20bd0624ee714a724581eb1537fa2edf;hb=d1638062ec3a28dc64b91f308858211db243db7b;hp=3c53343175c11421413eed9cfe0ae26518d9799f;hpb=556f614c2cbbe91e34967ea78464436413ae1c75;p=dunshire.git diff --git a/src/dunshire/games.py b/src/dunshire/games.py index 3c53343..65fc791 100644 --- a/src/dunshire/games.py +++ b/src/dunshire/games.py @@ -504,7 +504,7 @@ class SymmetricLinearGame: -def _random_square_matrix(dims): +def _random_matrix(dims): """ Generate a random square (``dims``-by-``dims``) matrix, represented as a list of rows. This is used only by the @@ -512,6 +512,23 @@ def _random_square_matrix(dims): """ return [[uniform(-10, 10) for i in range(dims)] for j in range(dims)] +def _random_nonnegative_matrix(dims): + """ + Generate a random square (``dims``-by-``dims``) matrix with + nonnegative entries, represented as a list of rows. This is used + only by the :class:`SymmetricLinearGameTest` class. + """ + L = _random_matrix(dims) + return [[abs(entry) for entry in row] for row in L] + +def _random_diagonal_matrix(dims): + """ + Generate a random square (``dims``-by-``dims``) matrix with nonzero + entries only on the diagonal, represented as a list of rows. This is + used only by the :class:`SymmetricLinearGameTest` class. + """ + return [[uniform(-10, 10)*int(i == j) for i in range(dims)] + for j in range(dims)] def _random_orthant_params(): """ @@ -523,7 +540,7 @@ def _random_orthant_params(): K = NonnegativeOrthant(ambient_dim) e1 = [uniform(0.5, 10) for idx in range(K.dimension())] e2 = [uniform(0.5, 10) for idx in range(K.dimension())] - L = _random_square_matrix(K.dimension()) + L = _random_matrix(K.dimension()) return (L, K, e1, e2) @@ -550,7 +567,7 @@ def _random_icecream_params(): fudge_factor = 1.0 / (2.0*sqrt(K.dimension() - 1.0)) e1 += [fudge_factor*uniform(0, 1) for idx in range(K.dimension() - 1)] e2 += [fudge_factor*uniform(0, 1) for idx in range(K.dimension() - 1)] - L = _random_square_matrix(K.dimension()) + L = _random_matrix(K.dimension()) return (L, K, e1, e2) @@ -763,3 +780,61 @@ class SymmetricLinearGameTest(TestCase): """ (L, K, e1, e2) = _random_icecream_params() self.assert_opposite_game_works(L, K, e1, e2) + + + def assert_orthogonality(self, L, K, e1, e2): + """ + Two orthogonality relations hold at an optimal solution, and we + check them here. + """ + game = SymmetricLinearGame(L, K, e1, e2) + soln = game.solution() + x_bar = soln.player1_optimal() + y_bar = soln.player2_optimal() + value = soln.game_value() + + # Make these matrices so that we can compute with them. + L = matrix(L).trans() + e1 = matrix(e1, (K.dimension(), 1)) + e2 = matrix(e2, (K.dimension(), 1)) + + ip1 = inner_product(y_bar, L*x_bar - value*e1) + self.assert_within_tol(ip1, 0) + + ip2 = inner_product(value*e2 - L.trans()*y_bar, x_bar) + self.assert_within_tol(ip2, 0) + + + def test_orthogonality_orthant(self): + """ + Check the orthgonality relationships that hold for a solution + over the nonnegative orthant. + """ + (L, K, e1, e2) = _random_orthant_params() + self.assert_orthogonality(L, K, e1, e2) + + + def test_orthogonality_icecream(self): + """ + Check the orthgonality relationships that hold for a solution + over the ice-cream cone. + """ + (L, K, e1, e2) = _random_icecream_params() + self.assert_orthogonality(L, K, e1, e2) + + + def test_positive_operator_value(self): + """ + Test that a positive operator on the nonnegative orthant gives + rise to a a game with a nonnegative value. + + This test theoretically applies to the ice-cream cone as well, + but we don't know how to make positive operators on that cone. + """ + (_, K, e1, e2) = _random_orthant_params() + + # Ignore that L, we need a nonnegative one. + L = _random_nonnegative_matrix(K.dimension()) + + game = SymmetricLinearGame(L, K, e1, e2) + self.assertTrue(game.solution().game_value() >= -options.ABS_TOL)