Coverage for /usr/lib/python3/dist-packages/sympy/polys/heuristicgcd.py: 6%
63 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""Heuristic polynomial GCD algorithm (HEUGCD). """
3from .polyerrors import HeuristicGCDFailed
5HEU_GCD_MAX = 6
7def heugcd(f, g):
8 """
9 Heuristic polynomial GCD in ``Z[X]``.
11 Given univariate polynomials ``f`` and ``g`` in ``Z[X]``, returns
12 their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg``
13 such that::
15 h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h)
17 The algorithm is purely heuristic which means it may fail to compute
18 the GCD. This will be signaled by raising an exception. In this case
19 you will need to switch to another GCD method.
21 The algorithm computes the polynomial GCD by evaluating polynomials
22 ``f`` and ``g`` at certain points and computing (fast) integer GCD
23 of those evaluations. The polynomial GCD is recovered from the integer
24 image by interpolation. The evaluation process reduces f and g variable
25 by variable into a large integer. The final step is to verify if the
26 interpolated polynomial is the correct GCD. This gives cofactors of
27 the input polynomials as a side effect.
29 Examples
30 ========
32 >>> from sympy.polys.heuristicgcd import heugcd
33 >>> from sympy.polys import ring, ZZ
35 >>> R, x,y, = ring("x,y", ZZ)
37 >>> f = x**2 + 2*x*y + y**2
38 >>> g = x**2 + x*y
40 >>> h, cff, cfg = heugcd(f, g)
41 >>> h, cff, cfg
42 (x + y, x + y, x)
44 >>> cff*h == f
45 True
46 >>> cfg*h == g
47 True
49 References
50 ==========
52 .. [1] [Liao95]_
54 """
55 assert f.ring == g.ring and f.ring.domain.is_ZZ
57 ring = f.ring
58 x0 = ring.gens[0]
59 domain = ring.domain
61 gcd, f, g = f.extract_ground(g)
63 f_norm = f.max_norm()
64 g_norm = g.max_norm()
66 B = domain(2*min(f_norm, g_norm) + 29)
68 x = max(min(B, 99*domain.sqrt(B)),
69 2*min(f_norm // abs(f.LC),
70 g_norm // abs(g.LC)) + 4)
72 for i in range(0, HEU_GCD_MAX):
73 ff = f.evaluate(x0, x)
74 gg = g.evaluate(x0, x)
76 if ff and gg:
77 if ring.ngens == 1:
78 h, cff, cfg = domain.cofactors(ff, gg)
79 else:
80 h, cff, cfg = heugcd(ff, gg)
82 h = _gcd_interpolate(h, x, ring)
83 h = h.primitive()[1]
85 cff_, r = f.div(h)
87 if not r:
88 cfg_, r = g.div(h)
90 if not r:
91 h = h.mul_ground(gcd)
92 return h, cff_, cfg_
94 cff = _gcd_interpolate(cff, x, ring)
96 h, r = f.div(cff)
98 if not r:
99 cfg_, r = g.div(h)
101 if not r:
102 h = h.mul_ground(gcd)
103 return h, cff, cfg_
105 cfg = _gcd_interpolate(cfg, x, ring)
107 h, r = g.div(cfg)
109 if not r:
110 cff_, r = f.div(h)
112 if not r:
113 h = h.mul_ground(gcd)
114 return h, cff_, cfg
116 x = 73794*x * domain.sqrt(domain.sqrt(x)) // 27011
118 raise HeuristicGCDFailed('no luck')
120def _gcd_interpolate(h, x, ring):
121 """Interpolate polynomial GCD from integer GCD. """
122 f, i = ring.zero, 0
124 # TODO: don't expose poly repr implementation details
125 if ring.ngens == 1:
126 while h:
127 g = h % x
128 if g > x // 2: g -= x
129 h = (h - g) // x
131 # f += X**i*g
132 if g:
133 f[(i,)] = g
134 i += 1
135 else:
136 while h:
137 g = h.trunc_ground(x)
138 h = (h - g).quo_ground(x)
140 # f += X**i*g
141 if g:
142 for monom, coeff in g.iterterms():
143 f[(i,) + monom] = coeff
144 i += 1
146 if f.LC < 0:
147 return -f
148 else:
149 return f