Skip to content

Commit 117065e

Browse files
Merge pull request #274 from rdbliss/resultant
Add resultant methods to fmpz_poly and fmpq_poly
2 parents 704528a + 4870eec commit 117065e

File tree

6 files changed

+130
-1
lines changed

6 files changed

+130
-1
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,18 @@ CHANGELOG
162162
Next release (0.8.0)...
163163
-----------------------
164164

165+
Contributors
166+
167+
- Robert Dougherty-Bliss (RDB)
168+
169+
Changes
170+
171+
- [gh-274](https://github.com/flintlib/python-flint/pull/274),
172+
Add resultant methods to `fmpz_poly`, `fmpq_poly` and
173+
`nmod_poly`. Now all univariate and polynomial types have the
174+
resultant method except for `fq_default_poly`. (RDB)
175+
176+
165177
0.7.0
166178
-----
167179

src/flint/test/test_all.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2834,6 +2834,42 @@ def setbad(obj, i, val):
28342834
if type(p) == flint.fq_default_poly:
28352835
assert raises(lambda: p.integral(), NotImplementedError)
28362836

2837+
# resultant checks.
2838+
x = P([0, 1])
2839+
2840+
if composite_characteristic and type(x) in [flint.fmpz_mod_poly, flint.nmod_poly]:
2841+
# Flint sometimes crashes in this case, even though the resultant
2842+
# could be computed.
2843+
divisor = characteristic.factor()[0][0]
2844+
assert raises(lambda: x.resultant(x + divisor), ValueError)
2845+
elif type(x) == flint.fq_default_poly:
2846+
# Flint does not implement resultants over GF(q) for nonprime q, so
2847+
# there's nothing for us to check.
2848+
pass
2849+
else:
2850+
assert x.resultant(x) == 0
2851+
assert x.resultant(x**2 + x - x) == 0
2852+
assert x.resultant(x**10 - x**5 + 1) == S(1)
2853+
assert (x - 1).resultant(x**5 + 1) == S(2)
2854+
2855+
for k in range(-10, 10):
2856+
assert x.resultant(x + S(k)) == S(k)
2857+
2858+
def test_poly_resultants():
2859+
# Check that the resultant of two cyclotomic polynomials is right.
2860+
# See Dresden's 2012 "Resultants of Cyclotomic Polynomials"
2861+
for m in range(1, 50):
2862+
for n in range(m + 1, 50):
2863+
a = flint.fmpz_poly.cyclotomic(m)
2864+
b = flint.fmpz_poly.cyclotomic(n)
2865+
q, r = divmod(flint.fmpz(n), flint.fmpz(m))
2866+
fs = q.factor()
2867+
if r != 0 or len(fs) > 1:
2868+
assert a.resultant(b) == 1
2869+
else:
2870+
prime = fs[0][0]
2871+
tot = flint.fmpz(m).euler_phi()
2872+
assert a.resultant(b) == prime**tot
28372873

28382874
def _all_mpolys():
28392875
return [
@@ -2869,7 +2905,6 @@ def _all_mpolys():
28692905
),
28702906
]
28712907

2872-
28732908
def test_mpolys():
28742909
for P, get_context, S, is_field, characteristic in _all_mpolys():
28752910

@@ -4832,6 +4867,8 @@ def test_all_tests():
48324867
test_polys,
48334868
test_mpolys,
48344869

4870+
test_poly_resultants,
4871+
48354872
test_fmpz_mpoly_vec,
48364873

48374874
test_matrices_eq,

src/flint/types/fmpq_poly.pyx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,32 @@ cdef class fmpq_poly(flint_poly):
415415
fmpq_poly_gcd(res.val, self.val, (<fmpq_poly>other).val)
416416
return res
417417

418+
def resultant(self, other):
419+
"""
420+
Returns the resultant of *self* and *other*.
421+
422+
>>> A = fmpq_poly([1, 0, -1]); B = fmpq_poly([1, -1])
423+
>>> A.resultant(B)
424+
0
425+
>>> C = fmpq_poly([1, 0, 0, 0, 0, -1, 1])
426+
>>> D = fmpq_poly([1, 0, 0, -1, 0, 0, 1])
427+
>>> C.resultant(D)
428+
3
429+
>>> f = fmpq_poly([1, -1] + [0] * 98 + [1])
430+
>>> g = fmpq_poly([1] + [0] * 50 + [-1] + [0] * 48 + [1])
431+
>>> f.resultant(g)
432+
1125899906842623
433+
434+
"""
435+
cdef fmpq res
436+
other = any_as_fmpq_poly(other)
437+
if other is NotImplemented:
438+
raise TypeError("cannot convert input to fmpq_poly")
439+
440+
res = fmpq.__new__(fmpq)
441+
fmpq_poly_resultant(res.val, self.val, (<fmpq_poly>other).val)
442+
return res
443+
418444
def xgcd(self, other):
419445
cdef fmpq_poly res1, res2, res3
420446
other = any_as_fmpq_poly(other)

src/flint/types/fmpz_mod_poly.pyx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,6 +1465,9 @@ cdef class fmpz_mod_poly(flint_poly):
14651465
"""
14661466
cdef fmpz_mod res
14671467

1468+
if not self.ctx.mod.is_prime():
1469+
raise ValueError("cannot compute fmpz_mod_poly resultants with composite moduli")
1470+
14681471
other = self.ctx.any_as_fmpz_mod_poly(other)
14691472
if other is NotImplemented:
14701473
raise TypeError(f"Cannot interpret {other} as a polynomial")

src/flint/types/fmpz_poly.pyx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,32 @@ cdef class fmpz_poly(flint_poly):
397397
fmpz_poly_gcd(res.val, self.val, (<fmpz_poly>other).val)
398398
return res
399399

400+
def resultant(self, other):
401+
"""
402+
Returns the resultant of *self* and *other*.
403+
404+
>>> A = fmpz_poly([1, 0, -1]); B = fmpz_poly([1, -1])
405+
>>> A.resultant(B)
406+
0
407+
>>> C = fmpz_poly([1, 0, 0, 0, 0, -1, 1])
408+
>>> D = fmpz_poly([1, 0, 0, -1, 0, 0, 1])
409+
>>> C.resultant(D)
410+
3
411+
>>> f = fmpz_poly([1, -1] + [0] * 98 + [1])
412+
>>> g = fmpz_poly([1] + [0] * 50 + [-1] + [0] * 48 + [1])
413+
>>> f.resultant(g)
414+
1125899906842623
415+
416+
"""
417+
cdef fmpz res
418+
other = any_as_fmpz_poly(other)
419+
if other is NotImplemented:
420+
raise TypeError("cannot convert input to fmpz_poly")
421+
422+
res = fmpz.__new__(fmpz)
423+
fmpz_poly_resultant(res.val, self.val, (<fmpz_poly>other).val)
424+
return res
425+
400426
def factor(self):
401427
"""
402428
Factors self into irreducible factors, returning a tuple

src/flint/types/nmod_poly.pyx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,31 @@ cdef class nmod_poly(flint_poly):
621621
nmod_poly_gcd(res.val, self.val, (<nmod_poly>other).val)
622622
return res
623623

624+
def resultant(self, other):
625+
"""
626+
Returns the resultant of *self* and *other*.
627+
628+
>>> f = nmod_poly([1, 2, 3], 3)
629+
>>> g = nmod_poly([1, 0, 1], 3)
630+
>>> f.resultant(f)
631+
0
632+
>>> f.resultant(g)
633+
2
634+
635+
"""
636+
cdef ulong res
637+
638+
mod = any_as_fmpz(self.val.mod.n)
639+
if not mod.is_prime():
640+
raise ValueError("cannot compute nmod_poly resultants with composite moduli")
641+
642+
other = any_as_nmod_poly(other, (<nmod_poly>self).val.mod)
643+
if other is NotImplemented:
644+
raise TypeError("cannot convert input to nmod_poly")
645+
646+
res = nmod_poly_resultant(self.val, (<nmod_poly>other).val)
647+
return res
648+
624649
def xgcd(self, other):
625650
cdef nmod_poly res1, res2, res3
626651
other = any_as_nmod_poly(other, (<nmod_poly>self).val.mod)

0 commit comments

Comments
 (0)