X-Git-Url: https://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=src%2Ftest%2Fsymmetric_linear_game_test.py;fp=test%2Fsymmetric_linear_game_test.py;h=f6f397f8354b983407e8abb61bd8997ee19ea5e3;hb=6344038e97380527bef52ba48ba54cc9180c7a82;hp=5adbb2ddd11942faf3c31354513b62ab6877e853;hpb=e56739b9f432a5f2dce0223158de946b3db6c0e5;p=dunshire.git diff --git a/test/symmetric_linear_game_test.py b/src/test/symmetric_linear_game_test.py similarity index 71% rename from test/symmetric_linear_game_test.py rename to src/test/symmetric_linear_game_test.py index 5adbb2d..f6f397f 100644 --- a/test/symmetric_linear_game_test.py +++ b/src/test/symmetric_linear_game_test.py @@ -10,42 +10,128 @@ from dunshire.matrices import (append_col, append_row, eigenvalues_re, identity, inner_product) from dunshire import options -def _random_matrix(dims): + +def random_matrix(dims): """ - Generate a random square (``dims``-by-``dims``) matrix. This is used - only by the :class:`SymmetricLinearGameTest` class. + Generate a random square matrix. + + Parameters + ---------- + + dims : int + The number of rows/columns you want in the returned matrix. + + Returns + ------- + + matrix + A new matrix whose entries are random floats chosen uniformly from + the interval [-10, 10]. + + Examples + -------- + + >>> A = random_matrix(3) + >>> A.size + (3, 3) + """ return matrix([[uniform(-10, 10) for i in range(dims)] for j in range(dims)]) -def _random_nonnegative_matrix(dims): + +def random_nonnegative_matrix(dims): """ - Generate a random square (``dims``-by-``dims``) matrix with - nonnegative entries. This is used only by the - :class:`SymmetricLinearGameTest` class. + Generate a random square matrix with nonnegative entries. + + Parameters + ---------- + + dims : int + The number of rows/columns you want in the returned matrix. + + Returns + ------- + + matrix + A new matrix whose entries are random floats chosen uniformly from + the interval [0, 10]. + + Examples + -------- + + >>> A = random_nonnegative_matrix(3) + >>> A.size + (3, 3) + >>> all([entry >= 0 for entry in A]) + True + """ - L = _random_matrix(dims) + L = random_matrix(dims) return matrix([abs(entry) for entry in L], (dims, dims)) -def _random_diagonal_matrix(dims): + +def random_diagonal_matrix(dims): """ - Generate a random square (``dims``-by-``dims``) matrix with nonzero - entries only on the diagonal. This is used only by the - :class:`SymmetricLinearGameTest` class. + Generate a random square matrix with zero off-diagonal entries. + + These matrices are Lyapunov-like on the nonnegative orthant, as is + fairly easy to see. + + Parameters + ---------- + + dims : int + The number of rows/columns you want in the returned matrix. + + Returns + ------- + + matrix + A new matrix whose diagonal entries are random floats chosen + uniformly from the interval [-10, 10] and whose off-diagonal + entries are zero. + + Examples + -------- + + >>> A = random_diagonal_matrix(3) + >>> A.size + (3, 3) + >>> A[0,1] == A[0,2] == A[1,0] == A[2,0] == A[1,2] == A[2,1] == 0 + True + """ return matrix([[uniform(-10, 10)*int(i == j) for i in range(dims)] for j in range(dims)]) -def _random_skew_symmetric_matrix(dims): +def random_skew_symmetric_matrix(dims): """ - Generate a random skew-symmetrix (``dims``-by-``dims``) matrix. + Generate a random skew-symmetrix matrix. + + Parameters + ---------- + + dims : int + The number of rows/columns you want in the returned matrix. + + Returns + ------- + + matrix + A new skew-matrix whose strictly above-diagonal entries are + random floats chosen uniformly from the interval [-10, 10]. Examples -------- + >>> A = random_skew_symmetric_matrix(3) + >>> A.size + (3, 3) + >>> from dunshire.matrices import norm - >>> A = _random_skew_symmetric_matrix(randint(1, 10)) + >>> A = random_skew_symmetric_matrix(randint(1, 10)) >>> norm(A + A.trans()) < options.ABS_TOL True @@ -57,38 +143,78 @@ def _random_skew_symmetric_matrix(dims): return strict_ut - strict_ut.trans() -def _random_lyapunov_like_icecream(dims): - """ - Generate a random Lyapunov-like matrix over the ice-cream cone in - ``dims`` dimensions. +def random_lyapunov_like_icecream(dims): + r""" + Generate a random matrix Lyapunov-like on the ice-cream cone. + + The form of these matrices is cited in Gowda and Tao + [GowdaTao]_. The scalar ``a`` and the vector ``b`` (using their + notation) are easy to generate. The submatrix ``D`` is a little + trickier, but it can be found noticing that :math:`C + C^{T} = 0` + for a skew-symmetric matrix :math:`C` implying that :math:`C + C^{T} + + \left(2a\right)I = \left(2a\right)I`. Thus we can stick an + :math:`aI` with each of :math:`C,C^{T}` and let those be our + :math:`D,D^{T}`. + + Parameters + ---------- + + dims : int + The dimension of the ice-cream cone (not of the matrix you want!) + on which the returned matrix should be Lyapunov-like. + + Returns + ------- + + matrix + A new matrix, Lyapunov-like on the ice-cream cone in ``dims`` + dimensions, whose free entries are random floats chosen uniformly + from the interval [-10, 10]. + + References + ---------- + + .. [GowdaTao] M. S. Gowda and J. Tao. On the bilinearity rank of a + proper cone and Lyapunov-like transformations. Mathematical + Programming, 147:155–170, 2014. + + Examples + -------- + + >>> L = random_lyapunov_like_icecream(3) + >>> L.size + (3, 3) + >>> x = matrix([1,1,0]) + >>> s = matrix([1,-1,0]) + >>> abs(inner_product(L*x, s)) < options.ABS_TOL + True + """ a = matrix([uniform(-10, 10)], (1, 1)) b = matrix([uniform(-10, 10) for idx in range(dims-1)], (dims-1, 1)) - D = _random_skew_symmetric_matrix(dims-1) + a*identity(dims-1) + D = random_skew_symmetric_matrix(dims-1) + a*identity(dims-1) row1 = append_col(a, b.trans()) row2 = append_col(b, D) return append_row(row1, row2) -def _random_orthant_params(): +def random_orthant_params(): """ Generate the ``L``, ``K``, ``e1``, and ``e2`` parameters for a - random game over the nonnegative orthant. This is only used by - the :class:`SymmetricLinearGameTest` class. + random game over the nonnegative orthant. """ ambient_dim = randint(1, 10) 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_matrix(K.dimension()) + L = random_matrix(K.dimension()) return (L, K, matrix(e1), matrix(e2)) -def _random_icecream_params(): +def random_icecream_params(): """ Generate the ``L``, ``K``, ``e1``, and ``e2`` parameters for a - random game over the ice cream cone. This is only used by - the :class:`SymmetricLinearGameTest` class. + random game over the ice-cream cone. """ # Use a minimum dimension of two to avoid divide-by-zero in # the fudge factor we make up later. @@ -107,7 +233,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_matrix(K.dimension()) + L = random_matrix(K.dimension()) return (L, K, matrix(e1), matrix(e2)) @@ -158,7 +284,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 optimal solutions should give us the optimal game value when we apply the payoff operator to them. """ - (L, K, e1, e2) = _random_orthant_params() + (L, K, e1, e2) = random_orthant_params() self.assert_solution_exists(L, K, e1, e2) @@ -167,7 +293,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 Like :meth:`test_solution_exists_nonnegative_orthant`, except over the ice cream cone. """ - (L, K, e1, e2) = _random_icecream_params() + (L, K, e1, e2) = random_icecream_params() self.assert_solution_exists(L, K, e1, e2) @@ -203,7 +329,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 Test that scaling ``L`` by a nonnegative number scales the value of the game by the same number over the nonnegative orthant. """ - (L, K, e1, e2) = _random_orthant_params() + (L, K, e1, e2) = random_orthant_params() self.assert_scaling_works(L, K, e1, e2) @@ -212,7 +338,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 The same test as :meth:`test_nonnegative_scaling_orthant`, except over the ice cream cone. """ - (L, K, e1, e2) = _random_icecream_params() + (L, K, e1, e2) = random_icecream_params() self.assert_scaling_works(L, K, e1, e2) @@ -250,7 +376,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 """ Test that translation works over the nonnegative orthant. """ - (L, K, e1, e2) = _random_orthant_params() + (L, K, e1, e2) = random_orthant_params() self.assert_translation_works(L, K, e1, e2) @@ -259,7 +385,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 The same as :meth:`test_translation_orthant`, except over the ice cream cone. """ - (L, K, e1, e2) = _random_icecream_params() + (L, K, e1, e2) = random_icecream_params() self.assert_translation_works(L, K, e1, e2) @@ -297,7 +423,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 Test the value of the "opposite" game over the nonnegative orthant. """ - (L, K, e1, e2) = _random_orthant_params() + (L, K, e1, e2) = random_orthant_params() self.assert_opposite_game_works(L, K, e1, e2) @@ -306,7 +432,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 Like :meth:`test_opposite_game_orthant`, except over the ice-cream cone. """ - (L, K, e1, e2) = _random_icecream_params() + (L, K, e1, e2) = random_icecream_params() self.assert_opposite_game_works(L, K, e1, e2) @@ -335,7 +461,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 Check the orthgonality relationships that hold for a solution over the nonnegative orthant. """ - (L, K, e1, e2) = _random_orthant_params() + (L, K, e1, e2) = random_orthant_params() self.assert_orthogonality(L, K, e1, e2) @@ -344,7 +470,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 Check the orthgonality relationships that hold for a solution over the ice-cream cone. """ - (L, K, e1, e2) = _random_icecream_params() + (L, K, e1, e2) = random_icecream_params() self.assert_orthogonality(L, K, e1, e2) @@ -356,8 +482,8 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 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()[1:] - L = _random_nonnegative_matrix(K.dimension()) + (K, e1, e2) = random_orthant_params()[1:] + L = random_nonnegative_matrix(K.dimension()) game = SymmetricLinearGame(L, K, e1, e2) self.assertTrue(game.solution().game_value() >= -options.ABS_TOL) @@ -392,8 +518,8 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 """ Test that a Lyapunov game on the nonnegative orthant works. """ - (K, e1, e2) = _random_orthant_params()[1:] - L = _random_diagonal_matrix(K.dimension()) + (K, e1, e2) = random_orthant_params()[1:] + L = random_diagonal_matrix(K.dimension()) self.assert_lyapunov_works(L, K, e1, e2) @@ -402,7 +528,7 @@ class SymmetricLinearGameTest(TestCase): # pylint: disable=R0904 """ Test that a Lyapunov game on the ice-cream cone works. """ - (K, e1, e2) = _random_icecream_params()[1:] - L = _random_lyapunov_like_icecream(K.dimension()) + (K, e1, e2) = random_icecream_params()[1:] + L = random_lyapunov_like_icecream(K.dimension()) self.assert_lyapunov_works(L, K, e1, e2)