test__remove_redundancy.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. """
  2. Unit test for Linear Programming via Simplex Algorithm.
  3. """
  4. # TODO: add tests for:
  5. # https://github.com/scipy/scipy/issues/5400
  6. # https://github.com/scipy/scipy/issues/6690
  7. import numpy as np
  8. from numpy.testing import (
  9. assert_,
  10. assert_allclose,
  11. assert_equal)
  12. from .test_linprog import magic_square
  13. from scipy.optimize._remove_redundancy import _remove_redundancy_svd
  14. from scipy.optimize._remove_redundancy import _remove_redundancy_pivot_dense
  15. from scipy.optimize._remove_redundancy import _remove_redundancy_pivot_sparse
  16. from scipy.optimize._remove_redundancy import _remove_redundancy_id
  17. from scipy.sparse import csc_matrix
  18. def setup_module():
  19. np.random.seed(2017)
  20. def _assert_success(
  21. res,
  22. desired_fun=None,
  23. desired_x=None,
  24. rtol=1e-7,
  25. atol=1e-7):
  26. # res: linprog result object
  27. # desired_fun: desired objective function value or None
  28. # desired_x: desired solution or None
  29. assert_(res.success)
  30. assert_equal(res.status, 0)
  31. if desired_fun is not None:
  32. assert_allclose(
  33. res.fun,
  34. desired_fun,
  35. err_msg="converged to an unexpected objective value",
  36. rtol=rtol,
  37. atol=atol)
  38. if desired_x is not None:
  39. assert_allclose(
  40. res.x,
  41. desired_x,
  42. err_msg="converged to an unexpected solution",
  43. rtol=rtol,
  44. atol=atol)
  45. def redundancy_removed(A, B):
  46. """Checks whether a matrix contains only independent rows of another"""
  47. for rowA in A:
  48. # `rowA in B` is not a reliable check
  49. for rowB in B:
  50. if np.all(rowA == rowB):
  51. break
  52. else:
  53. return False
  54. return A.shape[0] == np.linalg.matrix_rank(A) == np.linalg.matrix_rank(B)
  55. class RRCommonTests:
  56. def test_no_redundancy(self):
  57. m, n = 10, 10
  58. A0 = np.random.rand(m, n)
  59. b0 = np.random.rand(m)
  60. A1, b1, status, message = self.rr(A0, b0)
  61. assert_allclose(A0, A1)
  62. assert_allclose(b0, b1)
  63. assert_equal(status, 0)
  64. def test_infeasible_zero_row(self):
  65. A = np.eye(3)
  66. A[1, :] = 0
  67. b = np.random.rand(3)
  68. A1, b1, status, message = self.rr(A, b)
  69. assert_equal(status, 2)
  70. def test_remove_zero_row(self):
  71. A = np.eye(3)
  72. A[1, :] = 0
  73. b = np.random.rand(3)
  74. b[1] = 0
  75. A1, b1, status, message = self.rr(A, b)
  76. assert_equal(status, 0)
  77. assert_allclose(A1, A[[0, 2], :])
  78. assert_allclose(b1, b[[0, 2]])
  79. def test_infeasible_m_gt_n(self):
  80. m, n = 20, 10
  81. A0 = np.random.rand(m, n)
  82. b0 = np.random.rand(m)
  83. A1, b1, status, message = self.rr(A0, b0)
  84. assert_equal(status, 2)
  85. def test_infeasible_m_eq_n(self):
  86. m, n = 10, 10
  87. A0 = np.random.rand(m, n)
  88. b0 = np.random.rand(m)
  89. A0[-1, :] = 2 * A0[-2, :]
  90. A1, b1, status, message = self.rr(A0, b0)
  91. assert_equal(status, 2)
  92. def test_infeasible_m_lt_n(self):
  93. m, n = 9, 10
  94. A0 = np.random.rand(m, n)
  95. b0 = np.random.rand(m)
  96. A0[-1, :] = np.arange(m - 1).dot(A0[:-1])
  97. A1, b1, status, message = self.rr(A0, b0)
  98. assert_equal(status, 2)
  99. def test_m_gt_n(self):
  100. np.random.seed(2032)
  101. m, n = 20, 10
  102. A0 = np.random.rand(m, n)
  103. b0 = np.random.rand(m)
  104. x = np.linalg.solve(A0[:n, :], b0[:n])
  105. b0[n:] = A0[n:, :].dot(x)
  106. A1, b1, status, message = self.rr(A0, b0)
  107. assert_equal(status, 0)
  108. assert_equal(A1.shape[0], n)
  109. assert_equal(np.linalg.matrix_rank(A1), n)
  110. def test_m_gt_n_rank_deficient(self):
  111. m, n = 20, 10
  112. A0 = np.zeros((m, n))
  113. A0[:, 0] = 1
  114. b0 = np.ones(m)
  115. A1, b1, status, message = self.rr(A0, b0)
  116. assert_equal(status, 0)
  117. assert_allclose(A1, A0[0:1, :])
  118. assert_allclose(b1, b0[0])
  119. def test_m_lt_n_rank_deficient(self):
  120. m, n = 9, 10
  121. A0 = np.random.rand(m, n)
  122. b0 = np.random.rand(m)
  123. A0[-1, :] = np.arange(m - 1).dot(A0[:-1])
  124. b0[-1] = np.arange(m - 1).dot(b0[:-1])
  125. A1, b1, status, message = self.rr(A0, b0)
  126. assert_equal(status, 0)
  127. assert_equal(A1.shape[0], 8)
  128. assert_equal(np.linalg.matrix_rank(A1), 8)
  129. def test_dense1(self):
  130. A = np.ones((6, 6))
  131. A[0, :3] = 0
  132. A[1, 3:] = 0
  133. A[3:, ::2] = -1
  134. A[3, :2] = 0
  135. A[4, 2:] = 0
  136. b = np.zeros(A.shape[0])
  137. A1, b1, status, message = self.rr(A, b)
  138. assert_(redundancy_removed(A1, A))
  139. assert_equal(status, 0)
  140. def test_dense2(self):
  141. A = np.eye(6)
  142. A[-2, -1] = 1
  143. A[-1, :] = 1
  144. b = np.zeros(A.shape[0])
  145. A1, b1, status, message = self.rr(A, b)
  146. assert_(redundancy_removed(A1, A))
  147. assert_equal(status, 0)
  148. def test_dense3(self):
  149. A = np.eye(6)
  150. A[-2, -1] = 1
  151. A[-1, :] = 1
  152. b = np.random.rand(A.shape[0])
  153. b[-1] = np.sum(b[:-1])
  154. A1, b1, status, message = self.rr(A, b)
  155. assert_(redundancy_removed(A1, A))
  156. assert_equal(status, 0)
  157. def test_m_gt_n_sparse(self):
  158. np.random.seed(2013)
  159. m, n = 20, 5
  160. p = 0.1
  161. A = np.random.rand(m, n)
  162. A[np.random.rand(m, n) > p] = 0
  163. rank = np.linalg.matrix_rank(A)
  164. b = np.zeros(A.shape[0])
  165. A1, b1, status, message = self.rr(A, b)
  166. assert_equal(status, 0)
  167. assert_equal(A1.shape[0], rank)
  168. assert_equal(np.linalg.matrix_rank(A1), rank)
  169. def test_m_lt_n_sparse(self):
  170. np.random.seed(2017)
  171. m, n = 20, 50
  172. p = 0.05
  173. A = np.random.rand(m, n)
  174. A[np.random.rand(m, n) > p] = 0
  175. rank = np.linalg.matrix_rank(A)
  176. b = np.zeros(A.shape[0])
  177. A1, b1, status, message = self.rr(A, b)
  178. assert_equal(status, 0)
  179. assert_equal(A1.shape[0], rank)
  180. assert_equal(np.linalg.matrix_rank(A1), rank)
  181. def test_m_eq_n_sparse(self):
  182. np.random.seed(2017)
  183. m, n = 100, 100
  184. p = 0.01
  185. A = np.random.rand(m, n)
  186. A[np.random.rand(m, n) > p] = 0
  187. rank = np.linalg.matrix_rank(A)
  188. b = np.zeros(A.shape[0])
  189. A1, b1, status, message = self.rr(A, b)
  190. assert_equal(status, 0)
  191. assert_equal(A1.shape[0], rank)
  192. assert_equal(np.linalg.matrix_rank(A1), rank)
  193. def test_magic_square(self):
  194. A, b, c, numbers, _ = magic_square(3)
  195. A1, b1, status, message = self.rr(A, b)
  196. assert_equal(status, 0)
  197. assert_equal(A1.shape[0], 23)
  198. assert_equal(np.linalg.matrix_rank(A1), 23)
  199. def test_magic_square2(self):
  200. A, b, c, numbers, _ = magic_square(4)
  201. A1, b1, status, message = self.rr(A, b)
  202. assert_equal(status, 0)
  203. assert_equal(A1.shape[0], 39)
  204. assert_equal(np.linalg.matrix_rank(A1), 39)
  205. class TestRRSVD(RRCommonTests):
  206. def rr(self, A, b):
  207. return _remove_redundancy_svd(A, b)
  208. class TestRRPivotDense(RRCommonTests):
  209. def rr(self, A, b):
  210. return _remove_redundancy_pivot_dense(A, b)
  211. class TestRRID(RRCommonTests):
  212. def rr(self, A, b):
  213. return _remove_redundancy_id(A, b)
  214. class TestRRPivotSparse(RRCommonTests):
  215. def rr(self, A, b):
  216. rr_res = _remove_redundancy_pivot_sparse(csc_matrix(A), b)
  217. A1, b1, status, message = rr_res
  218. return A1.toarray(), b1, status, message