X-Git-Url: https://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=test%2Fmatrices_test.py;h=69f9da906e16462423d4a922e420046125ffef04;hb=0274de467062ab29d2a41d2a91ec0b28fcd95c8d;hp=195549f2e5aa0fb38ee34f0c4e85ccff9dac87a1;hpb=040374ca134b2f3d962b91a9dac97a7600032685;p=dunshire.git diff --git a/test/matrices_test.py b/test/matrices_test.py index 195549f..69f9da9 100644 --- a/test/matrices_test.py +++ b/test/matrices_test.py @@ -1,88 +1,179 @@ """ -Unit tests for the functions in the ``matrices`` module. +Unit tests for the functions in the :mod:`dunshire.matrices` module. """ +from copy import deepcopy 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:`dunshire.matrices.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:`dunshire.matrices.eigenvalues` function. + """ def test_eigenvalues_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` + function does not do the same. + + We use a ``deepcopy`` here in case the ``copy`` used in the + :func:`eigenvalues` function is insufficient. If ``copy`` didn't + work and this test used it too, then this test would pass when + it shouldn't. + """ mat = random_matrix(random_natural()) symmat = mat + mat.trans() - symmat_copy = matrix(symmat, symmat.size) - eigs = eigenvalues(symmat) + symmat_copy = deepcopy(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:`dunshire.matrices.eigenvalues` and + :func:`dunshire.matrices.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:`dunshire.matrices.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:`dunshire.matrices.eigenvalues_re` function does not do + the same. + + We use a ``deepcopy`` here in case the ``copy`` used in the + :func:`dunshire.matrices.eigenvalues_re` function is + insufficient. If ``copy`` didn't work and this test used it too, + then this test would pass when it shouldn't. + """ + mat = random_matrix(random_natural()) + mat_copy = deepcopy(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:`dunshire.matrices.inner_product` function. + """ + + def test_inner_product_with_self_is_norm_squared(self): + """ + Ensure that the func:`dunshire.matrices.inner_product` and + :func:`dunshire.matrices.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:`dunshire.matrices.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:`dunshire.matrices.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)