]> gitweb.michael.orlitzky.com - dunshire.git/blob - test/matrices_test.py
e755f31cf30140c8bb80c6f539ba4bb86deaf41c
[dunshire.git] / test / matrices_test.py
1 """
2 Unit tests for the functions in the ``matrices`` module.
3 """
4
5 from copy import deepcopy
6 from unittest import TestCase
7
8 from dunshire.matrices import (append_col, append_row, condition_number,
9 eigenvalues, eigenvalues_re, identity,
10 inner_product, norm)
11 from dunshire.options import ABS_TOL
12 from .randomgen import random_matrix, random_natural
13
14
15 class AppendColTest(TestCase):
16 """
17 Tests for the :func:`append_col` function.
18 """
19
20 def test_new_dimensions(self):
21 """
22 If we append one matrix to another side-by-side, then the result
23 should have the same number of rows as the two original
24 matrices. However, the number of their columns should add up to
25 the number of columns in the new combined matrix.
26 """
27 rows = random_natural()
28 cols1 = random_natural()
29 cols2 = random_natural()
30 mat1 = random_matrix(rows, cols1)
31 mat2 = random_matrix(rows, cols2)
32 bigmat = append_col(mat1, mat2)
33 self.assertTrue(bigmat.size[0] == rows)
34 self.assertTrue(bigmat.size[1] == cols1+cols2)
35
36
37 class AppendRowTest(TestCase):
38 """
39 Tests for the :func:`append_row` function.
40 """
41
42 def test_new_dimensions(self):
43 """
44 If we append one matrix to another top-to-bottom, then
45 the result should have the same number of columns as the two
46 original matrices. However, the number of their rows should add
47 up to the number of rows in the the new combined matrix.
48 """
49 rows1 = random_natural()
50 rows2 = random_natural()
51 cols = random_natural()
52 mat1 = random_matrix(rows1, cols)
53 mat2 = random_matrix(rows2, cols)
54 bigmat = append_row(mat1, mat2)
55 self.assertTrue(bigmat.size[0] == rows1+rows2)
56 self.assertTrue(bigmat.size[1] == cols)
57
58
59 class EigenvaluesTest(TestCase):
60 """
61 Tests for the :func:`eigenvalues` function.
62 """
63
64 def test_eigenvalues_input_not_clobbered(self):
65 """
66 The eigenvalue functions provided by CVXOPT/LAPACK like to
67 overwrite the matrices that you pass into them as
68 arguments. This test makes sure that our :func:`eigenvalues`
69 function does not do the same.
70
71 We use a ``deepcopy`` here in case the ``copy`` used in the
72 :func:`eigenvalues` function is insufficient. If ``copy`` didn't
73 work and this test used it too, then this test would pass when
74 it shouldn't.
75 """
76 mat = random_matrix(random_natural())
77 symmat = mat + mat.trans()
78 symmat_copy = deepcopy(symmat)
79 dummy = eigenvalues(symmat)
80 self.assertTrue(norm(symmat - symmat_copy) < ABS_TOL)
81
82 def test_eigenvalues_of_symmat_are_real(self):
83 """
84 A real symmetric matrix has real eigenvalues, so if we start
85 with a symmetric matrix, then the two functions :func:`eigenvalues`
86 and :func:`eigenvalues_re` should agree on it.
87 """
88 mat = random_matrix(random_natural())
89 symmat = mat + mat.trans()
90 eigs1 = sorted(eigenvalues(symmat))
91 eigs2 = sorted(eigenvalues_re(symmat))
92 diffs = [abs(e1 - e2) for (e1, e2) in zip(eigs1, eigs2)]
93 self.assertTrue(all([diff < ABS_TOL for diff in diffs]))
94
95 def test_eigenvalues_of_identity(self):
96 """
97 All eigenvalues of the identity matrix should be one.
98 """
99 mat = identity(random_natural(), typecode='d')
100 eigs = eigenvalues(mat)
101 self.assertTrue(all([abs(ev - 1) < ABS_TOL for ev in eigs]))
102
103
104 class EigenvaluesRealPartTest(TestCase):
105 """
106 Tests for the :func:`eigenvalues_re` function.
107 """
108
109 def test_eigenvalues_re_input_not_clobbered(self):
110 """
111 The eigenvalue functions provided by CVXOPT/LAPACK like to
112 overwrite the matrices that you pass into them as
113 arguments. This test makes sure that our :func:`eigenvalues_re`
114 function does not do the same.
115
116 We use a ``deepcopy`` here in case the ``copy`` used in the
117 :func:`eigenvalues_re` function is insufficient. If ``copy`` didn't
118 work and this test used it too, then this test would pass when
119 it shouldn't.
120 """
121 mat = random_matrix(random_natural())
122 mat_copy = deepcopy(mat)
123 dummy = eigenvalues_re(mat)
124 self.assertTrue(norm(mat - mat_copy) < ABS_TOL)
125
126 def test_eigenvalues_re_of_identity(self):
127 """
128 All eigenvalues of the identity matrix should be one.
129 """
130 mat = identity(random_natural(), typecode='d')
131 eigs = eigenvalues_re(mat)
132 self.assertTrue(all([abs(ev - 1) < ABS_TOL for ev in eigs]))
133
134
135 class InnerProductTest(TestCase):
136 """
137 Tests for the :func:`inner_product` function.
138 """
139
140 def test_inner_product_with_self_is_norm_squared(self):
141 """
142 Ensure that the func:`inner_product` and :func:`norm` functions
143 are compatible by checking that the square of the norm of a
144 vector is its inner product with itself.
145 """
146 vec = random_matrix(random_natural(), 1)
147 actual = inner_product(vec, vec)
148 expected = norm(vec)**2
149 self.assertTrue(abs(actual - expected) < ABS_TOL)
150
151
152 class NormTest(TestCase):
153 """
154 Tests for the :func:`norm` function.
155 """
156
157 def test_norm_is_nonnegative(self):
158 """
159 Test one of the properties of a norm, that it is nonnegative.
160 """
161 mat = random_matrix(random_natural(), random_natural())
162 self.assertTrue(norm(mat) >= 0)
163
164
165 class ConditionNumberTest(TestCase):
166 """
167 Tests for the :func:`condition_number` function.
168 """
169
170 def test_condition_number_ge_one(self):
171 """
172 From the way that it is defined, the condition number should
173 always be greater than or equal to one.
174 """
175 mat = random_matrix(random_natural(), random_natural())
176 self.assertTrue(condition_number(mat) >= 1)