test_ddm.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. from sympy.testing.pytest import raises
  2. from sympy.external.gmpy import GROUND_TYPES
  3. from sympy.polys import ZZ, QQ
  4. from sympy.polys.matrices.ddm import DDM
  5. from sympy.polys.matrices.exceptions import (
  6. DMShapeError, DMNonInvertibleMatrixError, DMDomainError,
  7. DMBadInputError)
  8. def test_DDM_init():
  9. items = [[ZZ(0), ZZ(1), ZZ(2)], [ZZ(3), ZZ(4), ZZ(5)]]
  10. shape = (2, 3)
  11. ddm = DDM(items, shape, ZZ)
  12. assert ddm.shape == shape
  13. assert ddm.rows == 2
  14. assert ddm.cols == 3
  15. assert ddm.domain == ZZ
  16. raises(DMBadInputError, lambda: DDM([[ZZ(2), ZZ(3)]], (2, 2), ZZ))
  17. raises(DMBadInputError, lambda: DDM([[ZZ(1)], [ZZ(2), ZZ(3)]], (2, 2), ZZ))
  18. def test_DDM_getsetitem():
  19. ddm = DDM([[ZZ(2), ZZ(3)], [ZZ(4), ZZ(5)]], (2, 2), ZZ)
  20. assert ddm[0][0] == ZZ(2)
  21. assert ddm[0][1] == ZZ(3)
  22. assert ddm[1][0] == ZZ(4)
  23. assert ddm[1][1] == ZZ(5)
  24. raises(IndexError, lambda: ddm[2][0])
  25. raises(IndexError, lambda: ddm[0][2])
  26. ddm[0][0] = ZZ(-1)
  27. assert ddm[0][0] == ZZ(-1)
  28. def test_DDM_str():
  29. ddm = DDM([[ZZ(0), ZZ(1)], [ZZ(2), ZZ(3)]], (2, 2), ZZ)
  30. if GROUND_TYPES == 'gmpy': # pragma: no cover
  31. assert str(ddm) == '[[0, 1], [2, 3]]'
  32. assert repr(ddm) == 'DDM([[mpz(0), mpz(1)], [mpz(2), mpz(3)]], (2, 2), ZZ)'
  33. else: # pragma: no cover
  34. assert repr(ddm) == 'DDM([[0, 1], [2, 3]], (2, 2), ZZ)'
  35. assert str(ddm) == '[[0, 1], [2, 3]]'
  36. def test_DDM_eq():
  37. items = [[ZZ(0), ZZ(1)], [ZZ(2), ZZ(3)]]
  38. ddm1 = DDM(items, (2, 2), ZZ)
  39. ddm2 = DDM(items, (2, 2), ZZ)
  40. assert (ddm1 == ddm1) is True
  41. assert (ddm1 == items) is False
  42. assert (items == ddm1) is False
  43. assert (ddm1 == ddm2) is True
  44. assert (ddm2 == ddm1) is True
  45. assert (ddm1 != ddm1) is False
  46. assert (ddm1 != items) is True
  47. assert (items != ddm1) is True
  48. assert (ddm1 != ddm2) is False
  49. assert (ddm2 != ddm1) is False
  50. ddm3 = DDM([[ZZ(0), ZZ(1)], [ZZ(3), ZZ(3)]], (2, 2), ZZ)
  51. ddm3 = DDM(items, (2, 2), QQ)
  52. assert (ddm1 == ddm3) is False
  53. assert (ddm3 == ddm1) is False
  54. assert (ddm1 != ddm3) is True
  55. assert (ddm3 != ddm1) is True
  56. def test_DDM_convert_to():
  57. ddm = DDM([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
  58. assert ddm.convert_to(ZZ) == ddm
  59. ddmq = ddm.convert_to(QQ)
  60. assert ddmq.domain == QQ
  61. def test_DDM_zeros():
  62. ddmz = DDM.zeros((3, 4), QQ)
  63. assert list(ddmz) == [[QQ(0)] * 4] * 3
  64. assert ddmz.shape == (3, 4)
  65. assert ddmz.domain == QQ
  66. def test_DDM_ones():
  67. ddmone = DDM.ones((2, 3), QQ)
  68. assert list(ddmone) == [[QQ(1)] * 3] * 2
  69. assert ddmone.shape == (2, 3)
  70. assert ddmone.domain == QQ
  71. def test_DDM_eye():
  72. ddmz = DDM.eye(3, QQ)
  73. f = lambda i, j: QQ(1) if i == j else QQ(0)
  74. assert list(ddmz) == [[f(i, j) for i in range(3)] for j in range(3)]
  75. assert ddmz.shape == (3, 3)
  76. assert ddmz.domain == QQ
  77. def test_DDM_copy():
  78. ddm1 = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
  79. ddm2 = ddm1.copy()
  80. assert (ddm1 == ddm2) is True
  81. ddm1[0][0] = QQ(-1)
  82. assert (ddm1 == ddm2) is False
  83. ddm2[0][0] = QQ(-1)
  84. assert (ddm1 == ddm2) is True
  85. def test_DDM_transpose():
  86. ddm = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
  87. ddmT = DDM([[QQ(1), QQ(2)]], (1, 2), QQ)
  88. assert ddm.transpose() == ddmT
  89. ddm02 = DDM([], (0, 2), QQ)
  90. ddm02T = DDM([[], []], (2, 0), QQ)
  91. assert ddm02.transpose() == ddm02T
  92. assert ddm02T.transpose() == ddm02
  93. ddm0 = DDM([], (0, 0), QQ)
  94. assert ddm0.transpose() == ddm0
  95. def test_DDM_add():
  96. A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
  97. B = DDM([[ZZ(3)], [ZZ(4)]], (2, 1), ZZ)
  98. C = DDM([[ZZ(4)], [ZZ(6)]], (2, 1), ZZ)
  99. AQ = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
  100. assert A + B == A.add(B) == C
  101. raises(DMShapeError, lambda: A + DDM([[ZZ(5)]], (1, 1), ZZ))
  102. raises(TypeError, lambda: A + ZZ(1))
  103. raises(TypeError, lambda: ZZ(1) + A)
  104. raises(DMDomainError, lambda: A + AQ)
  105. raises(DMDomainError, lambda: AQ + A)
  106. def test_DDM_sub():
  107. A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
  108. B = DDM([[ZZ(3)], [ZZ(4)]], (2, 1), ZZ)
  109. C = DDM([[ZZ(-2)], [ZZ(-2)]], (2, 1), ZZ)
  110. AQ = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
  111. D = DDM([[ZZ(5)]], (1, 1), ZZ)
  112. assert A - B == A.sub(B) == C
  113. raises(TypeError, lambda: A - ZZ(1))
  114. raises(TypeError, lambda: ZZ(1) - A)
  115. raises(DMShapeError, lambda: A - D)
  116. raises(DMShapeError, lambda: D - A)
  117. raises(DMShapeError, lambda: A.sub(D))
  118. raises(DMShapeError, lambda: D.sub(A))
  119. raises(DMDomainError, lambda: A - AQ)
  120. raises(DMDomainError, lambda: AQ - A)
  121. raises(DMDomainError, lambda: A.sub(AQ))
  122. raises(DMDomainError, lambda: AQ.sub(A))
  123. def test_DDM_neg():
  124. A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
  125. An = DDM([[ZZ(-1)], [ZZ(-2)]], (2, 1), ZZ)
  126. assert -A == A.neg() == An
  127. assert -An == An.neg() == A
  128. def test_DDM_mul():
  129. A = DDM([[ZZ(1)]], (1, 1), ZZ)
  130. A2 = DDM([[ZZ(2)]], (1, 1), ZZ)
  131. assert A * ZZ(2) == A2
  132. assert ZZ(2) * A == A2
  133. raises(TypeError, lambda: [[1]] * A)
  134. raises(TypeError, lambda: A * [[1]])
  135. def test_DDM_matmul():
  136. A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
  137. B = DDM([[ZZ(3), ZZ(4)]], (1, 2), ZZ)
  138. AB = DDM([[ZZ(3), ZZ(4)], [ZZ(6), ZZ(8)]], (2, 2), ZZ)
  139. BA = DDM([[ZZ(11)]], (1, 1), ZZ)
  140. assert A @ B == A.matmul(B) == AB
  141. assert B @ A == B.matmul(A) == BA
  142. raises(TypeError, lambda: A @ 1)
  143. raises(TypeError, lambda: A @ [[3, 4]])
  144. Bq = DDM([[QQ(3), QQ(4)]], (1, 2), QQ)
  145. raises(DMDomainError, lambda: A @ Bq)
  146. raises(DMDomainError, lambda: Bq @ A)
  147. C = DDM([[ZZ(1)]], (1, 1), ZZ)
  148. assert A @ C == A.matmul(C) == A
  149. raises(DMShapeError, lambda: C @ A)
  150. raises(DMShapeError, lambda: C.matmul(A))
  151. Z04 = DDM([], (0, 4), ZZ)
  152. Z40 = DDM([[]]*4, (4, 0), ZZ)
  153. Z50 = DDM([[]]*5, (5, 0), ZZ)
  154. Z05 = DDM([], (0, 5), ZZ)
  155. Z45 = DDM([[0] * 5] * 4, (4, 5), ZZ)
  156. Z54 = DDM([[0] * 4] * 5, (5, 4), ZZ)
  157. Z00 = DDM([], (0, 0), ZZ)
  158. assert Z04 @ Z45 == Z04.matmul(Z45) == Z05
  159. assert Z45 @ Z50 == Z45.matmul(Z50) == Z40
  160. assert Z00 @ Z04 == Z00.matmul(Z04) == Z04
  161. assert Z50 @ Z00 == Z50.matmul(Z00) == Z50
  162. assert Z00 @ Z00 == Z00.matmul(Z00) == Z00
  163. assert Z50 @ Z04 == Z50.matmul(Z04) == Z54
  164. raises(DMShapeError, lambda: Z05 @ Z40)
  165. raises(DMShapeError, lambda: Z05.matmul(Z40))
  166. def test_DDM_hstack():
  167. A = DDM([[ZZ(1), ZZ(2), ZZ(3)]], (1, 3), ZZ)
  168. B = DDM([[ZZ(4), ZZ(5)]], (1, 2), ZZ)
  169. C = DDM([[ZZ(6)]], (1, 1), ZZ)
  170. Ah = A.hstack(B)
  171. assert Ah.shape == (1, 5)
  172. assert Ah.domain == ZZ
  173. assert Ah == DDM([[ZZ(1), ZZ(2), ZZ(3), ZZ(4), ZZ(5)]], (1, 5), ZZ)
  174. Ah = A.hstack(B, C)
  175. assert Ah.shape == (1, 6)
  176. assert Ah.domain == ZZ
  177. assert Ah == DDM([[ZZ(1), ZZ(2), ZZ(3), ZZ(4), ZZ(5), ZZ(6)]], (1, 6), ZZ)
  178. def test_DDM_vstack():
  179. A = DDM([[ZZ(1)], [ZZ(2)], [ZZ(3)]], (3, 1), ZZ)
  180. B = DDM([[ZZ(4)], [ZZ(5)]], (2, 1), ZZ)
  181. C = DDM([[ZZ(6)]], (1, 1), ZZ)
  182. Ah = A.vstack(B)
  183. assert Ah.shape == (5, 1)
  184. assert Ah.domain == ZZ
  185. assert Ah == DDM([[ZZ(1)], [ZZ(2)], [ZZ(3)], [ZZ(4)], [ZZ(5)]], (5, 1), ZZ)
  186. Ah = A.vstack(B, C)
  187. assert Ah.shape == (6, 1)
  188. assert Ah.domain == ZZ
  189. assert Ah == DDM([[ZZ(1)], [ZZ(2)], [ZZ(3)], [ZZ(4)], [ZZ(5)], [ZZ(6)]], (6, 1), ZZ)
  190. def test_DDM_applyfunc():
  191. A = DDM([[ZZ(1), ZZ(2), ZZ(3)]], (1, 3), ZZ)
  192. B = DDM([[ZZ(2), ZZ(4), ZZ(6)]], (1, 3), ZZ)
  193. assert A.applyfunc(lambda x: 2*x, ZZ) == B
  194. def test_DDM_rref():
  195. A = DDM([], (0, 4), QQ)
  196. assert A.rref() == (A, [])
  197. A = DDM([[QQ(0), QQ(1)], [QQ(1), QQ(1)]], (2, 2), QQ)
  198. Ar = DDM([[QQ(1), QQ(0)], [QQ(0), QQ(1)]], (2, 2), QQ)
  199. pivots = [0, 1]
  200. assert A.rref() == (Ar, pivots)
  201. A = DDM([[QQ(1), QQ(2), QQ(1)], [QQ(3), QQ(4), QQ(1)]], (2, 3), QQ)
  202. Ar = DDM([[QQ(1), QQ(0), QQ(-1)], [QQ(0), QQ(1), QQ(1)]], (2, 3), QQ)
  203. pivots = [0, 1]
  204. assert A.rref() == (Ar, pivots)
  205. A = DDM([[QQ(3), QQ(4), QQ(1)], [QQ(1), QQ(2), QQ(1)]], (2, 3), QQ)
  206. Ar = DDM([[QQ(1), QQ(0), QQ(-1)], [QQ(0), QQ(1), QQ(1)]], (2, 3), QQ)
  207. pivots = [0, 1]
  208. assert A.rref() == (Ar, pivots)
  209. A = DDM([[QQ(1), QQ(0)], [QQ(1), QQ(3)], [QQ(0), QQ(1)]], (3, 2), QQ)
  210. Ar = DDM([[QQ(1), QQ(0)], [QQ(0), QQ(1)], [QQ(0), QQ(0)]], (3, 2), QQ)
  211. pivots = [0, 1]
  212. assert A.rref() == (Ar, pivots)
  213. A = DDM([[QQ(1), QQ(0), QQ(1)], [QQ(3), QQ(0), QQ(1)]], (2, 3), QQ)
  214. Ar = DDM([[QQ(1), QQ(0), QQ(0)], [QQ(0), QQ(0), QQ(1)]], (2, 3), QQ)
  215. pivots = [0, 2]
  216. assert A.rref() == (Ar, pivots)
  217. def test_DDM_nullspace():
  218. # more tests are in test_nullspace.py
  219. A = DDM([[QQ(1), QQ(1)], [QQ(1), QQ(1)]], (2, 2), QQ)
  220. Anull = DDM([[QQ(-1), QQ(1)]], (1, 2), QQ)
  221. nonpivots = [1]
  222. assert A.nullspace() == (Anull, nonpivots)
  223. def test_DDM_particular():
  224. A = DDM([[QQ(1), QQ(0)]], (1, 2), QQ)
  225. assert A.particular() == DDM.zeros((1, 1), QQ)
  226. def test_DDM_det():
  227. # 0x0 case
  228. A = DDM([], (0, 0), ZZ)
  229. assert A.det() == ZZ(1)
  230. # 1x1 case
  231. A = DDM([[ZZ(2)]], (1, 1), ZZ)
  232. assert A.det() == ZZ(2)
  233. # 2x2 case
  234. A = DDM([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  235. assert A.det() == ZZ(-2)
  236. # 3x3 with swap
  237. A = DDM([[ZZ(1), ZZ(2), ZZ(3)], [ZZ(1), ZZ(2), ZZ(4)], [ZZ(1), ZZ(2), ZZ(5)]], (3, 3), ZZ)
  238. assert A.det() == ZZ(0)
  239. # 2x2 QQ case
  240. A = DDM([[QQ(1, 2), QQ(1, 2)], [QQ(1, 3), QQ(1, 4)]], (2, 2), QQ)
  241. assert A.det() == QQ(-1, 24)
  242. # Nonsquare error
  243. A = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
  244. raises(DMShapeError, lambda: A.det())
  245. # Nonsquare error with empty matrix
  246. A = DDM([], (0, 1), ZZ)
  247. raises(DMShapeError, lambda: A.det())
  248. def test_DDM_inv():
  249. A = DDM([[QQ(1, 1), QQ(2, 1)], [QQ(3, 1), QQ(4, 1)]], (2, 2), QQ)
  250. Ainv = DDM([[QQ(-2, 1), QQ(1, 1)], [QQ(3, 2), QQ(-1, 2)]], (2, 2), QQ)
  251. assert A.inv() == Ainv
  252. A = DDM([[QQ(1), QQ(2)]], (1, 2), QQ)
  253. raises(DMShapeError, lambda: A.inv())
  254. A = DDM([[ZZ(2)]], (1, 1), ZZ)
  255. raises(DMDomainError, lambda: A.inv())
  256. A = DDM([], (0, 0), QQ)
  257. assert A.inv() == A
  258. A = DDM([[QQ(1), QQ(2)], [QQ(2), QQ(4)]], (2, 2), QQ)
  259. raises(DMNonInvertibleMatrixError, lambda: A.inv())
  260. def test_DDM_lu():
  261. A = DDM([[QQ(1), QQ(2)], [QQ(3), QQ(4)]], (2, 2), QQ)
  262. L, U, swaps = A.lu()
  263. assert L == DDM([[QQ(1), QQ(0)], [QQ(3), QQ(1)]], (2, 2), QQ)
  264. assert U == DDM([[QQ(1), QQ(2)], [QQ(0), QQ(-2)]], (2, 2), QQ)
  265. assert swaps == []
  266. A = [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 1], [0, 0, 1, 2]]
  267. Lexp = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 1, 1]]
  268. Uexp = [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]
  269. to_dom = lambda rows, dom: [[dom(e) for e in row] for row in rows]
  270. A = DDM(to_dom(A, QQ), (4, 4), QQ)
  271. Lexp = DDM(to_dom(Lexp, QQ), (4, 4), QQ)
  272. Uexp = DDM(to_dom(Uexp, QQ), (4, 4), QQ)
  273. L, U, swaps = A.lu()
  274. assert L == Lexp
  275. assert U == Uexp
  276. assert swaps == []
  277. def test_DDM_lu_solve():
  278. # Basic example
  279. A = DDM([[QQ(1), QQ(2)], [QQ(3), QQ(4)]], (2, 2), QQ)
  280. b = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
  281. x = DDM([[QQ(0)], [QQ(1, 2)]], (2, 1), QQ)
  282. assert A.lu_solve(b) == x
  283. # Example with swaps
  284. A = DDM([[QQ(0), QQ(2)], [QQ(3), QQ(4)]], (2, 2), QQ)
  285. assert A.lu_solve(b) == x
  286. # Overdetermined, consistent
  287. A = DDM([[QQ(1), QQ(2)], [QQ(3), QQ(4)], [QQ(5), QQ(6)]], (3, 2), QQ)
  288. b = DDM([[QQ(1)], [QQ(2)], [QQ(3)]], (3, 1), QQ)
  289. assert A.lu_solve(b) == x
  290. # Overdetermined, inconsistent
  291. b = DDM([[QQ(1)], [QQ(2)], [QQ(4)]], (3, 1), QQ)
  292. raises(DMNonInvertibleMatrixError, lambda: A.lu_solve(b))
  293. # Square, noninvertible
  294. A = DDM([[QQ(1), QQ(2)], [QQ(1), QQ(2)]], (2, 2), QQ)
  295. b = DDM([[QQ(1)], [QQ(2)]], (2, 1), QQ)
  296. raises(DMNonInvertibleMatrixError, lambda: A.lu_solve(b))
  297. # Underdetermined
  298. A = DDM([[QQ(1), QQ(2)]], (1, 2), QQ)
  299. b = DDM([[QQ(3)]], (1, 1), QQ)
  300. raises(NotImplementedError, lambda: A.lu_solve(b))
  301. # Domain mismatch
  302. bz = DDM([[ZZ(1)], [ZZ(2)]], (2, 1), ZZ)
  303. raises(DMDomainError, lambda: A.lu_solve(bz))
  304. # Shape mismatch
  305. b3 = DDM([[QQ(1)], [QQ(2)], [QQ(3)]], (3, 1), QQ)
  306. raises(DMShapeError, lambda: A.lu_solve(b3))
  307. def test_DDM_charpoly():
  308. A = DDM([], (0, 0), ZZ)
  309. assert A.charpoly() == [ZZ(1)]
  310. A = DDM([
  311. [ZZ(1), ZZ(2), ZZ(3)],
  312. [ZZ(4), ZZ(5), ZZ(6)],
  313. [ZZ(7), ZZ(8), ZZ(9)]], (3, 3), ZZ)
  314. Avec = [ZZ(1), ZZ(-15), ZZ(-18), ZZ(0)]
  315. assert A.charpoly() == Avec
  316. A = DDM([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
  317. raises(DMShapeError, lambda: A.charpoly())
  318. def test_DDM_getitem():
  319. dm = DDM([
  320. [ZZ(1), ZZ(2), ZZ(3)],
  321. [ZZ(4), ZZ(5), ZZ(6)],
  322. [ZZ(7), ZZ(8), ZZ(9)]], (3, 3), ZZ)
  323. assert dm.getitem(1, 1) == ZZ(5)
  324. assert dm.getitem(1, -2) == ZZ(5)
  325. assert dm.getitem(-1, -3) == ZZ(7)
  326. raises(IndexError, lambda: dm.getitem(3, 3))
  327. def test_DDM_setitem():
  328. dm = DDM.zeros((3, 3), ZZ)
  329. dm.setitem(0, 0, 1)
  330. dm.setitem(1, -2, 1)
  331. dm.setitem(-1, -1, 1)
  332. assert dm == DDM.eye(3, ZZ)
  333. raises(IndexError, lambda: dm.setitem(3, 3, 0))
  334. def test_DDM_extract_slice():
  335. dm = DDM([
  336. [ZZ(1), ZZ(2), ZZ(3)],
  337. [ZZ(4), ZZ(5), ZZ(6)],
  338. [ZZ(7), ZZ(8), ZZ(9)]], (3, 3), ZZ)
  339. assert dm.extract_slice(slice(0, 3), slice(0, 3)) == dm
  340. assert dm.extract_slice(slice(1, 3), slice(-2)) == DDM([[4], [7]], (2, 1), ZZ)
  341. assert dm.extract_slice(slice(1, 3), slice(-2)) == DDM([[4], [7]], (2, 1), ZZ)
  342. assert dm.extract_slice(slice(2, 3), slice(-2)) == DDM([[ZZ(7)]], (1, 1), ZZ)
  343. assert dm.extract_slice(slice(0, 2), slice(-2)) == DDM([[1], [4]], (2, 1), ZZ)
  344. assert dm.extract_slice(slice(-1), slice(-1)) == DDM([[1, 2], [4, 5]], (2, 2), ZZ)
  345. assert dm.extract_slice(slice(2), slice(3, 4)) == DDM([[], []], (2, 0), ZZ)
  346. assert dm.extract_slice(slice(3, 4), slice(2)) == DDM([], (0, 2), ZZ)
  347. assert dm.extract_slice(slice(3, 4), slice(3, 4)) == DDM([], (0, 0), ZZ)
  348. def test_DDM_extract():
  349. dm1 = DDM([
  350. [ZZ(1), ZZ(2), ZZ(3)],
  351. [ZZ(4), ZZ(5), ZZ(6)],
  352. [ZZ(7), ZZ(8), ZZ(9)]], (3, 3), ZZ)
  353. dm2 = DDM([
  354. [ZZ(6), ZZ(4)],
  355. [ZZ(3), ZZ(1)]], (2, 2), ZZ)
  356. assert dm1.extract([1, 0], [2, 0]) == dm2
  357. assert dm1.extract([-2, 0], [-1, 0]) == dm2
  358. assert dm1.extract([], []) == DDM.zeros((0, 0), ZZ)
  359. assert dm1.extract([1], []) == DDM.zeros((1, 0), ZZ)
  360. assert dm1.extract([], [1]) == DDM.zeros((0, 1), ZZ)
  361. raises(IndexError, lambda: dm2.extract([2], [0]))
  362. raises(IndexError, lambda: dm2.extract([0], [2]))
  363. raises(IndexError, lambda: dm2.extract([-3], [0]))
  364. raises(IndexError, lambda: dm2.extract([0], [-3]))
  365. def test_DDM_flat():
  366. dm = DDM([
  367. [ZZ(6), ZZ(4)],
  368. [ZZ(3), ZZ(1)]], (2, 2), ZZ)
  369. assert dm.flat() == [ZZ(6), ZZ(4), ZZ(3), ZZ(1)]
  370. def test_DDM_is_zero_matrix():
  371. A = DDM([[QQ(1), QQ(0)], [QQ(0), QQ(0)]], (2, 2), QQ)
  372. Azero = DDM.zeros((1, 2), QQ)
  373. assert A.is_zero_matrix() is False
  374. assert Azero.is_zero_matrix() is True
  375. def test_DDM_is_upper():
  376. # Wide matrices:
  377. A = DDM([
  378. [QQ(1), QQ(2), QQ(3), QQ(4)],
  379. [QQ(0), QQ(5), QQ(6), QQ(7)],
  380. [QQ(0), QQ(0), QQ(8), QQ(9)]
  381. ], (3, 4), QQ)
  382. B = DDM([
  383. [QQ(1), QQ(2), QQ(3), QQ(4)],
  384. [QQ(0), QQ(5), QQ(6), QQ(7)],
  385. [QQ(0), QQ(7), QQ(8), QQ(9)]
  386. ], (3, 4), QQ)
  387. assert A.is_upper() is True
  388. assert B.is_upper() is False
  389. # Tall matrices:
  390. A = DDM([
  391. [QQ(1), QQ(2), QQ(3)],
  392. [QQ(0), QQ(5), QQ(6)],
  393. [QQ(0), QQ(0), QQ(8)],
  394. [QQ(0), QQ(0), QQ(0)]
  395. ], (4, 3), QQ)
  396. B = DDM([
  397. [QQ(1), QQ(2), QQ(3)],
  398. [QQ(0), QQ(5), QQ(6)],
  399. [QQ(0), QQ(0), QQ(8)],
  400. [QQ(0), QQ(0), QQ(10)]
  401. ], (4, 3), QQ)
  402. assert A.is_upper() is True
  403. assert B.is_upper() is False
  404. def test_DDM_is_lower():
  405. # Tall matrices:
  406. A = DDM([
  407. [QQ(1), QQ(2), QQ(3), QQ(4)],
  408. [QQ(0), QQ(5), QQ(6), QQ(7)],
  409. [QQ(0), QQ(0), QQ(8), QQ(9)]
  410. ], (3, 4), QQ).transpose()
  411. B = DDM([
  412. [QQ(1), QQ(2), QQ(3), QQ(4)],
  413. [QQ(0), QQ(5), QQ(6), QQ(7)],
  414. [QQ(0), QQ(7), QQ(8), QQ(9)]
  415. ], (3, 4), QQ).transpose()
  416. assert A.is_lower() is True
  417. assert B.is_lower() is False
  418. # Wide matrices:
  419. A = DDM([
  420. [QQ(1), QQ(2), QQ(3)],
  421. [QQ(0), QQ(5), QQ(6)],
  422. [QQ(0), QQ(0), QQ(8)],
  423. [QQ(0), QQ(0), QQ(0)]
  424. ], (4, 3), QQ).transpose()
  425. B = DDM([
  426. [QQ(1), QQ(2), QQ(3)],
  427. [QQ(0), QQ(5), QQ(6)],
  428. [QQ(0), QQ(0), QQ(8)],
  429. [QQ(0), QQ(0), QQ(10)]
  430. ], (4, 3), QQ).transpose()
  431. assert A.is_lower() is True
  432. assert B.is_lower() is False