]> gitweb.michael.orlitzky.com - dunshire.git/blobdiff - test/matrices_test.py
Clean up and document some of the new test code.
[dunshire.git] / test / matrices_test.py
index 195549f2e5aa0fb38ee34f0c4e85ccff9dac87a1..ce57c7dc32a845b38af4dbad75e362c9e1da9b6f 100644 (file)
 Unit tests for the functions in the ``matrices`` module.
 """
 
+from copy import copy
 from unittest import TestCase
 
-from cvxopt import matrix
-
 from dunshire.matrices import (append_col, append_row, condition_number,
                                eigenvalues, eigenvalues_re, identity,
                                inner_product, norm)
 from dunshire.options import ABS_TOL
-from .randomgen import random_matrix, random_natural, random_scalar
+from .randomgen import random_matrix, random_natural
+
 
 class AppendColTest(TestCase):
+    """
+    Tests for the :func:`append_col` function.
+    """
 
-    def test_size_increases(self):
+    def test_new_dimensions(self):
         """
-        If we append a column to a matrix, the result should be bigger
-        than the original matrix.
+        If we append one matrix to another side-by-side, then the result
+        should have the same number of rows as the two original
+        matrices. However, the number of their columns should add up to
+        the number of columns in the new combined matrix.
         """
-        dims = random_natural()
-        mat1 = random_matrix(dims)
-        mat2 = random_matrix(dims)
+        rows = random_natural()
+        cols1 = random_natural()
+        cols2 = random_natural()
+        mat1 = random_matrix(rows, cols1)
+        mat2 = random_matrix(rows, cols2)
         bigmat = append_col(mat1, mat2)
-        self.assertTrue(bigmat.size[0] >= mat1.size[0])
-        self.assertTrue(bigmat.size[1] >= mat1.size[1])
+        self.assertTrue(bigmat.size[0] == rows)
+        self.assertTrue(bigmat.size[1] == cols1+cols2)
 
 
 class AppendRowTest(TestCase):
+    """
+    Tests for the :func:`append_row` function.
+    """
 
-    def test_size_increases(self):
+    def test_new_dimensions(self):
         """
-        If we append a row to a matrix, the result should be bigger
-        than the original matrix.
+        If we append one matrix to another top-to-bottom, then
+        the result should have the same number of columns as the two
+        original matrices. However, the number of their rows should add
+        up to the number of rows in the the new combined matrix.
         """
-        dims = random_natural()
-        mat1 = random_matrix(dims)
-        mat2 = random_matrix(dims)
+        rows1 = random_natural()
+        rows2 = random_natural()
+        cols = random_natural()
+        mat1 = random_matrix(rows1, cols)
+        mat2 = random_matrix(rows2, cols)
         bigmat = append_row(mat1, mat2)
-        self.assertTrue(bigmat.size[0] >= mat1.size[0])
-        self.assertTrue(bigmat.size[1] >= mat1.size[1])
+        self.assertTrue(bigmat.size[0] == rows1+rows2)
+        self.assertTrue(bigmat.size[1] == cols)
 
 
 class EigenvaluesTest(TestCase):
+    """
+    Tests for the :func:`eigenvalues` function.
+    """
 
-    def test_eigenvalues_input_not_clobbered(self):
+    def test_eigenvalues_input_untouched(self):
+        """
+        The eigenvalue functions provided by CVXOPT/LAPACK like to
+        overwrite the matrices that you pass into them as
+        arguments. This test makes sure that our :func:`eigenvalues`
+        function does not do the same.
+        """
         mat = random_matrix(random_natural())
         symmat = mat + mat.trans()
-        symmat_copy = matrix(symmat, symmat.size)
-        eigs = eigenvalues(symmat)
+        symmat_copy = copy(symmat)
+        dummy = eigenvalues(symmat)
         self.assertTrue(norm(symmat - symmat_copy) < ABS_TOL)
 
-    def test_eigenvalues_re_input_not_clobbered(self):
-        mat = random_matrix(random_natural())
-        mat_copy = matrix(mat, mat.size)
-        eigs = eigenvalues_re(mat)
-        self.assertTrue(norm(mat - mat_copy) < ABS_TOL)
-
-    def test_eigenvalues_of_symmetric_are_real(self):
+    def test_eigenvalues_of_symmat_are_real(self):
+        """
+        A real symmetric matrix has real eigenvalues, so if we start
+        with a symmetric matrix, then the two functions :func:`eigenvalues`
+        and :func:`eigenvalues_re` should agree on it.
+        """
         mat = random_matrix(random_natural())
         symmat = mat + mat.trans()
         eigs1 = sorted(eigenvalues(symmat))
         eigs2 = sorted(eigenvalues_re(symmat))
-        diffs = [abs(e1-e2) for (e1,e2) in zip(eigs1,eigs2)]
+        diffs = [abs(e1 - e2) for (e1, e2) in zip(eigs1, eigs2)]
         self.assertTrue(all([diff < ABS_TOL for diff in diffs]))
 
-
     def test_eigenvalues_of_identity(self):
+        """
+        All eigenvalues of the identity matrix should be one.
+        """
         mat = identity(random_natural(), typecode='d')
-        eigs1 = eigenvalues(mat)
-        eigs2 = eigenvalues_re(mat)
-        self.assertTrue(all([abs(e1 - 1) < ABS_TOL for e1 in eigs1]))
-        self.assertTrue(all([abs(e2 - 1) < ABS_TOL for e2 in eigs2]))
+        eigs = eigenvalues(mat)
+        self.assertTrue(all([abs(ev - 1) < ABS_TOL for ev in eigs]))
+
+
+class EigenvaluesRealPartTest(TestCase):
+    """
+    Tests for the :func:`eigenvalues_re` function.
+    """
+
+    def test_eigenvalues_re_input_not_clobbered(self):
+        """
+        The eigenvalue functions provided by CVXOPT/LAPACK like to
+        overwrite the matrices that you pass into them as
+        arguments. This test makes sure that our :func:`eigenvalues_re`
+        function does not do the same.
+        """
+        mat = random_matrix(random_natural())
+        mat_copy = copy(mat)
+        dummy = eigenvalues_re(mat)
+        self.assertTrue(norm(mat - mat_copy) < ABS_TOL)
+
+    def test_eigenvalues_re_of_identity(self):
+        """
+        All eigenvalues of the identity matrix should be one.
+        """
+        mat = identity(random_natural(), typecode='d')
+        eigs = eigenvalues_re(mat)
+        self.assertTrue(all([abs(ev - 1) < ABS_TOL for ev in eigs]))
 
 
 class InnerProductTest(TestCase):
+    """
+    Tests for the :func:`inner_product` function.
+    """
+
+    def test_inner_product_with_self_is_norm_squared(self):
+        """
+        Ensure that the func:`inner_product` and :func:`norm` functions
+        are compatible by checking that the square of the norm of a
+        vector is its inner product with itself.
+        """
+        vec = random_matrix(random_natural(), 1)
+        actual = inner_product(vec, vec)
+        expected = norm(vec)**2
+        self.assertTrue(abs(actual - expected) < ABS_TOL)
+
+
+class NormTest(TestCase):
+    """
+    Tests for the :func:`norm` function.
+    """
 
     def test_norm_is_nonnegative(self):
-        vec = matrix([random_scalar() for _ in range(random_natural())])
-        self.assertTrue(inner_product(vec,vec) >= 0)
+        """
+        Test one of the properties of a norm, that it is nonnegative.
+        """
+        mat = random_matrix(random_natural(), random_natural())
+        self.assertTrue(norm(mat) >= 0)
 
 
-def ConditionNumberTest(TestCase):
+class ConditionNumberTest(TestCase):
+    """
+    Tests for the :func:`condition_number` function.
+    """
 
     def test_condition_number_ge_one(self):
-        mat = random_matrix(random_natural())
+        """
+        From the way that it is defined, the condition number should
+        always be greater than or equal to one.
+        """
+        mat = random_matrix(random_natural(), random_natural())
         self.assertTrue(condition_number(mat) >= 1)