]> gitweb.michael.orlitzky.com - dunshire.git/commitdiff
Return CVXOPT matrices from the random matrix functions.
authorMichael Orlitzky <michael@orlitzky.com>
Thu, 13 Oct 2016 16:33:31 +0000 (12:33 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Thu, 13 Oct 2016 16:33:31 +0000 (12:33 -0400)
src/dunshire/games.py

index a039e1f232ff12395e5f0634de1dde7b3cdcfb10..1364fddd16ea2221601ca150b78d2944216e83ad 100644 (file)
@@ -507,29 +507,29 @@ class SymmetricLinearGame:
 
 def _random_matrix(dims):
     """
-    Generate a random square (``dims``-by-``dims``) matrix,
-    represented as a list of rows. This is used only by the
-    :class:`SymmetricLinearGameTest` class.
+    Generate a random square (``dims``-by-``dims``) matrix. This is used
+    only by the :class:`SymmetricLinearGameTest` class.
     """
-    return [[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):
     """
     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.
+    nonnegative entries. This is used only by the
+    :class:`SymmetricLinearGameTest` class.
     """
     L = _random_matrix(dims)
-    return [[abs(entry) for entry in row] for row in L]
+    return matrix([abs(entry) for entry in L], (dims, dims))
 
 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.
+    entries only on the diagonal. 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)]
+    return matrix([[uniform(-10, 10)*int(i == j) for i in range(dims)]
+                   for j in range(dims)])
 
 def _random_orthant_params():
     """
@@ -542,7 +542,7 @@ def _random_orthant_params():
     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())
-    return (L, K, e1, e2)
+    return (L, K, matrix(e1), matrix(e2))
 
 
 def _random_icecream_params():
@@ -570,7 +570,7 @@ def _random_icecream_params():
     e2 += [fudge_factor*uniform(0, 1) for idx in range(K.dimension() - 1)]
     L = _random_matrix(K.dimension())
 
-    return (L, K, e1, e2)
+    return (L, K, matrix(e1), matrix(e2))
 
 
 class SymmetricLinearGameTest(TestCase):
@@ -599,14 +599,13 @@ class SymmetricLinearGameTest(TestCase):
         Given the parameters needed to construct a SymmetricLinearGame,
         ensure that that game has a solution.
         """
-        G = SymmetricLinearGame(L, K, e1, e2)
-        soln = G.solution()
-
         # The matrix() constructor assumes that ``L`` is a list of
         # columns, so we transpose it to agree with what
         # SymmetricLinearGame() thinks.
-        L_matrix = matrix(L).trans()
-        expected = inner_product(L_matrix*soln.player1_optimal(),
+        G = SymmetricLinearGame(L.trans(), K, e1, e2)
+        soln = G.solution()
+
+        expected = inner_product(L*soln.player1_optimal(),
                                  soln.player2_optimal())
         self.assert_within_tol(soln.game_value(), expected)
 
@@ -650,9 +649,6 @@ class SymmetricLinearGameTest(TestCase):
         Test that scaling ``L`` by a nonnegative number scales the value
         of the game by the same number.
         """
-        # Make ``L`` a matrix so that we can scale it by alpha. Its
-        # random, so who cares if it gets transposed.
-        L = matrix(L)
         game1 = SymmetricLinearGame(L, K, e1, e2)
         value1 = game1.solution().game_value()
 
@@ -685,23 +681,19 @@ class SymmetricLinearGameTest(TestCase):
         Check that translating ``L`` by alpha*(e1*e2.trans()) increases
         the value of the associated game by alpha.
         """
-        e1 = matrix(e1, (K.dimension(), 1))
-        e2 = matrix(e2, (K.dimension(), 1))
-        game1 = SymmetricLinearGame(L, K, e1, e2)
+        # We need to use ``L`` later, so make sure we transpose it
+        # before passing it in as a column-indexed matrix.
+        game1 = SymmetricLinearGame(L.trans(), K, e1, e2)
         soln1 = game1.solution()
         value1 = soln1.game_value()
         x_bar = soln1.player1_optimal()
         y_bar = soln1.player2_optimal()
 
-        # Make ``L`` a CVXOPT matrix so that we can do math with
-        # it. Note that this gives us the "correct" representation of
-        # ``L`` (in agreement with what G has), but COLUMN indexed.
         alpha = uniform(-10, 10)
-        L = matrix(L).trans()
         tensor_prod = e1*e2.trans()
 
-        # Likewise, this is the "correct" representation of ``M``, but
-        # COLUMN indexed...
+        # This is the "correct" representation of ``M``, but COLUMN
+        # indexed...
         M = L + alpha*tensor_prod
 
         # so we have to transpose it when we feed it to the constructor.
@@ -737,16 +729,11 @@ class SymmetricLinearGameTest(TestCase):
         value that is the negation of the original game. Comes from
         some corollary.
         """
-        e1 = matrix(e1, (K.dimension(), 1))
-        e2 = matrix(e2, (K.dimension(), 1))
-        game1 = SymmetricLinearGame(L, K, e1, e2)
+        # We need to use ``L`` later, so make sure we transpose it
+        # before passing it in as a column-indexed matrix.
+        game1 = SymmetricLinearGame(L.trans(), K, e1, e2)
 
-        # Make ``L`` a CVXOPT matrix so that we can do math with
-        # it. Note that this gives us the "correct" representation of
-        # ``L`` (in agreement with what G has), but COLUMN indexed.
-        L = matrix(L).trans()
-
-        # Likewise, this is the "correct" representation of ``M``, but
+        # This is the "correct" representation of ``M``, but
         # COLUMN indexed...
         M = -L.trans()
 
@@ -788,17 +775,14 @@ class SymmetricLinearGameTest(TestCase):
         Two orthogonality relations hold at an optimal solution, and we
         check them here.
         """
-        game = SymmetricLinearGame(L, K, e1, e2)
+        # We need to use ``L`` later, so make sure we transpose it
+        # before passing it in as a column-indexed matrix.
+        game = SymmetricLinearGame(L.trans(), 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)
 
@@ -840,13 +824,15 @@ class SymmetricLinearGameTest(TestCase):
         game = SymmetricLinearGame(L, K, e1, e2)
         self.assertTrue(game.solution().game_value() >= -options.ABS_TOL)
 
+
     def test_lyapunov_orthant(self):
         """
         Test that a Lyapunov game on the nonnegative orthant works.
         """
-        (_, K, e1, e2) = _random_orthant_params()
+        (L, K, e1, e2) = _random_orthant_params()
 
         # Ignore that L, we need a diagonal (Lyapunov-like) one.
+        # (And we don't need to transpose those.)
         L = _random_diagonal_matrix(K.dimension())
         game = SymmetricLinearGame(L, K, e1, e2)
         soln = game.solution()
@@ -854,7 +840,6 @@ class SymmetricLinearGameTest(TestCase):
         # We only check for positive/negative stability if the game
         # value is not basically zero. If the value is that close to
         # zero, we just won't check any assertions.
-        L = matrix(L).trans()
         if soln.game_value() > options.ABS_TOL:
             # L should be positive stable
             ps = all([eig > -options.ABS_TOL for eig in  eigenvalues_re(L)])