]> gitweb.michael.orlitzky.com - dunshire.git/blobdiff - src/test/symmetric_linear_game_test.py
Reorganize the test source code and doc building.
[dunshire.git] / 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 5adbb2ddd11942faf3c31354513b62ab6877e853..f6f397f8354b983407e8abb61bd8997ee19ea5e3 100644 (file)
@@ -10,42 +10,128 @@ from dunshire.matrices import (append_col, append_row, eigenvalues_re,
                                identity, inner_product)
 from dunshire import options
 
                                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)])
 
     """
     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))
 
     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)])
 
 
     """
     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
     --------
 
 
     Examples
     --------
 
+        >>> A = random_skew_symmetric_matrix(3)
+        >>> A.size
+        (3, 3)
+
         >>> from dunshire.matrices import norm
         >>> 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
 
         >>> norm(A + A.trans()) < options.ABS_TOL
         True
 
@@ -57,38 +143,78 @@ def _random_skew_symmetric_matrix(dims):
     return strict_ut - strict_ut.trans()
 
 
     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))
     """
     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)
 
 
     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
     """
     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())]
     """
     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))
 
 
     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
     """
     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.
     """
     # 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)]
     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))
 
 
     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.
         """
         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)
 
 
         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.
         """
         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)
 
 
         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.
         """
         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)
 
 
         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.
         """
         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)
 
 
         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.
         """
         """
         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)
 
 
         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.
         """
         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)
 
 
         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.
         """
         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)
 
 
         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.
         """
         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)
 
 
         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.
         """
         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)
 
 
         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.
         """
         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)
 
 
         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.
         """
         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)
 
         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.
         """
         """
         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)
 
 
         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.
         """
         """
         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)
 
         self.assert_lyapunov_works(L, K, e1, e2)