fglmtools.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. """Implementation of matrix FGLM Groebner basis conversion algorithm. """
  2. from sympy.polys.monomials import monomial_mul, monomial_div
  3. def matrix_fglm(F, ring, O_to):
  4. """
  5. Converts the reduced Groebner basis ``F`` of a zero-dimensional
  6. ideal w.r.t. ``O_from`` to a reduced Groebner basis
  7. w.r.t. ``O_to``.
  8. References
  9. ==========
  10. .. [1] J.C. Faugere, P. Gianni, D. Lazard, T. Mora (1994). Efficient
  11. Computation of Zero-dimensional Groebner Bases by Change of
  12. Ordering
  13. """
  14. domain = ring.domain
  15. ngens = ring.ngens
  16. ring_to = ring.clone(order=O_to)
  17. old_basis = _basis(F, ring)
  18. M = _representing_matrices(old_basis, F, ring)
  19. # V contains the normalforms (wrt O_from) of S
  20. S = [ring.zero_monom]
  21. V = [[domain.one] + [domain.zero] * (len(old_basis) - 1)]
  22. G = []
  23. L = [(i, 0) for i in range(ngens)] # (i, j) corresponds to x_i * S[j]
  24. L.sort(key=lambda k_l: O_to(_incr_k(S[k_l[1]], k_l[0])), reverse=True)
  25. t = L.pop()
  26. P = _identity_matrix(len(old_basis), domain)
  27. while True:
  28. s = len(S)
  29. v = _matrix_mul(M[t[0]], V[t[1]])
  30. _lambda = _matrix_mul(P, v)
  31. if all(_lambda[i] == domain.zero for i in range(s, len(old_basis))):
  32. # there is a linear combination of v by V
  33. lt = ring.term_new(_incr_k(S[t[1]], t[0]), domain.one)
  34. rest = ring.from_dict({S[i]: _lambda[i] for i in range(s)})
  35. g = (lt - rest).set_ring(ring_to)
  36. if g:
  37. G.append(g)
  38. else:
  39. # v is linearly independent from V
  40. P = _update(s, _lambda, P)
  41. S.append(_incr_k(S[t[1]], t[0]))
  42. V.append(v)
  43. L.extend([(i, s) for i in range(ngens)])
  44. L = list(set(L))
  45. L.sort(key=lambda k_l: O_to(_incr_k(S[k_l[1]], k_l[0])), reverse=True)
  46. L = [(k, l) for (k, l) in L if all(monomial_div(_incr_k(S[l], k), g.LM) is None for g in G)]
  47. if not L:
  48. G = [ g.monic() for g in G ]
  49. return sorted(G, key=lambda g: O_to(g.LM), reverse=True)
  50. t = L.pop()
  51. def _incr_k(m, k):
  52. return tuple(list(m[:k]) + [m[k] + 1] + list(m[k + 1:]))
  53. def _identity_matrix(n, domain):
  54. M = [[domain.zero]*n for _ in range(n)]
  55. for i in range(n):
  56. M[i][i] = domain.one
  57. return M
  58. def _matrix_mul(M, v):
  59. return [sum([row[i] * v[i] for i in range(len(v))]) for row in M]
  60. def _update(s, _lambda, P):
  61. """
  62. Update ``P`` such that for the updated `P'` `P' v = e_{s}`.
  63. """
  64. k = min([j for j in range(s, len(_lambda)) if _lambda[j] != 0])
  65. for r in range(len(_lambda)):
  66. if r != k:
  67. P[r] = [P[r][j] - (P[k][j] * _lambda[r]) / _lambda[k] for j in range(len(P[r]))]
  68. P[k] = [P[k][j] / _lambda[k] for j in range(len(P[k]))]
  69. P[k], P[s] = P[s], P[k]
  70. return P
  71. def _representing_matrices(basis, G, ring):
  72. r"""
  73. Compute the matrices corresponding to the linear maps `m \mapsto
  74. x_i m` for all variables `x_i`.
  75. """
  76. domain = ring.domain
  77. u = ring.ngens-1
  78. def var(i):
  79. return tuple([0] * i + [1] + [0] * (u - i))
  80. def representing_matrix(m):
  81. M = [[domain.zero] * len(basis) for _ in range(len(basis))]
  82. for i, v in enumerate(basis):
  83. r = ring.term_new(monomial_mul(m, v), domain.one).rem(G)
  84. for monom, coeff in r.terms():
  85. j = basis.index(monom)
  86. M[j][i] = coeff
  87. return M
  88. return [representing_matrix(var(i)) for i in range(u + 1)]
  89. def _basis(G, ring):
  90. r"""
  91. Computes a list of monomials which are not divisible by the leading
  92. monomials wrt to ``O`` of ``G``. These monomials are a basis of
  93. `K[X_1, \ldots, X_n]/(G)`.
  94. """
  95. order = ring.order
  96. leading_monomials = [g.LM for g in G]
  97. candidates = [ring.zero_monom]
  98. basis = []
  99. while candidates:
  100. t = candidates.pop()
  101. basis.append(t)
  102. new_candidates = [_incr_k(t, k) for k in range(ring.ngens)
  103. if all(monomial_div(_incr_k(t, k), lmg) is None
  104. for lmg in leading_monomials)]
  105. candidates.extend(new_candidates)
  106. candidates.sort(key=order, reverse=True)
  107. basis = list(set(basis))
  108. return sorted(basis, key=order)