图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

403 lines
13 KiB

  1. '''Functions returning normal forms of matrices'''
  2. from collections import defaultdict
  3. from .domainmatrix import DomainMatrix
  4. from .exceptions import DMDomainError, DMShapeError
  5. from sympy.ntheory.modular import symmetric_residue
  6. from sympy.polys.domains import QQ, ZZ
  7. # TODO (future work):
  8. # There are faster algorithms for Smith and Hermite normal forms, which
  9. # we should implement. See e.g. the Kannan-Bachem algorithm:
  10. # <https://www.researchgate.net/publication/220617516_Polynomial_Algorithms_for_Computing_the_Smith_and_Hermite_Normal_Forms_of_an_Integer_Matrix>
  11. def smith_normal_form(m):
  12. '''
  13. Return the Smith Normal Form of a matrix `m` over the ring `domain`.
  14. This will only work if the ring is a principal ideal domain.
  15. Examples
  16. ========
  17. >>> from sympy import ZZ
  18. >>> from sympy.polys.matrices import DomainMatrix
  19. >>> from sympy.polys.matrices.normalforms import smith_normal_form
  20. >>> m = DomainMatrix([[ZZ(12), ZZ(6), ZZ(4)],
  21. ... [ZZ(3), ZZ(9), ZZ(6)],
  22. ... [ZZ(2), ZZ(16), ZZ(14)]], (3, 3), ZZ)
  23. >>> print(smith_normal_form(m).to_Matrix())
  24. Matrix([[1, 0, 0], [0, 10, 0], [0, 0, -30]])
  25. '''
  26. invs = invariant_factors(m)
  27. smf = DomainMatrix.diag(invs, m.domain, m.shape)
  28. return smf
  29. def add_columns(m, i, j, a, b, c, d):
  30. # replace m[:, i] by a*m[:, i] + b*m[:, j]
  31. # and m[:, j] by c*m[:, i] + d*m[:, j]
  32. for k in range(len(m)):
  33. e = m[k][i]
  34. m[k][i] = a*e + b*m[k][j]
  35. m[k][j] = c*e + d*m[k][j]
  36. def invariant_factors(m):
  37. '''
  38. Return the tuple of abelian invariants for a matrix `m`
  39. (as in the Smith-Normal form)
  40. References
  41. ==========
  42. [1] https://en.wikipedia.org/wiki/Smith_normal_form#Algorithm
  43. [2] http://sierra.nmsu.edu/morandi/notes/SmithNormalForm.pdf
  44. '''
  45. domain = m.domain
  46. if not domain.is_PID:
  47. msg = "The matrix entries must be over a principal ideal domain"
  48. raise ValueError(msg)
  49. if 0 in m.shape:
  50. return ()
  51. rows, cols = shape = m.shape
  52. m = list(m.to_dense().rep)
  53. def add_rows(m, i, j, a, b, c, d):
  54. # replace m[i, :] by a*m[i, :] + b*m[j, :]
  55. # and m[j, :] by c*m[i, :] + d*m[j, :]
  56. for k in range(cols):
  57. e = m[i][k]
  58. m[i][k] = a*e + b*m[j][k]
  59. m[j][k] = c*e + d*m[j][k]
  60. def clear_column(m):
  61. # make m[1:, 0] zero by row and column operations
  62. if m[0][0] == 0:
  63. return m # pragma: nocover
  64. pivot = m[0][0]
  65. for j in range(1, rows):
  66. if m[j][0] == 0:
  67. continue
  68. d, r = domain.div(m[j][0], pivot)
  69. if r == 0:
  70. add_rows(m, 0, j, 1, 0, -d, 1)
  71. else:
  72. a, b, g = domain.gcdex(pivot, m[j][0])
  73. d_0 = domain.div(m[j][0], g)[0]
  74. d_j = domain.div(pivot, g)[0]
  75. add_rows(m, 0, j, a, b, d_0, -d_j)
  76. pivot = g
  77. return m
  78. def clear_row(m):
  79. # make m[0, 1:] zero by row and column operations
  80. if m[0][0] == 0:
  81. return m # pragma: nocover
  82. pivot = m[0][0]
  83. for j in range(1, cols):
  84. if m[0][j] == 0:
  85. continue
  86. d, r = domain.div(m[0][j], pivot)
  87. if r == 0:
  88. add_columns(m, 0, j, 1, 0, -d, 1)
  89. else:
  90. a, b, g = domain.gcdex(pivot, m[0][j])
  91. d_0 = domain.div(m[0][j], g)[0]
  92. d_j = domain.div(pivot, g)[0]
  93. add_columns(m, 0, j, a, b, d_0, -d_j)
  94. pivot = g
  95. return m
  96. # permute the rows and columns until m[0,0] is non-zero if possible
  97. ind = [i for i in range(rows) if m[i][0] != 0]
  98. if ind and ind[0] != 0:
  99. m[0], m[ind[0]] = m[ind[0]], m[0]
  100. else:
  101. ind = [j for j in range(cols) if m[0][j] != 0]
  102. if ind and ind[0] != 0:
  103. for row in m:
  104. row[0], row[ind[0]] = row[ind[0]], row[0]
  105. # make the first row and column except m[0,0] zero
  106. while (any(m[0][i] != 0 for i in range(1,cols)) or
  107. any(m[i][0] != 0 for i in range(1,rows))):
  108. m = clear_column(m)
  109. m = clear_row(m)
  110. if 1 in shape:
  111. invs = ()
  112. else:
  113. lower_right = DomainMatrix([r[1:] for r in m[1:]], (rows-1, cols-1), domain)
  114. invs = invariant_factors(lower_right)
  115. if m[0][0]:
  116. result = [m[0][0]]
  117. result.extend(invs)
  118. # in case m[0] doesn't divide the invariants of the rest of the matrix
  119. for i in range(len(result)-1):
  120. if result[i] and domain.div(result[i+1], result[i])[1] != 0:
  121. g = domain.gcd(result[i+1], result[i])
  122. result[i+1] = domain.div(result[i], g)[0]*result[i+1]
  123. result[i] = g
  124. else:
  125. break
  126. else:
  127. result = invs + (m[0][0],)
  128. return tuple(result)
  129. def _gcdex(a, b):
  130. r"""
  131. This supports the functions that compute Hermite Normal Form.
  132. Explanation
  133. ===========
  134. Let x, y be the coefficients returned by the extended Euclidean
  135. Algorithm, so that x*a + y*b = g. In the algorithms for computing HNF,
  136. it is critical that x, y not only satisfy the condition of being small
  137. in magnitude -- namely that |x| <= |b|/g, |y| <- |a|/g -- but also that
  138. y == 0 when a | b.
  139. """
  140. x, y, g = ZZ.gcdex(a, b)
  141. if a != 0 and b % a == 0:
  142. y = 0
  143. x = -1 if a < 0 else 1
  144. return x, y, g
  145. def _hermite_normal_form(A):
  146. r"""
  147. Compute the Hermite Normal Form of DomainMatrix *A* over :ref:`ZZ`.
  148. Parameters
  149. ==========
  150. A : :py:class:`~.DomainMatrix` over domain :ref:`ZZ`.
  151. Returns
  152. =======
  153. :py:class:`~.DomainMatrix`
  154. The HNF of matrix *A*.
  155. Raises
  156. ======
  157. DMDomainError
  158. If the domain of the matrix is not :ref:`ZZ`.
  159. References
  160. ==========
  161. .. [1] Cohen, H. *A Course in Computational Algebraic Number Theory.*
  162. (See Algorithm 2.4.5.)
  163. """
  164. if not A.domain.is_ZZ:
  165. raise DMDomainError('Matrix must be over domain ZZ.')
  166. # We work one row at a time, starting from the bottom row, and working our
  167. # way up. The total number of rows we will consider is min(m, n), where
  168. # A is an m x n matrix.
  169. m, n = A.shape
  170. rows = min(m, n)
  171. A = A.to_dense().rep.copy()
  172. # Our goal is to put pivot entries in the rightmost columns.
  173. # Invariant: Before processing each row, k should be the index of the
  174. # leftmost column in which we have so far put a pivot.
  175. k = n
  176. for i in range(m - 1, m - 1 - rows, -1):
  177. k -= 1
  178. # k now points to the column in which we want to put a pivot.
  179. # We want zeros in all entries to the left of the pivot column.
  180. for j in range(k - 1, -1, -1):
  181. if A[i][j] != 0:
  182. # Replace cols j, k by lin combs of these cols such that, in row i,
  183. # col j has 0, while col k has the gcd of their row i entries. Note
  184. # that this ensures a nonzero entry in col k.
  185. u, v, d = _gcdex(A[i][k], A[i][j])
  186. r, s = A[i][k] // d, A[i][j] // d
  187. add_columns(A, k, j, u, v, -s, r)
  188. b = A[i][k]
  189. # Do not want the pivot entry to be negative.
  190. if b < 0:
  191. add_columns(A, k, k, -1, 0, -1, 0)
  192. b = -b
  193. # The pivot entry will be 0 iff the row was 0 from the pivot col all the
  194. # way to the left. In this case, we are still working on the same pivot
  195. # col for the next row. Therefore:
  196. if b == 0:
  197. k += 1
  198. # If the pivot entry is nonzero, then we want to reduce all entries to its
  199. # right in the sense of the division algorithm, i.e. make them all remainders
  200. # w.r.t. the pivot as divisor.
  201. else:
  202. for j in range(k + 1, n):
  203. q = A[i][j] // b
  204. add_columns(A, j, k, 1, -q, 0, 1)
  205. # Finally, the HNF consists of those columns of A in which we succeeded in making
  206. # a nonzero pivot.
  207. return DomainMatrix.from_rep(A)[:, k:]
  208. def _hermite_normal_form_modulo_D(A, D):
  209. r"""
  210. Perform the mod *D* Hermite Normal Form reduction algorithm on
  211. :py:class:`~.DomainMatrix` *A*.
  212. Explanation
  213. ===========
  214. If *A* is an $m \times n$ matrix of rank $m$, having Hermite Normal Form
  215. $W$, and if *D* is any positive integer known in advance to be a multiple
  216. of $\det(W)$, then the HNF of *A* can be computed by an algorithm that
  217. works mod *D* in order to prevent coefficient explosion.
  218. Parameters
  219. ==========
  220. A : :py:class:`~.DomainMatrix` over :ref:`ZZ`
  221. $m \times n$ matrix, having rank $m$.
  222. D : :ref:`ZZ`
  223. Positive integer, known to be a multiple of the determinant of the
  224. HNF of *A*.
  225. Returns
  226. =======
  227. :py:class:`~.DomainMatrix`
  228. The HNF of matrix *A*.
  229. Raises
  230. ======
  231. DMDomainError
  232. If the domain of the matrix is not :ref:`ZZ`, or
  233. if *D* is given but is not in :ref:`ZZ`.
  234. DMShapeError
  235. If the matrix has more rows than columns.
  236. References
  237. ==========
  238. .. [1] Cohen, H. *A Course in Computational Algebraic Number Theory.*
  239. (See Algorithm 2.4.8.)
  240. """
  241. if not A.domain.is_ZZ:
  242. raise DMDomainError('Matrix must be over domain ZZ.')
  243. if not ZZ.of_type(D) or D < 1:
  244. raise DMDomainError('Modulus D must be positive element of domain ZZ.')
  245. def add_columns_mod_R(m, R, i, j, a, b, c, d):
  246. # replace m[:, i] by (a*m[:, i] + b*m[:, j]) % R
  247. # and m[:, j] by (c*m[:, i] + d*m[:, j]) % R
  248. for k in range(len(m)):
  249. e = m[k][i]
  250. m[k][i] = symmetric_residue((a * e + b * m[k][j]) % R, R)
  251. m[k][j] = symmetric_residue((c * e + d * m[k][j]) % R, R)
  252. W = defaultdict(dict)
  253. m, n = A.shape
  254. if n < m:
  255. raise DMShapeError('Matrix must have at least as many columns as rows.')
  256. A = A.to_dense().rep.copy()
  257. k = n
  258. R = D
  259. for i in range(m - 1, -1, -1):
  260. k -= 1
  261. for j in range(k - 1, -1, -1):
  262. if A[i][j] != 0:
  263. u, v, d = _gcdex(A[i][k], A[i][j])
  264. r, s = A[i][k] // d, A[i][j] // d
  265. add_columns_mod_R(A, R, k, j, u, v, -s, r)
  266. b = A[i][k]
  267. if b == 0:
  268. A[i][k] = b = R
  269. u, v, d = _gcdex(b, R)
  270. for ii in range(m):
  271. W[ii][i] = u*A[ii][k] % R
  272. if W[i][i] == 0:
  273. W[i][i] = R
  274. for j in range(i + 1, m):
  275. q = W[i][j] // W[i][i]
  276. add_columns(W, j, i, 1, -q, 0, 1)
  277. R //= d
  278. return DomainMatrix(W, (m, m), ZZ).to_dense()
  279. def hermite_normal_form(A, *, D=None, check_rank=False):
  280. r"""
  281. Compute the Hermite Normal Form of :py:class:`~.DomainMatrix` *A* over
  282. :ref:`ZZ`.
  283. Examples
  284. ========
  285. >>> from sympy import ZZ
  286. >>> from sympy.polys.matrices import DomainMatrix
  287. >>> from sympy.polys.matrices.normalforms import hermite_normal_form
  288. >>> m = DomainMatrix([[ZZ(12), ZZ(6), ZZ(4)],
  289. ... [ZZ(3), ZZ(9), ZZ(6)],
  290. ... [ZZ(2), ZZ(16), ZZ(14)]], (3, 3), ZZ)
  291. >>> print(hermite_normal_form(m).to_Matrix())
  292. Matrix([[10, 0, 2], [0, 15, 3], [0, 0, 2]])
  293. Parameters
  294. ==========
  295. A : $m \times n$ ``DomainMatrix`` over :ref:`ZZ`.
  296. D : :ref:`ZZ`, optional
  297. Let $W$ be the HNF of *A*. If known in advance, a positive integer *D*
  298. being any multiple of $\det(W)$ may be provided. In this case, if *A*
  299. also has rank $m$, then we may use an alternative algorithm that works
  300. mod *D* in order to prevent coefficient explosion.
  301. check_rank : boolean, optional (default=False)
  302. The basic assumption is that, if you pass a value for *D*, then
  303. you already believe that *A* has rank $m$, so we do not waste time
  304. checking it for you. If you do want this to be checked (and the
  305. ordinary, non-modulo *D* algorithm to be used if the check fails), then
  306. set *check_rank* to ``True``.
  307. Returns
  308. =======
  309. :py:class:`~.DomainMatrix`
  310. The HNF of matrix *A*.
  311. Raises
  312. ======
  313. DMDomainError
  314. If the domain of the matrix is not :ref:`ZZ`, or
  315. if *D* is given but is not in :ref:`ZZ`.
  316. DMShapeError
  317. If the mod *D* algorithm is used but the matrix has more rows than
  318. columns.
  319. References
  320. ==========
  321. .. [1] Cohen, H. *A Course in Computational Algebraic Number Theory.*
  322. (See Algorithms 2.4.5 and 2.4.8.)
  323. """
  324. if not A.domain.is_ZZ:
  325. raise DMDomainError('Matrix must be over domain ZZ.')
  326. if D is not None and (not check_rank or A.convert_to(QQ).rank() == A.shape[0]):
  327. return _hermite_normal_form_modulo_D(A, D)
  328. else:
  329. return _hermite_normal_form(A)