heuristicgcd.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. """Heuristic polynomial GCD algorithm (HEUGCD). """
  2. from .polyerrors import HeuristicGCDFailed
  3. HEU_GCD_MAX = 6
  4. def heugcd(f, g):
  5. """
  6. Heuristic polynomial GCD in ``Z[X]``.
  7. Given univariate polynomials ``f`` and ``g`` in ``Z[X]``, returns
  8. their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg``
  9. such that::
  10. h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h)
  11. The algorithm is purely heuristic which means it may fail to compute
  12. the GCD. This will be signaled by raising an exception. In this case
  13. you will need to switch to another GCD method.
  14. The algorithm computes the polynomial GCD by evaluating polynomials
  15. ``f`` and ``g`` at certain points and computing (fast) integer GCD
  16. of those evaluations. The polynomial GCD is recovered from the integer
  17. image by interpolation. The evaluation process reduces f and g variable
  18. by variable into a large integer. The final step is to verify if the
  19. interpolated polynomial is the correct GCD. This gives cofactors of
  20. the input polynomials as a side effect.
  21. Examples
  22. ========
  23. >>> from sympy.polys.heuristicgcd import heugcd
  24. >>> from sympy.polys import ring, ZZ
  25. >>> R, x,y, = ring("x,y", ZZ)
  26. >>> f = x**2 + 2*x*y + y**2
  27. >>> g = x**2 + x*y
  28. >>> h, cff, cfg = heugcd(f, g)
  29. >>> h, cff, cfg
  30. (x + y, x + y, x)
  31. >>> cff*h == f
  32. True
  33. >>> cfg*h == g
  34. True
  35. References
  36. ==========
  37. .. [1] [Liao95]_
  38. """
  39. assert f.ring == g.ring and f.ring.domain.is_ZZ
  40. ring = f.ring
  41. x0 = ring.gens[0]
  42. domain = ring.domain
  43. gcd, f, g = f.extract_ground(g)
  44. f_norm = f.max_norm()
  45. g_norm = g.max_norm()
  46. B = domain(2*min(f_norm, g_norm) + 29)
  47. x = max(min(B, 99*domain.sqrt(B)),
  48. 2*min(f_norm // abs(f.LC),
  49. g_norm // abs(g.LC)) + 4)
  50. for i in range(0, HEU_GCD_MAX):
  51. ff = f.evaluate(x0, x)
  52. gg = g.evaluate(x0, x)
  53. if ff and gg:
  54. if ring.ngens == 1:
  55. h, cff, cfg = domain.cofactors(ff, gg)
  56. else:
  57. h, cff, cfg = heugcd(ff, gg)
  58. h = _gcd_interpolate(h, x, ring)
  59. h = h.primitive()[1]
  60. cff_, r = f.div(h)
  61. if not r:
  62. cfg_, r = g.div(h)
  63. if not r:
  64. h = h.mul_ground(gcd)
  65. return h, cff_, cfg_
  66. cff = _gcd_interpolate(cff, x, ring)
  67. h, r = f.div(cff)
  68. if not r:
  69. cfg_, r = g.div(h)
  70. if not r:
  71. h = h.mul_ground(gcd)
  72. return h, cff, cfg_
  73. cfg = _gcd_interpolate(cfg, x, ring)
  74. h, r = g.div(cfg)
  75. if not r:
  76. cff_, r = f.div(h)
  77. if not r:
  78. h = h.mul_ground(gcd)
  79. return h, cff_, cfg
  80. x = 73794*x * domain.sqrt(domain.sqrt(x)) // 27011
  81. raise HeuristicGCDFailed('no luck')
  82. def _gcd_interpolate(h, x, ring):
  83. """Interpolate polynomial GCD from integer GCD. """
  84. f, i = ring.zero, 0
  85. # TODO: don't expose poly repr implementation details
  86. if ring.ngens == 1:
  87. while h:
  88. g = h % x
  89. if g > x // 2: g -= x
  90. h = (h - g) // x
  91. # f += X**i*g
  92. if g:
  93. f[(i,)] = g
  94. i += 1
  95. else:
  96. while h:
  97. g = h.trunc_ground(x)
  98. h = (h - g).quo_ground(x)
  99. # f += X**i*g
  100. if g:
  101. for monom, coeff in g.iterterms():
  102. f[(i,) + monom] = coeff
  103. i += 1
  104. if f.LC < 0:
  105. return -f
  106. else:
  107. return f