From 852944b1a1626e0ef2309a27c7804b9d72bc31a2 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Fri, 26 Jan 2024 14:34:29 +0000 Subject: [PATCH 01/30] Ugly patch for characteristic two in any_root --- .../rings/polynomial/polynomial_element.pyx | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 4c9cf2f7e0d..81097c2ee65 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2285,27 +2285,39 @@ cdef class Polynomial(CommutativePolynomial): # now self has only roots of degree ``degree``. # for now, we only implement the Cantor-Zassenhaus split k = self.degree() // degree - if k == 1: - try: - return self.roots(ring, multiplicities=False)[0] # is there something better to do here? - except IndexError: - raise ValueError("no roots F %s" % self) - if q % 2 == 0: - while True: - T = R.random_element(2*degree-1) - if T == 0: - continue - T = T.monic() - C = T - for i in range(degree-1): - C = T + pow(C,q,self) - h = self.gcd(C) - hd = h.degree() - if hd != 0 and hd != self.degree(): - if 2*hd <= self.degree(): - return h.any_root(ring, -degree, True) - else: - return (self//h).any_root(ring, -degree, True) + if k == 1 or q % 2 == 0: + # Is there something better to do here for k = 1? + roots = self.roots(ring, multiplicities=False) + if roots: + from sage.misc.prandom import choice + return choice(roots) + else: + raise ValueError(f"no roots F {self}" % self) + # Giacomo Pope (26-1-2024): + # This is buggy and very slow, I would like to fix it, but as far + # as I can tell, calling roots for GF(2^k) and picking one is simply + # faster and more robust. + # if q % 2 == 0: + # while True: + # T = R.random_element(2*degree-1) + # if T == 0: + # continue + # T = T.monic() + # # Compute the trace of T with field of order 2^k + # # sum T^(2^i) for i in range (degree * k) + # # We use repeated squaring to avoid redundent multiplications + # C = T + # TT = T + # for _ in range(degree * self.base_ring().degree() - 1): + # TT = pow(TT, 2, self) + # C += TT + # h = self.gcd(C) + # hd = h.degree() + # if hd != 0 and hd != self.degree(): + # if 2*hd <= self.degree(): + # return h.any_root(ring, -degree, True) + # else: + # return (self//h).any_root(ring, -degree, True) else: while True: T = R.random_element(2*degree-1) From 356c881672fa5f1e468750d0ba5583b33dd94a4c Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Sat, 27 Jan 2024 23:12:58 +0000 Subject: [PATCH 02/30] Simply rewrite any_root, as it's easier than debugging the old code --- .../rings/polynomial/polynomial_element.pyx | 304 ++++++++---------- 1 file changed, 140 insertions(+), 164 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 81097c2ee65..2154e197494 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2168,174 +2168,150 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: no roots (non-field) x^2 + 1 """ - if self.base_ring().is_finite() and self.base_ring().is_field(): - if self.degree() < 0: - return ring(0) - if self.degree() == 0: - raise ValueError("no roots A %s" % self) - if not assume_squarefree: - SFD = self.squarefree_decomposition() - SFD.sort() - for f, e in SFD: - try: - return f.any_root(ring, degree, True) - except ValueError: - pass - if self.degree() == 1 and (degree is None or degree == 1): - if ring is None: - return -self.get_unsafe(0) / self.get_unsafe(1) - else: - return ring(-self.get_unsafe(0) / self.get_unsafe(1)) - q = self.base_ring().order() - if ring is None: - allowed_deg_mult = Integer(1) - else: - if not (self.base_ring().is_field() and self.base_ring().is_finite()): - raise NotImplementedError - if ring.characteristic() != self.base_ring().characteristic(): - raise ValueError("ring must be an extension of the base ring") - if not (ring.is_field() and ring.is_finite()): - raise NotImplementedError - allowed_deg_mult = Integer(ring.factored_order()[0][1]) # generally it will be the quotient of this by the degree of the base ring. - if degree is None: - x = self._parent.gen() - if allowed_deg_mult == 1: - xq = pow(x,q,self) - self = self.gcd(xq-x) - degree = -1 - if self.degree() == 0: - raise ValueError("no roots B %s" % self) - else: - xq = x - d = Integer(0) - while True: - # one pass for roots that actually lie within ring. - e = self.degree() - if 2*d+2 > e: - # this polynomial has no factors dividing allowed_deg_mult - if allowed_deg_mult % e == 0: - degree = -e - break - while d < allowed_deg_mult: - d = d+1 - xq = pow(xq,q,self) - if d.divides(allowed_deg_mult): - break - A = self.gcd(xq-x) - if A != 1: - self = A - degree = -d - break - if d == allowed_deg_mult: - break - if degree is None: - if allowed_deg_mult == 1: - raise ValueError("no roots C %s" % self) - xq = x - d = Integer(0) - while True: - # now another for roots that will lie in an extension. - e = self.degree() - if 2*d+2 > e: - # this polynomial is irreducible. - degree = -e - break - while True: - # we waste a little effort here in computing the xq again. - d = d+1 - xq = pow(xq,q,self) - if allowed_deg_mult.divides(d): - break - A = self.gcd(xq-x) - if A != 1: - self = A - degree = -d - break - if degree == 0: - raise ValueError("degree should be nonzero") - R = self._parent - x = R.gen() - if degree > 0: - xq = x - d = 0 - while True: - e = self.degree() - if 2*d > e: - if degree != e: - raise ValueError("no roots D %s" % self) - break - d = d+1 - xq = pow(xq,q,self) - if d == degree: - break - A = self.gcd(xq-x) - if A != 1: - self = self // A - if d == degree: - self = self.gcd(xq-x) - if self.degree() == 0: - raise ValueError("no roots E %s" % self) - else: - degree = -degree + # Currently I can't see a benefit of the degree method, so I think we should + # remove it + if degree is not None: + print(f"Warning: 'degree' will soon be deprecated as it is no longer needed") + + # When not working over a finite field, do the simple + # thing of factoring for roots and picking the first + # root. If none available, raise an error. + from sage.categories.finite_fields import FiniteFields + if not self.base_ring() in FiniteFields(): + rs = self.roots(ring=ring, multiplicities=False) + if rs: + return rs[0] + raise ValueError("polynomial {self} has no roots" % self) + + # For finite fields, we find roots in the following three steps: + # + # 1. Compute the squarefree decomposition of the polynomial + # 2. For each squarefree polynomial find the distinct degree = 1 + # factorison, F, which is the product of degree one polynomials + # dividing the squarefree polynomial + # 3. Using Cantor-Zassenhaus splitting with degree one to find a + # single linear factor and return the root. + # + # These steps are performed by the following helper functions + # + # ======================== # + # Begin helper functions # + # ======================== # + def _linear_root(f, ring=None): if ring is None: - if degree == 1: - ring = self.base_ring() - else: - ring = self.base_ring().extension(degree) # this won't work yet. - # now self has only roots of degree ``degree``. - # for now, we only implement the Cantor-Zassenhaus split - k = self.degree() // degree - if k == 1 or q % 2 == 0: - # Is there something better to do here for k = 1? - roots = self.roots(ring, multiplicities=False) - if roots: - from sage.misc.prandom import choice - return choice(roots) - else: - raise ValueError(f"no roots F {self}" % self) - # Giacomo Pope (26-1-2024): - # This is buggy and very slow, I would like to fix it, but as far - # as I can tell, calling roots for GF(2^k) and picking one is simply - # faster and more robust. - # if q % 2 == 0: - # while True: - # T = R.random_element(2*degree-1) - # if T == 0: - # continue - # T = T.monic() - # # Compute the trace of T with field of order 2^k - # # sum T^(2^i) for i in range (degree * k) - # # We use repeated squaring to avoid redundent multiplications - # C = T - # TT = T - # for _ in range(degree * self.base_ring().degree() - 1): - # TT = pow(TT, 2, self) - # C += TT - # h = self.gcd(C) - # hd = h.degree() - # if hd != 0 and hd != self.degree(): - # if 2*hd <= self.degree(): - # return h.any_root(ring, -degree, True) - # else: - # return (self//h).any_root(ring, -degree, True) - else: - while True: - T = R.random_element(2*degree-1) - if T == 0: - continue + return -f[0] / f[1] + return ring(-f[0] / f[1]) + + def _any_root_squarefree(f, ring=None): + # Compute the distinct degree factorisation for degree one, + # the output will be the product of #all degree one + # irreducible polynomials dividing f + q = f.base_ring().order() # p^k + R, X = f.parent().objgen() + w = pow(X, q, f) - X + g = f.gcd(w) + + # When the degree is one, we have found our + # root. If the degree is zero, there are no roots + if g.degree().is_one(): + return _linear_root(g, ring=ring) + elif g.degree().is_zero(): + raise ValueError(f"no root can be computed for {f}") + + # Otherwise, we can find a root from the CZ splitting of g + return _cantor_zassenhaus_split_root(g, 1, ring=ring) + + def _cantor_zassenhaus_split_root(f, degree, ring=None): + R = f.parent() + q = f.base_ring().order() + # Need to handle odd and even charcteristic separately + if q % 2 != 0: + # We expect to succeed with 1/2 probability, so if we + # try 1000 times and fail, there's a bug somewhere. + for _ in range(1000): + T = R.random_element(2*degree + 1) + if T.is_zero(): continue # skip T = 0 T = T.monic() - h = self.gcd(pow(T, Integer((q**degree-1)/2), self)-1) + + # Compute the gcd. 50% chance this is a non-trivial factor of f + h = f.gcd(pow(T, (q-1)//2, f)-1) hd = h.degree() - if hd != 0 and hd != self.degree(): - if 2*hd <= self.degree(): - return h.any_root(ring, -degree, True) + + # If we found a degree-one root, return it + if hd.is_one(): + return _linear_root(h, ring=ring) + + # Else check if we have a non-trivial factor and keep going + if not hd.is_zero() and hd != f.degree(): + if 2*hd <= f.degree(): + return _cantor_zassenhaus_split_root(h, degree, ring=ring) else: - return (self//h).any_root(ring, -degree, True) - else: - rs = self.roots(ring=ring, multiplicities=False) - if rs: - return rs[0] - raise ValueError("no roots (non-field) %s" % self) + return _cantor_zassenhaus_split_root(f//h, degree, ring=ring) + # If you are reaching this error, chances are there's a bug in the code. + raise ValueError(f"no splitting of degree {degree} found for {f}") + + # Now handle even charactertistic + for _ in range(1000): + # Sample a uniformly random element of R + T = R.random_element(2*degree + 1) + if T.is_zero(): continue + T = T.monic() + + # Compute the trace of T with field of order 2^k + # sum T^(2^i) for i in range (degree * k) + # We use repeated squaring to avoid redundent multiplications + C, TT = T, T + for _ in range(degree * f.base_ring().degree() - 1): + TT = pow(TT, 2, f) + C += TT + + # Compute the gcd to find a factor + h = f.gcd(C) + hd = h.degree() + + # If we found a degree-one root, return it + if hd.is_one(): + return _linear_root(h, ring=ring) + + # else check if we have a factor + elif not hd.is_zero() and hd != f.degree(): + if 2*hd <= f.degree(): + return _cantor_zassenhaus_split_root(h, degree, ring) + else: + return _cantor_zassenhaus_split_root(f // h, degree, ring) + + # If you are reaching this error, chances are there's a bug in the code. + raise ValueError(f"no splitting of degree {degree} found for {f}") + # ======================== # + # End helper functions # + # ======================== # + + # Initial checks for bad input + if self.degree() < 0: + if ring is None: + return self.base_ring()(0) + return ring(0) + elif self.degree() == 0: + raise ValueError(f"no root can be computed for {self}") + + # If the input is a linear polynomial, simply compute the root + if self.degree() == 1: + return _linear_root(self, ring=ring) + + # If we know the polynomial is square-free, we can start here + if assume_squarefree: + return _any_root_squarefree(self, ring=ring) + + # Otherwise we compute the squarefree decomposition and check each + # polynomial for a root. If no poly has a root, we raise an error. + SFD = self.squarefree_decomposition() + SFD.sort() + for poly, _ in SFD: + try: + return _any_root_squarefree(poly, ring=ring) + except ValueError: + pass + raise ValueError(f"no root can be computed for {self}") def __truediv__(left, right): r""" From 661efd84b8f10940721175f2bc74f0493618f23c Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Sat, 27 Jan 2024 23:17:01 +0000 Subject: [PATCH 03/30] handle deprecation properly --- src/sage/rings/polynomial/polynomial_element.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 2154e197494..531dbf2f65f 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2171,7 +2171,8 @@ cdef class Polynomial(CommutativePolynomial): # Currently I can't see a benefit of the degree method, so I think we should # remove it if degree is not None: - print(f"Warning: 'degree' will soon be deprecated as it is no longer needed") + from sage.misc.superseded import deprecation + deprecation(37170, "'degree' will soon be deprecated as it is no longer needed") # When not working over a finite field, do the simple # thing of factoring for roots and picking the first From a8dcf5ac993c737b10a209d4d97fbceca44bf36f Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:10:33 +0000 Subject: [PATCH 04/30] Include any_irreducible_factor and refactor any_root to simply wrap this --- .../rings/polynomial/polynomial_element.pyx | 352 +++++++++++------- 1 file changed, 218 insertions(+), 134 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 531dbf2f65f..6211248c42b 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2047,6 +2047,198 @@ cdef class Polynomial(CommutativePolynomial): else: return (False, None) if root else False + def _distinct_degree_factorisation_squarefree(self): + """ + Helper function for any_irreducible_factor which computes + the distinct degree factorisation of `self`. + + Creates an iterator for all valid degrees `d`, and return + tuples of the form `(a_d, d)` for a polynomial `a_d` the + product of irreducible polynomials of degree `d` + + Assumes that self is squarefree. + """ + q = self.base_ring().order() # p^k + R, x = self.parent().objgen() + + # Initialise values + d = 0 + v = self + w = x + + # Iterate over all possible degrees with degree + while e <= 2*(d + 1): + e = v.degree() + d = d + 1 + w = pow(w, q, v) + + ad = v.gcd(w - x) + + if not ad.is_one(): + v = v // ad + w = w % v + + yield (ad, d) + + # Last case, v itself might be irreducible + e = v.degree() + if e > 0: + yield (v, e) + return + + def _cantor_zassenhaus_split_to_irreducible(self, degree): + """ + Helper function for any_irreducible_factor which computes + a factor from a polynomial of the form `self = prod g_i(x)` + with all `g_i(x)` having the same degree. Uses the Cantor + Zassenhaus splitting method. + """ + R = self.parent() + q = self.base_ring().order() + + # Polynomial is already irreducible by the assumptions of the function + if self.degree() == degree: + return self + + # We expect to succeed with greater than 1/2 probability, + #so if we try 1000 times and fail, there's a bug somewhere. + for _ in range(1000): + T = R.random_element(2*degree + 1) + if T.is_zero(): + continue + T = T.monic() + + # Need to handle odd and even charcteristic separately + if q % 2 != 0: + h = self.gcd(pow(T, (q-1)//2, self)-1) + else: + # Compute the trace of T with field of order 2^k + # sum T^(2^i) for i in range (degree * k) + # We use repeated squaring to avoid redundent multiplications + C, TT = T, T + for _ in range(degree * self.base_ring().degree() - 1): + TT = pow(TT, 2, self) + C += TT + h = self.gcd(C) + + hd = h.degree() + + # If we found a factor of desired degree, return it + if hd == degree: + return h + + # Else check if we have a non-trivial factor and keep going + if not hd.is_zero() and hd != self.degree(): + if 2*hd <= self.degree(): + return h._cantor_zassenhaus_split_to_irreducible(degree) + else: + return (self//h)._cantor_zassenhaus_split_to_irreducible(degree) + + # If you are reaching this error, chances are there's a bug in the code. + raise AssertionError(f"no splitting of degree {degree} found for {self}") + + def _any_irreducible_factor_squarefree(self, degree=None): + """ + TODO + """ + # If the degree is not None we only want to check a single polynomial + if degree is not None: + for (poly, d) in self._distinct_degree_factorisation_squarefree(): + if d == degree and not poly.is_one(): + return poly._cantor_zassenhaus_split_to_irreducible(degree) + # Stop iterating early if the degree is too large + elif d > degree: + raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") + raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") + + # Otherwise we check all degrees, starting from the smallest + for (poly, d) in self._distinct_degree_factorisation_squarefree(): + # Skip the split checking if `_distinct_degree_factorisation_squarefree` + # has found no elements of degree `d` + if poly.is_one(): + continue + + # Otherwise find a factor from the distinct degree factor + return poly._cantor_zassenhaus_split_to_irreducible(d) + + raise ValueError(f"no irreducible factor could be computed from {self}") + + def any_irreducible_factor(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): + """ + TODO + """ + # If the ring is not None, then first change ring and run the algorithm again + if ring is not None: + try: + f = self.change_ring(ring) + return f.any_irreducible_factor(None, degree, assume_squarefree, assume_distinct_deg) + except (TypeError, ValueError): + raise TypeError(f"unable to coerce from {self.base_ring()} to {ring}") + + # Make sure the user inputted something reasonable for degree + if degree is not None: + degree = ZZ(degree) + if degree < 1: + raise ValueError(f"{degree = } must be positive") + + if assume_distinct_deg and degree is None: + raise ValueError("degree must be known if distinct degree factorisation is assumed") + + # When not working over a finite field, do the simple thing of factoring. + # If degree is none, we return the first factor, otherwise we iterate + # through and look for a polynomial with the desired degree. + from sage.categories.finite_fields import FiniteFields + if self.base_ring() not in FiniteFields(): + try: + factorisation = self.factor() + except (NotImplementedError, ValueError): + raise ValueError(f"Cannot factor {self} over the base ring {self.base_ring()}") + if degree is None: + return factorisation[0][0] + for (poly, e) in factorisation: + if poly.degree() == degree: + return poly + raise ValueError(f"polynomial {self} has no irreducible factor of degree {degree}") + + # For finite fields, we find irreducible factors in the following three steps: + # + # 1. Compute the squarefree decomposition of the polynomial `self` + # 2. For each squarefree polynomial find the distinct degree `d` + # factorison, F, which is the product of degree `d` polynomials + # dividing the squarefree polynomial + # 3. Using Cantor-Zassenhaus splitting with degree `d` to find a + # single linear factor and return the root. + # + # When degree is None, we check all degrees smaller than the degree of the + # squarefree polynomial, otherwise we work with only a single degree set by + # the user. + + # Initial checks for bad input + if self.degree().is_zero(): + raise ValueError(f"there are no irreducible factors of {self}") + + # If we know the polynomial is square-free, we can start here + if assume_squarefree: + if assume_distinct_deg: + return self._cantor_zassenhaus_split_to_irreducible(degree) + return self._any_irreducible_factor_squarefree(degree) + + # Otherwise we compute the squarefree decomposition and check each + # polynomial for a root. If no poly has a root, we raise an error. + SFD = self.squarefree_decomposition() + SFD.sort() + for poly, _ in SFD: + try: + return poly._any_irreducible_factor_squarefree(degree) + except ValueError: + pass + + # If degree has been set, there could just be no factor of the desired degree + if degree: + raise ValueError(f"polynomial {self} has no irreducible factor of degree {degree}") + # But if any degree is allowed then there should certainly be a factor if self has degree > 0 + raise AssertionError(f"no irreducible factor was computed for {self}. Bug.") + def any_root(self, ring=None, degree=None, assume_squarefree=False): """ Return a root of this polynomial in the given ring. @@ -2168,12 +2360,6 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: no roots (non-field) x^2 + 1 """ - # Currently I can't see a benefit of the degree method, so I think we should - # remove it - if degree is not None: - from sage.misc.superseded import deprecation - deprecation(37170, "'degree' will soon be deprecated as it is no longer needed") - # When not working over a finite field, do the simple # thing of factoring for roots and picking the first # root. If none available, raise an error. @@ -2182,137 +2368,35 @@ cdef class Polynomial(CommutativePolynomial): rs = self.roots(ring=ring, multiplicities=False) if rs: return rs[0] - raise ValueError("polynomial {self} has no roots" % self) - - # For finite fields, we find roots in the following three steps: - # - # 1. Compute the squarefree decomposition of the polynomial - # 2. For each squarefree polynomial find the distinct degree = 1 - # factorison, F, which is the product of degree one polynomials - # dividing the squarefree polynomial - # 3. Using Cantor-Zassenhaus splitting with degree one to find a - # single linear factor and return the root. - # - # These steps are performed by the following helper functions - # - # ======================== # - # Begin helper functions # - # ======================== # - def _linear_root(f, ring=None): - if ring is None: - return -f[0] / f[1] - return ring(-f[0] / f[1]) - - def _any_root_squarefree(f, ring=None): - # Compute the distinct degree factorisation for degree one, - # the output will be the product of #all degree one - # irreducible polynomials dividing f - q = f.base_ring().order() # p^k - R, X = f.parent().objgen() - w = pow(X, q, f) - X - g = f.gcd(w) - - # When the degree is one, we have found our - # root. If the degree is zero, there are no roots - if g.degree().is_one(): - return _linear_root(g, ring=ring) - elif g.degree().is_zero(): - raise ValueError(f"no root can be computed for {f}") - - # Otherwise, we can find a root from the CZ splitting of g - return _cantor_zassenhaus_split_root(g, 1, ring=ring) - - def _cantor_zassenhaus_split_root(f, degree, ring=None): - R = f.parent() - q = f.base_ring().order() - # Need to handle odd and even charcteristic separately - if q % 2 != 0: - # We expect to succeed with 1/2 probability, so if we - # try 1000 times and fail, there's a bug somewhere. - for _ in range(1000): - T = R.random_element(2*degree + 1) - if T.is_zero(): continue # skip T = 0 - T = T.monic() - - # Compute the gcd. 50% chance this is a non-trivial factor of f - h = f.gcd(pow(T, (q-1)//2, f)-1) - hd = h.degree() - - # If we found a degree-one root, return it - if hd.is_one(): - return _linear_root(h, ring=ring) - - # Else check if we have a non-trivial factor and keep going - if not hd.is_zero() and hd != f.degree(): - if 2*hd <= f.degree(): - return _cantor_zassenhaus_split_root(h, degree, ring=ring) - else: - return _cantor_zassenhaus_split_root(f//h, degree, ring=ring) - # If you are reaching this error, chances are there's a bug in the code. - raise ValueError(f"no splitting of degree {degree} found for {f}") - - # Now handle even charactertistic - for _ in range(1000): - # Sample a uniformly random element of R - T = R.random_element(2*degree + 1) - if T.is_zero(): continue - T = T.monic() - - # Compute the trace of T with field of order 2^k - # sum T^(2^i) for i in range (degree * k) - # We use repeated squaring to avoid redundent multiplications - C, TT = T, T - for _ in range(degree * f.base_ring().degree() - 1): - TT = pow(TT, 2, f) - C += TT - - # Compute the gcd to find a factor - h = f.gcd(C) - hd = h.degree() - - # If we found a degree-one root, return it - if hd.is_one(): - return _linear_root(h, ring=ring) - - # else check if we have a factor - elif not hd.is_zero() and hd != f.degree(): - if 2*hd <= f.degree(): - return _cantor_zassenhaus_split_root(h, degree, ring) - else: - return _cantor_zassenhaus_split_root(f // h, degree, ring) - - # If you are reaching this error, chances are there's a bug in the code. - raise ValueError(f"no splitting of degree {degree} found for {f}") - # ======================== # - # End helper functions # - # ======================== # - - # Initial checks for bad input - if self.degree() < 0: - if ring is None: - return self.base_ring()(0) - return ring(0) - elif self.degree() == 0: - raise ValueError(f"no root can be computed for {self}") + raise ValueError(f"polynomial {self} has no roots") - # If the input is a linear polynomial, simply compute the root - if self.degree() == 1: - return _linear_root(self, ring=ring) - - # If we know the polynomial is square-free, we can start here - if assume_squarefree: - return _any_root_squarefree(self, ring=ring) - - # Otherwise we compute the squarefree decomposition and check each - # polynomial for a root. If no poly has a root, we raise an error. - SFD = self.squarefree_decomposition() - SFD.sort() - for poly, _ in SFD: + # Look for a linear factor, if there is none, raise a ValueError + if degree is None: try: - return _any_root_squarefree(poly, ring=ring) + f = self.any_irreducible_factor(ring=ring, degree=ZZ(1), assume_squarefree=assume_squarefree) + return - f[0] / f[1] except ValueError: - pass - raise ValueError(f"no root can be computed for {self}") + raise ValueError(f"no root of polynomial {self} can be computed") + + # The old version of `any_root()` allowed degree < 0 to indicate that the input polynomial + # had a distinct degree factorisation, we pass this to any_irreducible_factor as a bool and + # ensure that the degree is positive. + assume_distinct_deg = False + if degree < 0: + degree = ZZ(abs(degree)) + assume_distinct_deg = True + + # If a certain degree is requested, then we find an irreducible factor of degree `degree` + # use this to compute a field extension and return the generator as root of this polynomial + try: + f = self.any_irreducible_factor(ring=ring, degree=degree, assume_squarefree=assume_squarefree, assume_distinct_deg=assume_distinct_deg) + except ValueError: + raise ValueError(f"no irreducible factor of degree {degree} can be computed from {self}") + + if degree.is_one(): + return - f[0] / f[1] + F_ext = self.base_ring().extension(f, names="a") + return F_ext.gen() def __truediv__(left, right): r""" From 32066e28ea25f616b67178a6adfd190f2d04d8f8 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:40:42 +0000 Subject: [PATCH 05/30] Fix linting issues --- .../rings/polynomial/polynomial_element.pyx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 6211248c42b..aa1da8830a7 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2055,7 +2055,7 @@ cdef class Polynomial(CommutativePolynomial): Creates an iterator for all valid degrees `d`, and return tuples of the form `(a_d, d)` for a polynomial `a_d` the product of irreducible polynomials of degree `d` - + Assumes that self is squarefree. """ q = self.base_ring().order() # p^k @@ -2071,7 +2071,7 @@ cdef class Polynomial(CommutativePolynomial): e = v.degree() d = d + 1 w = pow(w, q, v) - + ad = v.gcd(w - x) if not ad.is_one(): @@ -2090,8 +2090,8 @@ cdef class Polynomial(CommutativePolynomial): """ Helper function for any_irreducible_factor which computes a factor from a polynomial of the form `self = prod g_i(x)` - with all `g_i(x)` having the same degree. Uses the Cantor - Zassenhaus splitting method. + with all `g_i(x)` having the same degree. Uses the Cantor + Zassenhaus splitting method. """ R = self.parent() q = self.base_ring().order() @@ -2100,7 +2100,7 @@ cdef class Polynomial(CommutativePolynomial): if self.degree() == degree: return self - # We expect to succeed with greater than 1/2 probability, + # We expect to succeed with greater than 1/2 probability, #so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): T = R.random_element(2*degree + 1) @@ -2119,7 +2119,7 @@ cdef class Polynomial(CommutativePolynomial): for _ in range(degree * self.base_ring().degree() - 1): TT = pow(TT, 2, self) C += TT - h = self.gcd(C) + h = self.gcd(C) hd = h.degree() @@ -2136,7 +2136,7 @@ cdef class Polynomial(CommutativePolynomial): # If you are reaching this error, chances are there's a bug in the code. raise AssertionError(f"no splitting of degree {degree} found for {self}") - + def _any_irreducible_factor_squarefree(self, degree=None): """ TODO @@ -2180,13 +2180,13 @@ cdef class Polynomial(CommutativePolynomial): degree = ZZ(degree) if degree < 1: raise ValueError(f"{degree = } must be positive") - + if assume_distinct_deg and degree is None: raise ValueError("degree must be known if distinct degree factorisation is assumed") # When not working over a finite field, do the simple thing of factoring. - # If degree is none, we return the first factor, otherwise we iterate - # through and look for a polynomial with the desired degree. + # If degree is none, we return the first factor, otherwise we iterate + # through and look for a polynomial with the desired degree. from sage.categories.finite_fields import FiniteFields if self.base_ring() not in FiniteFields(): try: @@ -2209,7 +2209,7 @@ cdef class Polynomial(CommutativePolynomial): # 3. Using Cantor-Zassenhaus splitting with degree `d` to find a # single linear factor and return the root. # - # When degree is None, we check all degrees smaller than the degree of the + # When degree is None, we check all degrees smaller than the degree of the # squarefree polynomial, otherwise we work with only a single degree set by # the user. @@ -2232,7 +2232,7 @@ cdef class Polynomial(CommutativePolynomial): return poly._any_irreducible_factor_squarefree(degree) except ValueError: pass - + # If degree has been set, there could just be no factor of the desired degree if degree: raise ValueError(f"polynomial {self} has no irreducible factor of degree {degree}") @@ -2360,8 +2360,8 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: no roots (non-field) x^2 + 1 """ - # When not working over a finite field, do the simple - # thing of factoring for roots and picking the first + # When not working over a finite field, do the simple + # thing of factoring for roots and picking the first # root. If none available, raise an error. from sage.categories.finite_fields import FiniteFields if not self.base_ring() in FiniteFields(): @@ -2369,7 +2369,7 @@ cdef class Polynomial(CommutativePolynomial): if rs: return rs[0] raise ValueError(f"polynomial {self} has no roots") - + # Look for a linear factor, if there is none, raise a ValueError if degree is None: try: From b810fcc120dd8f322e8832aed63c02436bbad45b Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:44:29 +0000 Subject: [PATCH 06/30] Small edits to fix review notes --- src/sage/rings/polynomial/polynomial_element.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index aa1da8830a7..e1fafc189e6 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2108,8 +2108,8 @@ cdef class Polynomial(CommutativePolynomial): continue T = T.monic() - # Need to handle odd and even charcteristic separately - if q % 2 != 0: + # Need to handle odd and even characteristic separately + if q % 2: h = self.gcd(pow(T, (q-1)//2, self)-1) else: # Compute the trace of T with field of order 2^k @@ -2117,7 +2117,7 @@ cdef class Polynomial(CommutativePolynomial): # We use repeated squaring to avoid redundent multiplications C, TT = T, T for _ in range(degree * self.base_ring().degree() - 1): - TT = pow(TT, 2, self) + TT = TT * TT % f C += TT h = self.gcd(C) @@ -2204,7 +2204,7 @@ cdef class Polynomial(CommutativePolynomial): # # 1. Compute the squarefree decomposition of the polynomial `self` # 2. For each squarefree polynomial find the distinct degree `d` - # factorison, F, which is the product of degree `d` polynomials + # factorisation, F, which is the product of degree `d` polynomials # dividing the squarefree polynomial # 3. Using Cantor-Zassenhaus splitting with degree `d` to find a # single linear factor and return the root. From b240bcfeda89a4a036d68e0e79dd5ffe7687605c Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:47:28 +0000 Subject: [PATCH 07/30] Add docstrings to helper functions --- src/sage/rings/polynomial/polynomial_element.pyx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e1fafc189e6..064f4bdcef1 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2139,7 +2139,17 @@ cdef class Polynomial(CommutativePolynomial): def _any_irreducible_factor_squarefree(self, degree=None): """ - TODO + Helper function for any_irreducible_factor which computes + an irreducible factor from self, assuming the input is + squarefree. + + Does this by first computing the distinct degree factorisations + of self and thenfinds a factor with Cantor-Zassenhaus + splitting. + + If degree is not None, then only irreducible factors of degree + `degree` are searched for, otherwise the smallest degree factor + is found. """ # If the degree is not None we only want to check a single polynomial if degree is not None: @@ -2165,7 +2175,7 @@ cdef class Polynomial(CommutativePolynomial): def any_irreducible_factor(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ - TODO + TODO: description with doctests and tests. """ # If the ring is not None, then first change ring and run the algorithm again if ring is not None: From f26534ebcbb6f772d3652558db52027b2916e5bc Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:49:31 +0000 Subject: [PATCH 08/30] Remove the check for the zero polynomial --- src/sage/rings/polynomial/polynomial_element.pyx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 064f4bdcef1..b80a417a4b4 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2103,10 +2103,8 @@ cdef class Polynomial(CommutativePolynomial): # We expect to succeed with greater than 1/2 probability, #so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): - T = R.random_element(2*degree + 1) - if T.is_zero(): - continue - T = T.monic() + # Sample a polynomial uniformly from R + T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately if q % 2: From 66e1ae05d41c622a25743c76dca530c68676e93a Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 14:50:14 +0000 Subject: [PATCH 09/30] Too much whitespace --- src/sage/rings/polynomial/polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index b80a417a4b4..90b779400b1 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2138,9 +2138,9 @@ cdef class Polynomial(CommutativePolynomial): def _any_irreducible_factor_squarefree(self, degree=None): """ Helper function for any_irreducible_factor which computes - an irreducible factor from self, assuming the input is + an irreducible factor from self, assuming the input is squarefree. - + Does this by first computing the distinct degree factorisations of self and thenfinds a factor with Cantor-Zassenhaus splitting. From 24c277948d8ffdd79730eae588802fe5f15cdd8b Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 15:22:30 +0000 Subject: [PATCH 10/30] Fix refactoring bugs --- src/sage/rings/polynomial/polynomial_element.pyx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 90b779400b1..5e9dbd63c01 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2062,26 +2062,26 @@ cdef class Polynomial(CommutativePolynomial): R, x = self.parent().objgen() # Initialise values - d = 0 v = self w = x + d = 0 + e = v.degree() # Iterate over all possible degrees with degree - while e <= 2*(d + 1): - e = v.degree() + while 2*(d + 1) <= e: d = d + 1 w = pow(w, q, v) ad = v.gcd(w - x) + yield (ad, d) if not ad.is_one(): v = v // ad w = w % v - yield (ad, d) + e = v.degree() # Last case, v itself might be irreducible - e = v.degree() if e > 0: yield (v, e) return @@ -2115,7 +2115,7 @@ cdef class Polynomial(CommutativePolynomial): # We use repeated squaring to avoid redundent multiplications C, TT = T, T for _ in range(degree * self.base_ring().degree() - 1): - TT = TT * TT % f + TT = TT * TT % self C += TT h = self.gcd(C) From f7e157c77a17537164d9c2bb376958c266a95ac5 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 17:33:17 +0000 Subject: [PATCH 11/30] Try and make new function work like th eold one --- .../rings/polynomial/polynomial_element.pyx | 83 ++++++++++++------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 5e9dbd63c01..27f5e9a4fce 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2171,18 +2171,10 @@ cdef class Polynomial(CommutativePolynomial): raise ValueError(f"no irreducible factor could be computed from {self}") - def any_irreducible_factor(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): + def any_irreducible_factor(self, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ TODO: description with doctests and tests. """ - # If the ring is not None, then first change ring and run the algorithm again - if ring is not None: - try: - f = self.change_ring(ring) - return f.any_irreducible_factor(None, degree, assume_squarefree, assume_distinct_deg) - except (TypeError, ValueError): - raise TypeError(f"unable to coerce from {self.base_ring()} to {ring}") - # Make sure the user inputted something reasonable for degree if degree is not None: degree = ZZ(degree) @@ -2277,26 +2269,30 @@ cdef class Polynomial(CommutativePolynomial): sage: f.factor() (7) * (x + 9) * (x^6 + 10*x^4 + 6*x^3 + 5*x^2 + 2*x + 2) sage: f = x^6 + 10*x^4 + 6*x^3 + 5*x^2 + 2*x + 2 - sage: f.any_root(GF(11^6, 'a')) - a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a - sage: sorted(f.roots(GF(11^6, 'a'))) - [(10*a^5 + 2*a^4 + 8*a^3 + 9*a^2 + a, 1), - (a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a, 1), - (9*a^5 + 5*a^4 + 10*a^3 + 8*a^2 + 3*a + 1, 1), - (2*a^5 + 8*a^4 + 3*a^3 + 6*a + 2, 1), - (a^5 + 3*a^4 + 8*a^3 + 2*a^2 + 3*a + 4, 1), - (10*a^5 + 3*a^4 + 8*a^3 + a^2 + 10*a + 4, 1)] - sage: f.any_root(GF(11^6, 'a')) - a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a + sage: root = f.any_root(GF(11^6, 'a')) + sage: roots = sorted(f.roots(GF(11^6, 'a'), multiplicities=False)) + sage: roots + [10*a^5 + 2*a^4 + 8*a^3 + 9*a^2 + a, + a^5 + a^4 + 7*a^3 + 2*a^2 + 10*a, + 9*a^5 + 5*a^4 + 10*a^3 + 8*a^2 + 3*a + 1, + 2*a^5 + 8*a^4 + 3*a^3 + 6*a + 2, + a^5 + 3*a^4 + 8*a^3 + 2*a^2 + 3*a + 4, + 10*a^5 + 3*a^4 + 8*a^3 + a^2 + 10*a + 4] + sage: root in roots + True sage: # needs sage.rings.finite_rings - sage: g = (x-1)*(x^2 + 3*x + 9) * (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5) + sage: g = (x-1) * (x^2 + 3*x + 9) * (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5) sage: g.any_root(ring=GF(11^10, 'b'), degree=1) 1 - sage: g.any_root(ring=GF(11^10, 'b'), degree=2) - 5*b^9 + 4*b^7 + 4*b^6 + 8*b^5 + 10*b^2 + 10*b + 5 - sage: g.any_root(ring=GF(11^10, 'b'), degree=5) - 5*b^9 + b^8 + 3*b^7 + 2*b^6 + b^5 + 4*b^4 + 3*b^3 + 7*b^2 + 10*b + sage: root = g.any_root(ring=GF(11^10, 'b'), degree=2) + sage: roots = (x^2 + 3*x + 9).roots(ring=GF(11^10, 'b'), multiplicities=False) + sage: root in roots + True + sage: root = g.any_root(ring=GF(11^10, 'b'), degree=5) + sage: roots = (x^5 + 5*x^4 + 8*x^3 + 5*x^2 + 3*x + 5).roots(ring=GF(11^10, 'b'), multiplicities=False) + sage: root in roots + True TESTS:: @@ -2366,7 +2362,7 @@ cdef class Polynomial(CommutativePolynomial): sage: (x^2 + 1).any_root() Traceback (most recent call last): ... - ValueError: no roots (non-field) x^2 + 1 + ValueError: polynomial x^2 + 1 has no roots """ # When not working over a finite field, do the simple # thing of factoring for roots and picking the first @@ -2380,12 +2376,21 @@ cdef class Polynomial(CommutativePolynomial): # Look for a linear factor, if there is none, raise a ValueError if degree is None: + # if a ring is given try and coerce the polynomial into this ring + if ring is not None: + try: + self = self.change_ring(ring) + except ValueError: + raise(f"cannot coerce polynomial {self} to the new ring: {ring}") + + # try and find a linear irreducible polynomial from f to compute a root try: - f = self.any_irreducible_factor(ring=ring, degree=ZZ(1), assume_squarefree=assume_squarefree) - return - f[0] / f[1] + f = self.any_irreducible_factor(degree=ZZ(1), assume_squarefree=assume_squarefree) except ValueError: raise ValueError(f"no root of polynomial {self} can be computed") + return - f[0] / f[1] + # The old version of `any_root()` allowed degree < 0 to indicate that the input polynomial # had a distinct degree factorisation, we pass this to any_irreducible_factor as a bool and # ensure that the degree is positive. @@ -2396,13 +2401,31 @@ cdef class Polynomial(CommutativePolynomial): # If a certain degree is requested, then we find an irreducible factor of degree `degree` # use this to compute a field extension and return the generator as root of this polynomial + # if the degree and a ring is given however, instead compute a degree `degree` factor in the + # base ring and then find a factor from this in the supplied ring. try: - f = self.any_irreducible_factor(ring=ring, degree=degree, assume_squarefree=assume_squarefree, assume_distinct_deg=assume_distinct_deg) + f = self.any_irreducible_factor(degree=degree, assume_squarefree=assume_squarefree, assume_distinct_deg=assume_distinct_deg) except ValueError: raise ValueError(f"no irreducible factor of degree {degree} can be computed from {self}") + # For the case when the degree is one, just return the root if degree.is_one(): - return - f[0] / f[1] + root = - f[0] / f[1] + if ring is None: + return root + return ring(root) + + # If the user asks for a specific ring, find the root in this ring from + # the polynomial of user supplied degree `degree` + if ring is not None: + try: + f = f.change_ring(ring) + except ValueError: + raise(f"cannot coerce polynomial {f} to the supplied ring: {ring}") + return f.any_root() + + # Otherwise change the ring of this degree `degree` irreducible + # polynomial and attempt to find a root from this F_ext = self.base_ring().extension(f, names="a") return F_ext.gen() From afa5f5f6e4b171fdf41621a4f060a4a0089a86a2 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 17:40:38 +0000 Subject: [PATCH 12/30] white space --- src/sage/rings/polynomial/polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 27f5e9a4fce..dfb70ce742c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2382,7 +2382,7 @@ cdef class Polynomial(CommutativePolynomial): self = self.change_ring(ring) except ValueError: raise(f"cannot coerce polynomial {self} to the new ring: {ring}") - + # try and find a linear irreducible polynomial from f to compute a root try: f = self.any_irreducible_factor(degree=ZZ(1), assume_squarefree=assume_squarefree) @@ -2424,7 +2424,7 @@ cdef class Polynomial(CommutativePolynomial): raise(f"cannot coerce polynomial {f} to the supplied ring: {ring}") return f.any_root() - # Otherwise change the ring of this degree `degree` irreducible + # Otherwise change the ring of this degree `degree` irreducible # polynomial and attempt to find a root from this F_ext = self.base_ring().extension(f, names="a") return F_ext.gen() From aa5a29060d0c6cf6b164ea3d613ce58939018d14 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 21:29:10 +0000 Subject: [PATCH 13/30] Replace trac with issue throughout polynomial_element.pyx --- .../rings/polynomial/polynomial_element.pyx | 178 +++++++++--------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index dfb70ce742c..7c66eecce48 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -43,7 +43,7 @@ AUTHORS: - David Zureick-Brown (2017-09): added is_weil_polynomial - Sebastian Oehms (2018-10): made :meth:`roots` and :meth:`factor` work over more - cases of proper integral domains (see :trac:`26421`) + cases of proper integral domains (see :issue:`26421`) """ @@ -613,7 +613,7 @@ cdef class Polynomial(CommutativePolynomial): sage: F(y=1) x*t - The following shows that :trac:`2360` is indeed fixed. :: + The following shows that :issue:`2360` is indeed fixed. :: sage: R. = ZZ[] sage: P. = ZZ[] @@ -628,13 +628,13 @@ cdef class Polynomial(CommutativePolynomial): sage: f(x) 6*x^4 - The following shows that :trac:`9006` is also fixed. :: + The following shows that :issue:`9006` is also fixed. :: sage: f = ZZ['x'](1000000 * [1]) sage: f(1) 1000000 - The following test came up in :trac:`9051`:: + The following test came up in :issue:`9051`:: sage: # needs sage.rings.complex_interval_field sage: Cif = ComplexIntervalField(64) @@ -644,7 +644,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f(jj).center(), f(jj).diameter() (1.00000000000000000, 4.00000000000000000) - The following failed before the patch to :trac:`3979` + The following failed before the patch to :issue:`3979` :: @@ -700,7 +700,7 @@ cdef class Polynomial(CommutativePolynomial): ... TypeError: Wrong number of arguments - Check that :trac:`22317` is fixed:: + Check that :issue:`22317` is fixed:: sage: R = ZZ['x']['y']['z'] sage: d = R.gens_dict_recursive() @@ -708,7 +708,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p(x=QQ(0)) 0 - Check that :trac:`32513` is fixed:: + Check that :issue:`32513` is fixed:: sage: R. = PolynomialRing(ZZ) sage: x.substitute([]) @@ -716,7 +716,7 @@ cdef class Polynomial(CommutativePolynomial): sage: Polynomial.__call__(x, []) x - These were drastically slower prior to :trac:`33165`:: + These were drastically slower prior to :issue:`33165`:: sage: # needs sage.rings.finite_rings sage: R. = GF(31337)[] @@ -754,7 +754,7 @@ cdef class Polynomial(CommutativePolynomial): sage: g(U(2)) -8268 - Sparse tests for :trac:`33165`:: + Sparse tests for :issue:`33165`:: sage: R. = PolynomialRing(QQ, sparse=True) sage: f = x^1000000 + 1 @@ -1030,7 +1030,7 @@ cdef class Polynomial(CommutativePolynomial): sage: x^3 - 3 > 393939393 True - Test comparison with zero (:trac:`18633`):: + Test comparison with zero (:issue:`18633`):: sage: 0 < R(1) True @@ -1150,7 +1150,7 @@ cdef class Polynomial(CommutativePolynomial): sage: pol[:6] 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x - Any other kind of slicing is an error, see :trac:`18940`:: + Any other kind of slicing is an error, see :issue:`18940`:: sage: f[1:3] Traceback (most recent call last): @@ -1261,7 +1261,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Verify that :trac:`16251` has been resolved, i.e., polynomials with + Verify that :issue:`16251` has been resolved, i.e., polynomials with unhashable coefficients are unhashable:: sage: K. = Qq(9) # needs sage.rings.padics @@ -1495,7 +1495,7 @@ cdef class Polynomial(CommutativePolynomial): 10 The polynomial has to be over a field of characteristic 0 (see - :trac:`24072`):: + :issue:`24072`):: sage: R. = GF(7)[] sage: f = SR(2*w^3 + 1); f # needs sage.symbolic @@ -2012,7 +2012,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Make sure :trac:`9093` is fixed:: + Make sure :issue:`9093` is fixed:: sage: R(1).is_square() True @@ -2023,7 +2023,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R(0).is_square() True - Make sure :trac:`35860` is fixed:: + Make sure :issue:`35860` is fixed:: sage: S. = PolynomialRing(ZZ) sage: is_square(S(1), True)[1].parent() @@ -2303,7 +2303,7 @@ cdef class Polynomial(CommutativePolynomial): ....: assert f(f.any_root(K)) == 0 Check that our Cantor-Zassenhaus implementation does not loop - over finite fields of even characteristic (see :trac:`16162`):: + over finite fields of even characteristic (see :issue:`16162`):: sage: # needs sage.rings.finite_rings sage: K. = GF(2**8) @@ -2346,7 +2346,7 @@ cdef class Polynomial(CommutativePolynomial): + a^27 + a^25 + a^23 + a^22 + a^20 + a^18 + a^16 + a^14 + a^11 + a^10 + a^8 + a^6 + a^5 + a^4 + a + 1 - Check that :trac:`21998` has been resolved:: + Check that :issue:`21998` has been resolved:: sage: # needs sage.rings.finite_rings sage: K. = GF(2^4) @@ -2481,7 +2481,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`12217` is fixed:: + Check that :issue:`12217` is fixed:: sage: P. = GF(5)[] sage: x/0 @@ -2495,7 +2495,7 @@ cdef class Polynomial(CommutativePolynomial): ... ZeroDivisionError: division by zero in finite field - Check that :trac:`23611` is fixed:: + Check that :issue:`23611` is fixed:: sage: int(1) / x 1/x @@ -2572,7 +2572,7 @@ cdef class Polynomial(CommutativePolynomial): sage: pow(f, 10**7, h) 4*x*t^3 + 2*x*t^2 + 4*x*t + 4 - Check that :trac:`18457` is fixed:: + Check that :issue:`18457` is fixed:: sage: R. = PolynomialRing(GF(5), sparse=True) sage: (1+x)^(5^10) @@ -2610,7 +2610,7 @@ cdef class Polynomial(CommutativePolynomial): 18009460*y^6*x^6 + 2349060*y^5*x^5 + ... + 51*y*x + 1 Check that fallback method is used when it is not possible to compute - the characteristic of the base ring (:trac:`24308`):: + the characteristic of the base ring (:issue:`24308`):: sage: # needs sage.libs.singular sage: kk. = GF(2)[] @@ -2805,7 +2805,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - We verify that :trac:`23020` has been resolved. (There are no + We verify that :issue:`23020` has been resolved. (There are no elements in the Sage library yet that do not implement ``__bool__``, so we have to create one artificially.):: @@ -2898,7 +2898,7 @@ cdef class Polynomial(CommutativePolynomial): sage: latex(x+2) x + 2.0 - The following illustrates the fix of :trac:`2586`:: + The following illustrates the fix of :issue:`2586`:: sage: latex(ZZ['alpha']['b']([0, ZZ['alpha'].0])) \alpha b @@ -3085,7 +3085,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check the problem reported at :trac:`12529` is fixed:: + Check the problem reported at :issue:`12529` is fixed:: sage: # needs sage.rings.finite_rings sage: gens = 'y a0 a1 a2 b0 b1 b2 c1 c2 d0 d1 d2 d3 d4 d5 d6 d7'.split() @@ -3554,7 +3554,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`25022` is fixed:: + Check that :issue:`25022` is fixed:: sage: K. = ZZ[] sage: x.change_ring(SR) == SR['x'].gen() # needs sage.symbolic @@ -3562,7 +3562,7 @@ cdef class Polynomial(CommutativePolynomial): sage: x.change_ring(ZZ['x']) == ZZ['x']['x'].gen() True - Check that :trac:`28541` is fixed:: + Check that :issue:`28541` is fixed:: sage: # needs sage.rings.finite_rings sage: F. = GF(7^2) @@ -3766,7 +3766,7 @@ cdef class Polynomial(CommutativePolynomial): 1.00000000000000 Check that the denominator is an element over the base whenever the base - has no :meth:`denominator` method. This closes :trac:`9063`. :: + has no :meth:`denominator` method. This closes :issue:`9063`. :: sage: R. = GF(5)[] sage: x = R(0) @@ -3781,7 +3781,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18518` is fixed:: + Check that :issue:`18518` is fixed:: sage: R. = PolynomialRing(QQ, sparse=True) sage: p = x^(2^100) - 1/2 @@ -3974,7 +3974,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R(1).derivative(R(x)) 0 - Check that :trac:`28147` is fixed:: + Check that :issue:`28147` is fixed:: sage: # needs sage.rings.finite_rings sage: R. = GF(65537)[] @@ -4011,7 +4011,7 @@ cdef class Polynomial(CommutativePolynomial): ValueError: cannot differentiate with respect to y - Check that :trac:`26844` is fixed by :trac:`28147`:: + Check that :issue:`26844` is fixed by :issue:`28147`:: sage: A = PolynomialRing(GF(3), name='t') sage: K = A.fraction_field() @@ -4019,7 +4019,7 @@ cdef class Polynomial(CommutativePolynomial): sage: t.derivative(t) 1 - Check that :trac:`28187` is fixed:: + Check that :issue:`28187` is fixed:: sage: R. = GF(65537)[] # needs sage.rings.finite_rings sage: x._derivative(2*x) @@ -4104,7 +4104,7 @@ cdef class Polynomial(CommutativePolynomial): sage: g.parent() Univariate Polynomial Ring in x over Rational Field - This shows that the issue at :trac:`7711` is resolved:: + This shows that the issue at :issue:`7711` is resolved:: sage: # needs sage.rings.finite_rings sage: P. = PolynomialRing(GF(2147483647)) @@ -4155,7 +4155,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: Sx. = ZZ[] sage: Sxy. = Sx[] @@ -4176,7 +4176,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.integral(x).derivative(x) == p True - Check that it works with non-integral domains (:trac:`18600`):: + Check that it works with non-integral domains (:issue:`18600`):: sage: x = polygen(Zmod(4)) sage: p = x**4 + 1 @@ -4407,7 +4407,7 @@ cdef class Polynomial(CommutativePolynomial): rings with composite characteristic is not implemented Factoring polynomials over the algebraic numbers (see - :trac:`8544`):: + :issue:`8544`):: sage: R. = QQbar[] # needs sage.rings.number_field sage: (x^8 - 1).factor() # needs sage.rings.number_field @@ -4417,7 +4417,7 @@ cdef class Polynomial(CommutativePolynomial): * (x + 0.7071067811865475? + 0.7071067811865475?*I) * (x + 1) Factoring polynomials over the algebraic reals (see - :trac:`8544`):: + :issue:`8544`):: sage: R. = AA[] # needs sage.rings.number_field sage: (x^8 + 1).factor() # needs sage.rings.number_field @@ -4428,7 +4428,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - This came up in :trac:`7088`:: + This came up in :issue:`7088`:: sage: R. = PolynomialRing(ZZ) sage: f = 12*x^10 + x^9 + 432*x^3 + 9011 @@ -4439,7 +4439,7 @@ cdef class Polynomial(CommutativePolynomial): sage: F = f^2 * g^3 * 7; F.factor() # needs sage.libs.pari 7 * (12*x^10 + x^9 + 432*x^3 + 9011)^2 * (13*x^11 + 89*x^3 + 1)^3 - This example came up in :trac:`7097`:: + This example came up in :issue:`7097`:: sage: # needs sage.rings.number_field sage: x = polygen(QQ) @@ -4531,7 +4531,7 @@ cdef class Polynomial(CommutativePolynomial): sage: A(x^2 - 1/3).factor() (T - a) * (T + a) - Test that :trac:`10279` is fixed:: + Test that :issue:`10279` is fixed:: sage: # needs sage.rings.number_field sage: R. = PolynomialRing(QQ) @@ -4550,7 +4550,7 @@ cdef class Polynomial(CommutativePolynomial): ... sage: pari.default("debug", 0) - Test that :trac:`10369` is fixed:: + Test that :issue:`10369` is fixed:: sage: # needs sage.rings.number_field sage: x = polygen(QQ) @@ -4634,7 +4634,7 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(f) (x - a1) * (x^2 + a1*x + a1^2) - We check that :trac:`7554` is fixed:: + We check that :issue:`7554` is fixed:: sage: L. = LaurentPolynomialRing(QQ) sage: F = L.fraction_field() @@ -4652,14 +4652,14 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(p) # needs sage.libs.singular (a/(a + 2)) * (x - a) * (b*x + c)^2 - Check that :trac:`24973` is fixed:: + Check that :issue:`24973` is fixed:: sage: x1 = ZZ['x'].gen() sage: x2 = ZZ['x']['x'].gen() sage: (x1 - x2).factor() # needs sage.libs.singular -x + x - Check that :trac:`26421' is fixed:: + Check that :issue:`26421' is fixed:: sage: R. = LaurentPolynomialRing(ZZ) sage: P. = R[] @@ -4667,7 +4667,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.factor() # needs sage.libs.singular (x - 5) * (x - 2*t) * (x^2 - 2) - Check that :trac:`29266` is fixed: + Check that :issue:`29266` is fixed: sage: f = t*x + t sage: f.is_irreducible() # needs sage.libs.singular @@ -5219,7 +5219,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`32033` has been fixed:: + Check that :issue:`32033` has been fixed:: sage: R. = GF(3)[] sage: lcm(R(0), R(0)) @@ -5833,7 +5833,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: c = x^2^100 + 1 @@ -5878,7 +5878,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(Zmod(4), sparse=True) sage: (2*x^2^100 + 2).is_nilpotent() @@ -6359,7 +6359,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: (x^2^100 + x^8 - 1).padded_list(10) @@ -7000,7 +7000,7 @@ cdef class Polynomial(CommutativePolynomial): sage: h.parent() is R # needs sage.libs.pari sage.modules True - Check that :trac:`13672` is fixed:: + Check that :issue:`13672` is fixed:: sage: R. = GF(2)[] sage: S. = R[] @@ -7009,7 +7009,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.resultant(g) # needs sage.libs.pari t^4 + t - Check that :trac:`15061` is fixed:: + Check that :issue:`15061` is fixed:: sage: R. = PowerSeriesRing(QQ) sage: F = R([1,1],2) @@ -7018,7 +7018,7 @@ cdef class Polynomial(CommutativePolynomial): sage: P.resultant(P.derivative()) # needs sage.libs.pari -4 - 4*T + O(T^2) - Check that :trac:`16360` is fixed:: + Check that :issue:`16360` is fixed:: sage: K. = FunctionField(QQ) sage: R. = K[] @@ -7035,7 +7035,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.resultant(g) # needs sage.libs.pari 0 - Check that :trac:`17817` is fixed:: + Check that :issue:`17817` is fixed:: sage: A. = Frac(PolynomialRing(QQ,'a,b,c')) sage: B. = PolynomialRing(A,'d,e,f') @@ -7049,7 +7049,7 @@ cdef class Polynomial(CommutativePolynomial): + (48*c^4)*e^2*x^2 + ((-12*a*c)/b*d^2*e + (-12*b*c)*e)*x + (16*c^4)*e^3 + (4*a^3*b^3)/c - Test for :trac:`10978`:: + Test for :issue:`10978`:: sage: # needs sage.libs.pari sage.rings.complex_double sage.symbolic sage: R. = PolynomialRing(CDF) @@ -7658,7 +7658,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.discriminant() # needs sage.libs.pari 1 - Check that :trac:`13672` is fixed:: + Check that :issue:`13672` is fixed:: sage: R. = GF(5)[] sage: S. = R[] @@ -7666,7 +7666,7 @@ cdef class Polynomial(CommutativePolynomial): sage: (f - t).discriminant() # needs sage.rings.finite_rings 4*t^5 - The following examples show that :trac:`11782` has been fixed:: + The following examples show that :issue:`11782` has been fixed:: sage: var('x') # needs sage.symbolic x @@ -7675,13 +7675,13 @@ cdef class Polynomial(CommutativePolynomial): sage: ZZ.quo(9)['x'](2*x^3 + x^2 + x).discriminant() # needs sage.libs.pari sage.symbolic 2 - This was fixed by :trac:`15422`:: + This was fixed by :issue:`15422`:: sage: R. = PolynomialRing(Qp(2)) # needs sage.rings.padics sage: (s^2).discriminant() # needs sage.rings.padics 0 - This was fixed by :trac:`16014`:: + This was fixed by :issue:`16014`:: sage: # needs sage.modules sage: PR. = QQ[] @@ -7693,7 +7693,7 @@ cdef class Polynomial(CommutativePolynomial): sage: det(mu*E1 + E2).discriminant().degrees() # needs sage.libs.pari (24, 12, 12, 8, 8, 8, 8) - This addresses an issue raised by :trac:`15061`:: + This addresses an issue raised by :issue:`15061`:: sage: R. = PowerSeriesRing(QQ) sage: F = R([1,1],2) @@ -7976,7 +7976,7 @@ cdef class Polynomial(CommutativePolynomial): [] For some other polynomials, no roots can be found at the moment - due to the way roots are computed. :trac:`17516` addresses + due to the way roots are computed. :issue:`17516` addresses these defects. Until that gets implemented, one such example is the following:: @@ -8291,12 +8291,12 @@ cdef class Polynomial(CommutativePolynomial): sage: p.roots(ring=QQbar) # needs sage.rings.number_field [(-5.9223865215328558?e225, 1), (5.9223865215328558?e225, 1)] - Check that :trac:`30522` is fixed:: + Check that :issue:`30522` is fixed:: sage: PolynomialRing(SR, names="x")("x^2").roots() # needs sage.symbolic [(0, 2)] - Check that :trac:`30523` is fixed:: + Check that :issue:`30523` is fixed:: sage: PolynomialRing(SR, names="x")("x^2 + q").roots() # needs sage.symbolic [(-sqrt(-q), 1), (sqrt(-q), 1)] @@ -8329,7 +8329,7 @@ cdef class Polynomial(CommutativePolynomial): :meth:`sage.symbolic.expression.Expression.solve` which in turn uses Maxima to find radical solutions. Some solutions may be lost in this approach. - Once :trac:`17516` gets implemented, all possible radical + Once :issue:`17516` gets implemented, all possible radical solutions should become available. If `L` is ``AA`` or ``RIF``, and `K` is ``ZZ``, ``QQ``, or ``AA``, then the root isolation @@ -8396,7 +8396,7 @@ cdef class Polynomial(CommutativePolynomial): sage: factor(x^3 - 1) (x - 1) * (x^2 + x + 1) - This shows that the issue from :trac:`6237` is fixed:: + This shows that the issue from :issue:`6237` is fixed:: sage: R. = QQ[] sage: g = -27*u^14 - 32*u^9 @@ -8407,7 +8407,7 @@ cdef class Polynomial(CommutativePolynomial): [(-1.0345637159435719, 1), (0.0, 9), (-0.3196977699902601 - 0.9839285635706636*I, 1), (-0.3196977699902601 + 0.9839285635706636*I, 1), (0.8369796279620465 - 0.6081012947885318*I, 1), (0.8369796279620465 + 0.6081012947885318*I, 1)] - This shows that the issue at :trac:`2418` is fixed:: + This shows that the issue at :issue:`2418` is fixed:: sage: x = polygen(QQ) sage: p = (x^50/2^100 + x^10 + x + 1).change_ring(ComplexField(106)) # needs sage.rings.real_mpfr @@ -8416,7 +8416,7 @@ cdef class Polynomial(CommutativePolynomial): sage: [abs(p(rt)) < eps for rt in rts] == [True]*50 # needs sage.rings.number_field True - This shows that the issue at :trac:`10901` is fixed:: + This shows that the issue at :issue:`10901` is fixed:: sage: # needs sage.symbolic sage: a = var('a'); R. = SR[] @@ -8431,7 +8431,7 @@ cdef class Polynomial(CommutativePolynomial): TypeError: cannot evaluate symbolic expression to a numeric value We can find roots of polynomials defined over `\ZZ` or `\QQ` - over the `p`-adics, see :trac:`15422`:: + over the `p`-adics, see :issue:`15422`:: sage: R. = ZZ[] sage: pol = (x - 1)^2 @@ -8450,14 +8450,14 @@ cdef class Polynomial(CommutativePolynomial): sage: parent(r[0]) 3-adic Ring with capped relative precision 5 - Spurious crash with pari-2.5.5, see :trac:`16165`:: + Spurious crash with pari-2.5.5, see :issue:`16165`:: sage: f = (1+x+x^2)^3 sage: f.roots(ring=CC) # needs sage.libs.pari sage.rings.real_mpfr [(-0.500000000000000 - 0.866025403784439*I, 3), (-0.500000000000000 + 0.866025403784439*I, 3)] - Test a crash reported at :trac:`19649`:: + Test a crash reported at :issue:`19649`:: sage: polRing. = PolynomialRing(ZZ) sage: j = (x+1)^2 * (x-1)^7 * (x^2-x+1)^5 @@ -8467,19 +8467,19 @@ cdef class Polynomial(CommutativePolynomial): (0.500000000000000 - 0.866025403784439*I, 5), (0.500000000000000 + 0.866025403784439*I, 5)] - Test that some large finite rings can be handled (:trac:`13825`):: + Test that some large finite rings can be handled (:issue:`13825`):: sage: R. = IntegerModRing(20000009)[] # needs sage.libs.pari sage: eq = x^6+x-17 sage: eq.roots(multiplicities=False) # needs sage.libs.pari [3109038, 17207405] - Test that roots in fixed modulus p-adic fields work (:trac:`17598`):: + Test that roots in fixed modulus p-adic fields work (:issue:`17598`):: sage: len(cyclotomic_polynomial(3).roots(ZpFM(739, 566))) # needs sage.rings.padics 2 - Check that :trac:`26421` is fixed:: + Check that :issue:`26421` is fixed:: sage: R. = LaurentPolynomialRing(ZZ) sage: P. = R[] @@ -8487,7 +8487,7 @@ cdef class Polynomial(CommutativePolynomial): sage: p.roots() # needs sage.libs.singular [(5, 1), (2*t, 1)] - Check that :trac:`31040` is fixed:: + Check that :issue:`31040` is fixed:: sage: R. = QQ[] sage: K. = Qq(3).extension(x^2 + 1) # needs sage.rings.padics @@ -8497,14 +8497,14 @@ cdef class Polynomial(CommutativePolynomial): + 2*a*3^11 + 2*a*3^12 + 2*a*3^13 + 2*a*3^14 + 2*a*3^15 + 2*a*3^16 + 2*a*3^17 + 2*a*3^18 + 2*a*3^19 + O(3^20), 1)] - Check that :trac:`31710` is fixed:: + Check that :issue:`31710` is fixed:: sage: CBF['x'].zero().roots(multiplicities=False) # needs sage.libs.flint Traceback (most recent call last): ... ArithmeticError: taking the roots of the zero polynomial - Check that :trac:`33979` is fixed:: + Check that :issue:`33979` is fixed:: sage: # needs sage.libs.pari sage: n = randint(2, 10^6) @@ -9175,7 +9175,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`28395` is fixed:: + Check that :issue:`28395` is fixed:: sage: P. = QQ[] sage: u = t^4 + 3*t^2 + 1 @@ -9254,7 +9254,7 @@ cdef class Polynomial(CommutativePolynomial): TESTS: - Check that :trac:`28395` is fixed:: + Check that :issue:`28395` is fixed:: sage: P. = QQ[] sage: u = t^10 + 4*t^9 + 8*t^8 + 18*t^7 + 81*t^6 + 272*t^5 + 567*t^4 + 882*t^3 + 2744*t^2 + 9604*t + 16807 @@ -10133,7 +10133,7 @@ cdef class Polynomial(CommutativePolynomial): sage: radical(12 * x^5) 6*x - If self has a factor of multiplicity divisible by the characteristic (see :trac:`8736`):: + If self has a factor of multiplicity divisible by the characteristic (see :issue:`8736`):: sage: P. = GF(2)[] sage: (x^3 + x^2).radical() # needs sage.rings.finite_rings @@ -10225,7 +10225,7 @@ cdef class Polynomial(CommutativePolynomial): sage: f.norm(int(2)) 2.00000000000000 - Check that :trac:`18600` is fixed:: + Check that :issue:`18600` is fixed:: sage: R. = PolynomialRing(ZZ, sparse=True) sage: (x^2^100 + 1).norm(1) # needs sage.rings.real_mpfr @@ -11856,7 +11856,7 @@ cdef class Polynomial_generic_dense(Polynomial): """ TESTS: - Check that exceptions are propagated correctly (:trac:`18274`):: + Check that exceptions are propagated correctly (:issue:`18274`):: sage: class BrokenRational(Rational): ....: def __bool__(self): @@ -11953,7 +11953,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - Check that :trac:`13048` and :trac:`2034` are fixed:: + Check that :issue:`13048` and :issue:`2034` are fixed:: sage: # needs sage.rings.number_field sage: R. = QQbar[] @@ -12101,7 +12101,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - Check that :trac:`12552` is fixed:: + Check that :issue:`12552` is fixed:: sage: type(f.degree()) @@ -12184,7 +12184,7 @@ cdef class Polynomial_generic_dense(Polynomial): ZeroDivisionError: division by zero polynomial Polynomials over noncommutative rings are also allowed - (after :trac:`34733`):: + (after :issue:`34733`):: sage: # needs sage.combinat sage.modules sage: HH = QuaternionAlgebra(QQ, -1, -1) @@ -12197,7 +12197,7 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - The following shows that :trac:`16649` is indeed fixed. :: + The following shows that :issue:`16649` is indeed fixed. :: sage: P. = QQ[] sage: R. = P[] @@ -12211,7 +12211,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: h == q*f + r and r.degree() < f.degree() True - :trac:`26907`:: + :issue:`26907`:: sage: P. = ZZ[] sage: R. = P[] @@ -12363,7 +12363,7 @@ cpdef Polynomial generic_power_trunc(Polynomial p, Integer n, long prec) noexcep sage: from sage.rings.polynomial.polynomial_element import generic_power_trunc - sage: for S in [ZZ, GF(3)]: # known bug, not tested (see :trac:`32075`) + sage: for S in [ZZ, GF(3)]: # known bug, not tested (see :issue:`32075`) ....: R = PolynomialRing(S, 'x') ....: for _ in range(100): ....: p = R.random_element() @@ -12579,7 +12579,7 @@ cdef class ConstantPolynomialSection(Map): """ This class is used for conversion from a polynomial ring to its base ring. - Since :trac:`9944`, it calls the :meth:`constant_coefficient` method, + Since :issue:`9944`, it calls the :meth:`constant_coefficient` method, which can be optimized for a particular polynomial type. EXAMPLES:: @@ -12646,7 +12646,7 @@ cdef class PolynomialBaseringInjection(Morphism): We demonstrate that most polynomial ring classes use polynomial base injection maps for coercion. They are supposed to be the fastest maps for that purpose. See - :trac:`9944`. :: + :issue:`9944`. :: sage: # needs sage.rings.padics sage: R. = Qp(3)[] @@ -12673,7 +12673,7 @@ cdef class PolynomialBaseringInjection(Morphism): From: Rational Field To: Univariate Polynomial Ring in x over Rational Field - By :trac:`9944`, there are now only very few exceptions:: + By :issue:`9944`, there are now only very few exceptions:: sage: PolynomialRing(QQ,names=[]).coerce_map_from(QQ) Call morphism: @@ -12806,7 +12806,7 @@ cdef class PolynomialBaseringInjection(Morphism): sage: S.coerce_map_from(R).is_injective() True - Check that :trac:`23203` has been resolved:: + Check that :issue:`23203` has been resolved:: sage: R.is_subring(S) # indirect doctest True From 006fbae2bc6e8d8eb03b3ebceac9ca7789f2d3f6 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 22:12:30 +0000 Subject: [PATCH 14/30] Add doctests for new functions --- .../rings/polynomial/polynomial_element.pyx | 179 +++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 7c66eecce48..a1c5745061c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2057,6 +2057,29 @@ cdef class Polynomial(CommutativePolynomial): product of irreducible polynomials of degree `d` Assumes that self is squarefree. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x + 162) * (x^3 + 7*x + 161) * (x^7 + 9*x + 161) + sage: list(f._distinct_degree_factorisation_squarefree()) + [(x + 162, 1), (1, 2), (x^3 + 7*x + 161, 3), (x^7 + 9*x + 161, 7)] + + :: + + sage: # needs sage.rings.finite_rings + sage: K. = GF(2^8) + sage: R. = K[] + sage: u1 = x + z8^6 + z8^3 + z8 + sage: u2 = x^2 + (z8^5 + z8^3 + z8^2 + 1)*x + z8^4 + z8^3 + z8 + 1 + sage: u3 = x^3 + z8^7 + z8^4 + z8^3 + z8^2 + z8 + sage: f = u1 * u2 * u3 + sage: list(f._distinct_degree_factorisation_squarefree()) + [(x + z8^6 + z8^3 + z8, 1), + (x^2 + (z8^5 + z8^3 + z8^2 + 1)*x + z8^4 + z8^3 + z8 + 1, 2), + (x^3 + z8^7 + z8^4 + z8^3 + z8^2 + z8, 3)] """ q = self.base_ring().order() # p^k R, x = self.parent().objgen() @@ -2092,6 +2115,42 @@ cdef class Polynomial(CommutativePolynomial): a factor from a polynomial of the form `self = prod g_i(x)` with all `g_i(x)` having the same degree. Uses the Cantor Zassenhaus splitting method. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x^2 + 36*x + 34) * (x^2 + 36*x + 132) * (x^2 + 112*x + 48) + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 112*x + 48 + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 36*x + 34 + sage: f._cantor_zassenhaus_split_to_irreducible(2) # random + x^2 + 36*x + 132 + sage: factor = f._cantor_zassenhaus_split_to_irreducible(2) + sage: factor.is_irreducible() + True + sage: f % factor == 0 + True + + :: + + sage: # needs sage.rings.finite_rings + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^2 + z4) * (x + z4^2 + z4 + 1) + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + sage: f._cantor_zassenhaus_split_to_irreducible(1) # random + x + z4^2 + z4 + 1 + sage: factor = f._cantor_zassenhaus_split_to_irreducible(1) + sage: factor.is_irreducible() + True + sage: f % factor == 0 + True """ R = self.parent() q = self.base_ring().order() @@ -2148,6 +2207,42 @@ cdef class Polynomial(CommutativePolynomial): If degree is not None, then only irreducible factors of degree `degree` are searched for, otherwise the smallest degree factor is found. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x^2 + 36*x + 34) * (x^2 + 36*x + 132) * (x^2 + 112*x + 48) + sage: f = (x^2 + 50*x + 28) * (x^5 + 75*x^4 + 87*x^3 + 96*x^2 + 98*x + 40) * (x^7 + 20*x^6 + 16*x^5 + 36*x^4 + 59*x^3 + 3*x^2 + 46*x + 84) + sage: f._any_irreducible_factor_squarefree() + x^2 + 50*x + 28 + sage: f._any_irreducible_factor_squarefree(degree=2) + x^2 + 50*x + 28 + sage: f._any_irreducible_factor_squarefree(degree=5) + x^5 + 75*x^4 + 87*x^3 + 96*x^2 + 98*x + 40 + sage: f._any_irreducible_factor_squarefree(degree=7) + x^7 + 20*x^6 + 16*x^5 + 36*x^4 + 59*x^3 + 3*x^2 + 46*x + 84 + sage: g = (x^2 + 50*x + 28) + sage: g._any_irreducible_factor_squarefree(degree=1) + Traceback (most recent call last): + ... + ValueError: no irreducible factor of degree 1 could be computed from x^2 + 50*x + 28 + + :: + + sage: # needs sage.rings.finite_rings + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^3 + z4^2 + z4) * (x^2 + x + z4^3 + 1) * (x^3 + (z4^3 + z4)*x^2 + z4^2 + 1) + sage: f._any_irreducible_factor_squarefree() + x + z4^3 + z4^2 + z4 + sage: f._any_irreducible_factor_squarefree(degree=1) + x + z4^3 + z4^2 + z4 + sage: f._any_irreducible_factor_squarefree(degree=2) + x^2 + x + z4^3 + 1 + sage: f._any_irreducible_factor_squarefree(degree=3) + x^3 + (z4^3 + z4)*x^2 + z4^2 + 1 """ # If the degree is not None we only want to check a single polynomial if degree is not None: @@ -2173,7 +2268,89 @@ cdef class Polynomial(CommutativePolynomial): def any_irreducible_factor(self, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ - TODO: description with doctests and tests. + Return an irreducible factor of this polynomial. + + INPUT: + + - ``degree`` (None or positive integer) -- Used for polynomials + over finite fields. Attempts to return an irreducible factor of + ``self`` of chosen degree ``degree``. + + - ``assume_squarefree`` (bool) -- Used for polynomials over + finite fields. If ``True``, this polynomial is assumed to be + squarefree. + + - ``assume_distinct_deg`` (bool) -- Used for polynomials over + finite fields. If ``True``, this polynomial is assumed to be + the product of irreducible polynomials of the same degree. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: f = (x + 40)^3 * (x^5 + 76*x^4 + 93*x^3 + 112*x^2 + 22*x + 27)^2 * (x^6 + 50*x^5 + 143*x^4 + 162*x^2 + 109*x + 140) + sage: f.any_irreducible_factor() + x + 40 + sage: f.any_irreducible_factor(degree=5) + x^5 + 76*x^4 + 93*x^3 + 112*x^2 + 22*x + 27 + + When the polynomial is known to be squarefree we can optimise the call + by setting ``assume_squarefree`` to be ``True``:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: g = (x - 1) * (x^3 + 7*x + 161) + sage: g.any_irreducible_factor(assume_squarefree=True) + x + 162 + sage: g.any_irreducible_factor(degree=3, assume_squarefree=True) + x^3 + 7*x + 161 + + If we ask for an irreducible factor which does not exist, the function + will throw a ``ValueError``:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: g = (x - 1) * (x^3 + 7*x + 161) + sage: g.any_irreducible_factor(degree=2, assume_squarefree=True) + Traceback (most recent call last): + ... + ValueError: no irreducible factor of degree 2 could be computed from x^4 + 162*x^3 + 7*x^2 + 154*x + 2 + sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) + + If we assume that the polynomial is product of irreducible polynomials of the + same degree, we must also supply the degree:: + + sage: # needs sage.rings.finite_rings + sage: F = GF(163) + sage: R. = F[] + sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) + sage: h.any_irreducible_factor(assume_distinct_deg=True) + Traceback (most recent call last): + ... + ValueError: degree must be known if distinct degree factorisation is assumed + sage: h.any_irreducible_factor(degree=1, assume_distinct_deg=True) # random + x + 98 + + Also works for extension fields and even characteristic:: + + sage: F. = GF(2^4) + sage: R. = F[] + sage: f = (x + z4^3 + z4^2)^4 * (x^2 + z4*x + z4) * (x^2 + (z4^3 + z4^2 + z4)*x + z4^2 + z4 + 1) + sage: f.any_irreducible_factor() + x + z4^3 + z4^2 + sage: f.any_irreducible_factor(degree=2) # random + x^2 + (z4^3 + z4^2 + z4)*x + z4^2 + z4 + 1 + + We can also use this function for polynomials which are not defined over finite + fields, but this simply falls back to a slow method of factorisation:: + + sage: R. = ZZ[] + sage: f = 3*x^4 + 2*x^3 + sage: f.any_irreducible_factor() + 3*x + 2 """ # Make sure the user inputted something reasonable for degree if degree is not None: From f0ba87e7978a6120ecffbf9e1728f4d60ba0112b Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 29 Jan 2024 22:48:48 +0000 Subject: [PATCH 15/30] every time, whitespace... --- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index a1c5745061c..79ca95a2130 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2309,7 +2309,7 @@ cdef class Polynomial(CommutativePolynomial): If we ask for an irreducible factor which does not exist, the function will throw a ``ValueError``:: - + sage: # needs sage.rings.finite_rings sage: F = GF(163) sage: R. = F[] From c65548f72226343350b5378c9d8e24ccc7c7d98e Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Tue, 30 Jan 2024 23:43:34 +0000 Subject: [PATCH 16/30] Ensure that the roots returned are of the expected type and add a note about this --- src/sage/rings/polynomial/polynomial_element.pyx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 79ca95a2130..e4e6f820b77 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2601,10 +2601,17 @@ cdef class Polynomial(CommutativePolynomial): raise(f"cannot coerce polynomial {f} to the supplied ring: {ring}") return f.any_root() - # Otherwise change the ring of this degree `degree` irreducible - # polynomial and attempt to find a root from this - F_ext = self.base_ring().extension(f, names="a") - return F_ext.gen() + # Otherwise compute an extension ring of the correct degree and + # compute a root + # TODO: a faster option would be to create an extension with `f` + # as F_ext = self.base_ring().extension(f, names="a") + # however this returns a quotient ring rather than a + # FiniteField type if the base field is a non-prime field, + # and this slower option is chosen to ensure the root is + # over explicitly a FiniteField type. + F_ext = self.base_ring().extension(f.degree(), names="a") + f = f.change_ring(F_ext) + return f.any_root() def __truediv__(left, right): r""" From e4888173cf3501b8ba9bef42a4a2bd99a77abb33 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Tue, 30 Jan 2024 23:50:52 +0000 Subject: [PATCH 17/30] Whitespace --- src/sage/rings/polynomial/polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e4e6f820b77..d4ebbf5702d 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2603,11 +2603,11 @@ cdef class Polynomial(CommutativePolynomial): # Otherwise compute an extension ring of the correct degree and # compute a root - # TODO: a faster option would be to create an extension with `f` + # TODO: a faster option would be to create an extension with `f` # as F_ext = self.base_ring().extension(f, names="a") # however this returns a quotient ring rather than a # FiniteField type if the base field is a non-prime field, - # and this slower option is chosen to ensure the root is + # and this slower option is chosen to ensure the root is # over explicitly a FiniteField type. F_ext = self.base_ring().extension(f.degree(), names="a") f = f.change_ring(F_ext) From 450b0cd983fd74269f41ae1431a9a6941bb17070 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Wed, 31 Jan 2024 22:10:54 +0000 Subject: [PATCH 18/30] More comments and refactoring of any_root --- .../rings/polynomial/polynomial_element.pyx | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index d4ebbf5702d..e2742581a1c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2416,7 +2416,7 @@ cdef class Polynomial(CommutativePolynomial): # But if any degree is allowed then there should certainly be a factor if self has degree > 0 raise AssertionError(f"no irreducible factor was computed for {self}. Bug.") - def any_root(self, ring=None, degree=None, assume_squarefree=False): + def any_root(self, ring=None, degree=None, assume_squarefree=False, assume_distinct_deg=False): """ Return a root of this polynomial in the given ring. @@ -2436,6 +2436,15 @@ cdef class Polynomial(CommutativePolynomial): finite fields. If ``True``, this polynomial is assumed to be squarefree. + - ``assume_distinct_deg`` (bool) -- Used for polynomials over + finite fields. If ``True``, all factors of this polynomial + are assumed to have degree ``degree``. + + .. WARNING:: + + Negative degree input will be deprecated. Instead use + ``assume_distinct_deg``. + EXAMPLES:: sage: # needs sage.rings.finite_rings @@ -2541,9 +2550,8 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: polynomial x^2 + 1 has no roots """ - # When not working over a finite field, do the simple - # thing of factoring for roots and picking the first - # root. If none available, raise an error. + # When not working over a finite field, do the simplecthing of factoring for + # roots and picking the first root. If none are available, raise an error. from sage.categories.finite_fields import FiniteFields if not self.base_ring() in FiniteFields(): rs = self.roots(ring=ring, multiplicities=False) @@ -2551,7 +2559,7 @@ cdef class Polynomial(CommutativePolynomial): return rs[0] raise ValueError(f"polynomial {self} has no roots") - # Look for a linear factor, if there is none, raise a ValueError + # When the degree is none, we only look for a linear factor if degree is None: # if a ring is given try and coerce the polynomial into this ring if ring is not None: @@ -2562,7 +2570,7 @@ cdef class Polynomial(CommutativePolynomial): # try and find a linear irreducible polynomial from f to compute a root try: - f = self.any_irreducible_factor(degree=ZZ(1), assume_squarefree=assume_squarefree) + f = self.any_irreducible_factor(degree=1, assume_squarefree=assume_squarefree) except ValueError: raise ValueError(f"no root of polynomial {self} can be computed") @@ -2571,8 +2579,10 @@ cdef class Polynomial(CommutativePolynomial): # The old version of `any_root()` allowed degree < 0 to indicate that the input polynomial # had a distinct degree factorisation, we pass this to any_irreducible_factor as a bool and # ensure that the degree is positive. - assume_distinct_deg = False + degree = ZZ(degree) if degree < 0: + from sage.misc.superseded import deprecation + deprecation(37170, "negative ``degree`` will be disallowed. Instead use the bool `assume_distinct_deg`.") degree = ZZ(abs(degree)) assume_distinct_deg = True @@ -2581,7 +2591,9 @@ cdef class Polynomial(CommutativePolynomial): # if the degree and a ring is given however, instead compute a degree `degree` factor in the # base ring and then find a factor from this in the supplied ring. try: - f = self.any_irreducible_factor(degree=degree, assume_squarefree=assume_squarefree, assume_distinct_deg=assume_distinct_deg) + f = self.any_irreducible_factor(degree=degree, + assume_squarefree=assume_squarefree, + assume_distinct_deg=assume_distinct_deg) except ValueError: raise ValueError(f"no irreducible factor of degree {degree} can be computed from {self}") @@ -2592,25 +2604,24 @@ cdef class Polynomial(CommutativePolynomial): return root return ring(root) - # If the user asks for a specific ring, find the root in this ring from - # the polynomial of user supplied degree `degree` - if ring is not None: - try: - f = f.change_ring(ring) - except ValueError: - raise(f"cannot coerce polynomial {f} to the supplied ring: {ring}") - return f.any_root() - - # Otherwise compute an extension ring of the correct degree and - # compute a root - # TODO: a faster option would be to create an extension with `f` - # as F_ext = self.base_ring().extension(f, names="a") - # however this returns a quotient ring rather than a - # FiniteField type if the base field is a non-prime field, - # and this slower option is chosen to ensure the root is - # over explicitly a FiniteField type. - F_ext = self.base_ring().extension(f.degree(), names="a") - f = f.change_ring(F_ext) + # We are now in the case where the irreducible polynomial we have found is + # of degree > 1. The old version of this function simply computed the roots + # of this by calling f.roots(ring)... I don't really understand why + # though, as we can simply ask for f.any_root() for this polynomial over the + # new ring? + if ring is None: + # TODO: a faster option would be to create an extension with `f` + # as F_ext = self.base_ring().extension(f, names="a") + # however this returns a quotient ring rather than a + # FiniteField type if the base field is a non-prime field, + # so this slower option is chosen to ensure the root is + # over explicitly a FiniteField type. + ring = self.base_ring().extension(f.degree(), names="a") + + # Now we look for a linear root of this irreducible polynomial of degree `degree` + # over the user supplied ring or the extension we just computed. If the user sent + # a bad ring here of course there may be no root found. + f = f.change_ring(ring) return f.any_root() def __truediv__(left, right): From 6b6758267d32ee4508346183bd01fc6acac4bf14 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Wed, 31 Jan 2024 22:11:18 +0000 Subject: [PATCH 19/30] Modify doctests to handle that any_root() is no longer deterministic --- .../rings/finite_rings/conway_polynomials.py | 24 +++++------ .../rings/finite_rings/hom_finite_field.pyx | 42 +++++-------------- .../finite_rings/hom_finite_field_givaro.pyx | 20 +++------ .../polynomial/polynomial_quotient_ring.py | 2 +- src/sage/schemes/elliptic_curves/cm.py | 2 +- src/sage/schemes/elliptic_curves/ell_field.py | 4 +- 6 files changed, 32 insertions(+), 62 deletions(-) diff --git a/src/sage/rings/finite_rings/conway_polynomials.py b/src/sage/rings/finite_rings/conway_polynomials.py index 02779f49bca..fcb3e2ab8d6 100644 --- a/src/sage/rings/finite_rings/conway_polynomials.py +++ b/src/sage/rings/finite_rings/conway_polynomials.py @@ -136,7 +136,7 @@ class PseudoConwayLattice(WithEqualityById, SageObject): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + x + 1 TESTS:: @@ -164,16 +164,16 @@ def __init__(self, p, use_database=True): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(3) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + 2*x + 1 sage: # needs sage.rings.finite_rings sage: PCL = PseudoConwayLattice(5, use_database=False) - sage: PCL.polynomial(12) + sage: PCL.polynomial(12) # random x^12 + 4*x^11 + 2*x^10 + 4*x^9 + 2*x^8 + 2*x^7 + 4*x^6 + x^5 + 2*x^4 + 2*x^2 + x + 2 - sage: PCL.polynomial(6) + sage: PCL.polynomial(6) # random x^6 + x^5 + 4*x^4 + 3*x^3 + 3*x^2 + 2*x + 2 - sage: PCL.polynomial(11) + sage: PCL.polynomial(11) # random x^11 + x^6 + 3*x^3 + 4*x + 3 """ self.p = p @@ -215,11 +215,11 @@ def polynomial(self, n): sage: # needs sage.rings.finite_rings sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) - sage: PCL.polynomial(3) + sage: PCL.polynomial(3) # random x^3 + x + 1 - sage: PCL.polynomial(4) + sage: PCL.polynomial(4) # random x^4 + x^3 + 1 - sage: PCL.polynomial(60) + sage: PCL.polynomial(60) # random x^60 + x^59 + x^58 + x^55 + x^54 + x^53 + x^52 + x^51 + x^48 + x^46 + x^45 + x^42 + x^41 + x^39 + x^38 + x^37 + x^35 + x^32 + x^31 + x^30 + x^28 + x^24 + x^22 + x^21 + x^18 + x^17 + x^16 + x^15 + x^14 + x^10 + x^8 + x^7 + x^5 + x^3 + x^2 + x + 1 """ if n in self.nodes: @@ -239,7 +239,7 @@ def polynomial(self, n): # TODO: something like the following # gcds = [n.gcd(d) for d in self.nodes.keys()] # xi = { m: (...) for m in gcds } - xi = {q: self.polynomial(n//q).any_root(K, -n//q, assume_squarefree=True) + xi = {q: self.polynomial(n//q).any_root(K, n//q, assume_squarefree=True, assume_distinct_deg=True) for q in n.prime_divisors()} # The following is needed to ensure that in the concrete instantiation @@ -402,9 +402,9 @@ def _frobenius_shift(K, generators, check_only=False): sage: f20 = x^20 + x^19 + x^15 + x^13 + x^12 + x^11 + x^9 + x^8 + x^7 + x^4 + x^2 + x + 1 sage: f12 = x^12 + x^10 + x^9 + x^8 + x^4 + x^2 + 1 sage: K. = GF(2^60, modulus='first_lexicographic') - sage: x30 = f30.any_root(K) - sage: x20 = f20.any_root(K) - sage: x12 = f12.any_root(K) + sage: x30 = f30.roots(K, multiplicities=False)[0] + sage: x20 = f20.roots(K, multiplicities=False)[0] + sage: x12 = f12.roots(K, multiplicities=False)[0] sage: generators = {2: x30, 3: x20, 5: x12} sage: from sage.rings.finite_rings.conway_polynomials import _frobenius_shift, _find_pow_of_frobenius sage: _frobenius_shift(K, generators) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 85dadd6fb2c..8f38408cda9 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -16,32 +16,25 @@ Construction of an embedding:: sage: k. = GF(3^7) sage: K. = GF(3^21) - sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: f(t) + sage: f(t) # random T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T The map `f` has a method ``section`` which returns a partially defined map which is the inverse of `f` on the image of `f`:: - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) + sage: g(f(t^3+t^2+1)) # random t^3 + t^2 + 1 - sage: g(T) - Traceback (most recent call last): - ... - ValueError: T is not in the image of Ring morphism: - From: Finite Field in t of size 3^7 - To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T There is no embedding of `GF(5^6)` into `GF(5^11)`:: @@ -132,14 +125,6 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: g = f.section() sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 - - sage: g(T) - Traceback (most recent call last): - ... - ValueError: T is not in the image of Ring morphism: - From: Finite Field in t of size 3^7 - To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T """ for root, _ in x.minimal_polynomial().roots(ring=self.codomain()): if self._inverse(root) == x: @@ -158,7 +143,7 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g._repr_() + sage: g._repr_() # random 'Section of Ring morphism:\n From: Finite Field in t of size 3^7\n To: Finite Field in T of size 3^21\n Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T' """ return "Section of %s" % self._inverse @@ -202,7 +187,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: from sage.rings.finite_rings.hom_finite_field import FiniteFieldHomomorphism_generic sage: k. = GF(3^7) sage: K. = GF(3^21) - sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 @@ -299,7 +284,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(3^3) sage: K. = GF(3^9) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) - sage: f(t) + sage: f(t) # random 2*T^6 + 2*T^4 + T^2 + T sage: a = k.random_element() @@ -368,20 +353,13 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(3^7) sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 - sage: g(T) - Traceback (most recent call last): - ... - ValueError: T is not in the image of Ring morphism: - From: Finite Field in t of size 3^7 - To: Finite Field in T of size 3^21 - Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T """ if self.base_map() is not None: raise NotImplementedError @@ -751,14 +729,14 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): sage: kfixed, embed = f.fixed_field() sage: kfixed Finite Field in t_fixed of size 5^2 - sage: embed + sage: embed # random Ring morphism: From: Finite Field in t_fixed of size 5^2 To: Finite Field in t of size 5^6 Defn: t_fixed |--> 4*t^5 + 2*t^4 + 4*t^2 + t sage: tfixed = kfixed.gen() - sage: embed(tfixed) + sage: embed(tfixed) # random 4*t^5 + 2*t^4 + 4*t^2 + t """ if self._degree_fixed == 1: diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index bc685731610..87281d41c64 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -59,7 +59,7 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: k. = GF(3^2) sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) - sage: g = f.section(); g + sage: g = f.section(); g # random Section of Ring morphism: From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 @@ -105,16 +105,8 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) sage: g = f.section() - sage: g(f(t+1)) + sage: g(f(t+1)) # random t + 1 - - sage: g(T) - Traceback (most recent call last): - ... - ValueError: T is not in the image of Ring morphism: - From: Finite Field in t of size 3^2 - To: Finite Field in T of size 3^4 - Defn: t |--> 2*T^3 + 2*T^2 + 1 """ if x.parent() != self.domain(): raise TypeError("%s is not in %s" % (x, self.domain())) @@ -140,7 +132,7 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): sage: from sage.rings.finite_rings.hom_finite_field_givaro import FiniteFieldHomomorphism_givaro sage: k. = GF(3^2) sage: K. = GF(3^4) - sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)); f + sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)); f # random Ring morphism: From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 @@ -182,7 +174,7 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): sage: k. = GF(3^2) sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) - sage: f(t) + sage: f(t) # random 2*T^3 + 2*T^2 + 1 """ if x.parent() != self.domain(): @@ -242,14 +234,14 @@ cdef class FrobeniusEndomorphism_givaro(FrobeniusEndomorphism_finite_field): sage: kfixed, embed = f.fixed_field() sage: kfixed Finite Field in t_fixed of size 5^2 - sage: embed + sage: embed # random Ring morphism: From: Finite Field in t_fixed of size 5^2 To: Finite Field in t of size 5^6 Defn: t_fixed |--> 4*t^5 + 2*t^4 + 4*t^2 + t sage: tfixed = kfixed.gen() - sage: embed(tfixed) + sage: embed(tfixed) # random 4*t^5 + 2*t^4 + 4*t^2 + t """ if self._degree_fixed == 1: diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 6b39d95752b..004078b4559 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -2089,7 +2089,7 @@ def _isomorphic_ring(self): base_image = self.base_ring().modulus().change_ring(isomorphic_ring).any_root() base_to_isomorphic_ring = self.base_ring().hom([isomorphic_ring(base_image)]) modulus = self.modulus().map_coefficients(base_to_isomorphic_ring) - gen = modulus.any_root(assume_squarefree=True, degree=-1) + gen = modulus.any_root(assume_squarefree=True, degree=1, assume_distinct_deg=True) homspace = Hom(self, isomorphic_ring) to_isomorphic_ring = homspace.__make_element_class__(SetMorphism)(homspace, diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index 58bed0a67a4..1f0fd8bd34e 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -271,7 +271,7 @@ def is_HCP(f, check_monic_irreducible=True): continue if d < h and d not in h2list: return zero - jp = fp.any_root(degree=-1, assume_squarefree=True) + jp = fp.any_root(degree=1, assume_squarefree=True, assume_distinct_deg=True) E = EllipticCurve(j=jp) if E.is_supersingular(): continue diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 81d4a8ea359..d87661b07b5 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -2180,7 +2180,7 @@ def point_of_order(E, n): sage: from sage.schemes.elliptic_curves.ell_field import point_of_order sage: E = EllipticCurve(GF(101), [1,2,3,4,5]) - sage: P = point_of_order(E, 5); P + sage: P = point_of_order(E, 5); P # random (50*Y^5 + 48*Y^4 + 26*Y^3 + 37*Y^2 + 48*Y + 15 : 25*Y^5 + 31*Y^4 + 79*Y^3 + 39*Y^2 + 3*Y + 20 : 1) sage: P.base_ring() Finite Field in Y of size 101^6 @@ -2191,7 +2191,7 @@ def point_of_order(E, n): :: - sage: Q = point_of_order(E, 8); Q + sage: Q = point_of_order(E, 8); Q # random (69*x^5 + 24*x^4 + 100*x^3 + 65*x^2 + 88*x + 97 : 65*x^5 + 28*x^4 + 5*x^3 + 45*x^2 + 42*x + 18 : 1) sage: 8*Q == 0 and 4*Q != 0 True From cd20de449411a2ddd3e36d1b76c03b08780b9e5d Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 13:11:22 +0000 Subject: [PATCH 20/30] Reintroduce traceback doctests after getting advice --- .../rings/finite_rings/hom_finite_field.pyx | 24 ++++++++++++++++++- .../finite_rings/hom_finite_field_givaro.pyx | 8 +++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 8f38408cda9..babac2568f4 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -35,6 +35,13 @@ map which is the inverse of `f` on the image of `f`:: Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T sage: g(f(t^3+t^2+1)) # random t^3 + t^2 + 1 + sage: g(T) + Traceback (most recent call last): + ... + ValueError: T is not in the image of Ring morphism: + From: Finite Field in t of size 3^7 + To: Finite Field in T of size 3^21 + Defn: ... There is no embedding of `GF(5^6)` into `GF(5^11)`:: @@ -123,8 +130,16 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g(f(t^3+t^2+1)) + sage: g(f(t^3+t^2+1)) # random t^3 + t^2 + 1 + + sage: g(T) + Traceback (most recent call last): + ... + ValueError: T is not in the image of Ring morphism: + From: Finite Field in t of size 3^7 + To: Finite Field in T of size 3^21 + Defn: t |--> ... """ for root, _ in x.minimal_polynomial().roots(ring=self.codomain()): if self._inverse(root) == x: @@ -360,6 +375,13 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 + sage: g(T) + Traceback (most recent call last): + ... + ValueError: T is not in the image of Ring morphism: + From: Finite Field in t of size 3^7 + To: Finite Field in T of size 3^21 + Defn: ... """ if self.base_map() is not None: raise NotImplementedError diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index 87281d41c64..75249339c6c 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -107,6 +107,14 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: g = f.section() sage: g(f(t+1)) # random t + 1 + + sage: g(T) + Traceback (most recent call last): + ... + ValueError: T is not in the image of Ring morphism: + From: Finite Field in t of size 3^2 + To: Finite Field in T of size 3^4 + Defn: t |--> ... """ if x.parent() != self.domain(): raise TypeError("%s is not in %s" % (x, self.domain())) From a5b5403d1b3f4794043712be418103ef44d8c552 Mon Sep 17 00:00:00 2001 From: Giacomo Pope <44242839+GiacomoPope@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:14:44 +0000 Subject: [PATCH 21/30] Apply suggestions from code review Co-authored-by: Lorenz Panny <84067835+yyyyx4@users.noreply.github.com> --- .../rings/polynomial/polynomial_element.pyx | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e2742581a1c..5a20658a96d 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2052,11 +2052,11 @@ cdef class Polynomial(CommutativePolynomial): Helper function for any_irreducible_factor which computes the distinct degree factorisation of `self`. - Creates an iterator for all valid degrees `d`, and return + Creates an iterator for all valid degrees `d`, and returns tuples of the form `(a_d, d)` for a polynomial `a_d` the - product of irreducible polynomials of degree `d` + product of irreducible polynomials of degree `d`. - Assumes that self is squarefree. + Assumes that this polynomial is squarefree. EXAMPLES:: @@ -2096,9 +2096,8 @@ cdef class Polynomial(CommutativePolynomial): w = pow(w, q, v) ad = v.gcd(w - x) - yield (ad, d) - if not ad.is_one(): + yield (ad, d) v = v // ad w = w % v @@ -2111,10 +2110,10 @@ cdef class Polynomial(CommutativePolynomial): def _cantor_zassenhaus_split_to_irreducible(self, degree): """ - Helper function for any_irreducible_factor which computes + Helper function for :meth:`any_irreducible_factor` which computes a factor from a polynomial of the form `self = prod g_i(x)` - with all `g_i(x)` having the same degree. Uses the Cantor - Zassenhaus splitting method. + with all `g_i(x)` irreducible of the same degree. Uses the + Cantor-Zassenhaus splitting method. EXAMPLES:: @@ -2160,14 +2159,14 @@ cdef class Polynomial(CommutativePolynomial): return self # We expect to succeed with greater than 1/2 probability, - #so if we try 1000 times and fail, there's a bug somewhere. + # so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): # Sample a polynomial uniformly from R T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately if q % 2: - h = self.gcd(pow(T, (q-1)//2, self)-1) + h = self.gcd(pow(T, (q-1)//2, self) - 1) else: # Compute the trace of T with field of order 2^k # sum T^(2^i) for i in range (degree * k) @@ -2185,11 +2184,10 @@ cdef class Polynomial(CommutativePolynomial): return h # Else check if we have a non-trivial factor and keep going - if not hd.is_zero() and hd != self.degree(): - if 2*hd <= self.degree(): - return h._cantor_zassenhaus_split_to_irreducible(degree) - else: - return (self//h)._cantor_zassenhaus_split_to_irreducible(degree) + if 0 < hd < self.degree(): + if 2*hd > self.degree(): + h = self // h + return h._cantor_zassenhaus_split_to_irreducible(degree) # If you are reaching this error, chances are there's a bug in the code. raise AssertionError(f"no splitting of degree {degree} found for {self}") @@ -2201,7 +2199,7 @@ cdef class Polynomial(CommutativePolynomial): squarefree. Does this by first computing the distinct degree factorisations - of self and thenfinds a factor with Cantor-Zassenhaus + of self and then finds a factor with Cantor-Zassenhaus splitting. If degree is not None, then only irreducible factors of degree @@ -2369,7 +2367,7 @@ cdef class Polynomial(CommutativePolynomial): try: factorisation = self.factor() except (NotImplementedError, ValueError): - raise ValueError(f"Cannot factor {self} over the base ring {self.base_ring()}") + raise ValueError(f"cannot factor {self} over the base ring {self.base_ring()}") if degree is None: return factorisation[0][0] for (poly, e) in factorisation: @@ -2391,7 +2389,7 @@ cdef class Polynomial(CommutativePolynomial): # the user. # Initial checks for bad input - if self.degree().is_zero(): + if self.degree() <= 0: raise ValueError(f"there are no irreducible factors of {self}") # If we know the polynomial is square-free, we can start here @@ -2550,7 +2548,7 @@ cdef class Polynomial(CommutativePolynomial): ... ValueError: polynomial x^2 + 1 has no roots """ - # When not working over a finite field, do the simplecthing of factoring for + # When not working over a finite field, do the simple thing of factoring for # roots and picking the first root. If none are available, raise an error. from sage.categories.finite_fields import FiniteFields if not self.base_ring() in FiniteFields(): @@ -2583,7 +2581,7 @@ cdef class Polynomial(CommutativePolynomial): if degree < 0: from sage.misc.superseded import deprecation deprecation(37170, "negative ``degree`` will be disallowed. Instead use the bool `assume_distinct_deg`.") - degree = ZZ(abs(degree)) + degree = -degree assume_distinct_deg = True # If a certain degree is requested, then we find an irreducible factor of degree `degree` From 713ddf0110a32b8519576cecf0c771058702bc46 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:23:59 +0000 Subject: [PATCH 22/30] Add reviewer suggestions --- .../rings/polynomial/polynomial_element.pyx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 5a20658a96d..4a06cb79a96 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2099,7 +2099,6 @@ cdef class Polynomial(CommutativePolynomial): if not ad.is_one(): yield (ad, d) v = v // ad - w = w % v e = v.degree() @@ -2162,6 +2161,8 @@ cdef class Polynomial(CommutativePolynomial): # so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): # Sample a polynomial uniformly from R + # TODO: once #37118 has been merged, we can call + # R.random_monic_element(2*degree + 1) T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately @@ -2245,23 +2246,16 @@ cdef class Polynomial(CommutativePolynomial): # If the degree is not None we only want to check a single polynomial if degree is not None: for (poly, d) in self._distinct_degree_factorisation_squarefree(): - if d == degree and not poly.is_one(): + if d == degree: return poly._cantor_zassenhaus_split_to_irreducible(degree) # Stop iterating early if the degree is too large elif d > degree: raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") raise ValueError(f"no irreducible factor of degree {degree} could be computed from {self}") - # Otherwise we check all degrees, starting from the smallest + # Otherwise we use the smallest possible d value for (poly, d) in self._distinct_degree_factorisation_squarefree(): - # Skip the split checking if `_distinct_degree_factorisation_squarefree` - # has found no elements of degree `d` - if poly.is_one(): - continue - - # Otherwise find a factor from the distinct degree factor return poly._cantor_zassenhaus_split_to_irreducible(d) - raise ValueError(f"no irreducible factor could be computed from {self}") def any_irreducible_factor(self, degree=None, assume_squarefree=False, assume_distinct_deg=False): @@ -2280,7 +2274,8 @@ cdef class Polynomial(CommutativePolynomial): - ``assume_distinct_deg`` (bool) -- Used for polynomials over finite fields. If ``True``, this polynomial is assumed to be - the product of irreducible polynomials of the same degree. + the product of irreducible polynomials of the same degree. If + this parameter is set to ``True`` then degree must also be set. EXAMPLES:: From 51b733c72667dab5f2fc9e94fa5d99fffbff6f0e Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:34:18 +0000 Subject: [PATCH 23/30] Reformat INPUT for docstring --- .../rings/polynomial/polynomial_element.pyx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 4a06cb79a96..479c52a8123 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2264,18 +2264,18 @@ cdef class Polynomial(CommutativePolynomial): INPUT: - - ``degree`` (None or positive integer) -- Used for polynomials - over finite fields. Attempts to return an irreducible factor of - ``self`` of chosen degree ``degree``. - - - ``assume_squarefree`` (bool) -- Used for polynomials over - finite fields. If ``True``, this polynomial is assumed to be - squarefree. - - - ``assume_distinct_deg`` (bool) -- Used for polynomials over - finite fields. If ``True``, this polynomial is assumed to be - the product of irreducible polynomials of the same degree. If - this parameter is set to ``True`` then degree must also be set. + - ``degree`` (None or positive integer) -- (default: ``None``). + Used for polynomials over finite fields. Attempts to return an + irreducible factor of ``self`` of chosen degree ``degree``. + + - ``assume_squarefree`` (boolean) -- (default: ``False``). + Used for polynomials over finite fields. If ``True``, + this polynomial is assumed to be squarefree. + + - ``assume_distinct_deg`` (boolean) -- (default: ``False``). + Used for polynomials over finite fields. If ``True``, + this polynomial is assumed to be the product of irreducible + polynomials of degree equal to ``degree``. EXAMPLES:: From 89b8a4a29ed5dd0c83f7163f81bacb65275109e4 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:38:43 +0000 Subject: [PATCH 24/30] Clarify TODO about 37118 --- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 479c52a8123..0daee97834a 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2162,7 +2162,7 @@ cdef class Polynomial(CommutativePolynomial): for _ in range(1000): # Sample a polynomial uniformly from R # TODO: once #37118 has been merged, we can call - # R.random_monic_element(2*degree + 1) + # R.random_element(degree=(2*degree+1), monic=True) T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately From bc13fa564461e5617a9298a08dd5c78b6740acf1 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:48:16 +0000 Subject: [PATCH 25/30] Clarify degree=None --- src/sage/rings/polynomial/polynomial_element.pyx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 0daee97834a..bee696451dd 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2265,8 +2265,10 @@ cdef class Polynomial(CommutativePolynomial): INPUT: - ``degree`` (None or positive integer) -- (default: ``None``). - Used for polynomials over finite fields. Attempts to return an - irreducible factor of ``self`` of chosen degree ``degree``. + Used for polynomials over finite fields. If ``None``, returns + the the first factor found (usually the smallest). Otherwise, + attempts to return an irreducible factor of ``self`` of chosen + degree ``degree``. - ``assume_squarefree`` (boolean) -- (default: ``False``). Used for polynomials over finite fields. If ``True``, From 6b633fc2c118aff9ef71587ec94736f363b7aaba Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 15:49:54 +0000 Subject: [PATCH 26/30] Clean up examples --- src/sage/rings/polynomial/polynomial_element.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index bee696451dd..f02c60b5961 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2313,7 +2313,6 @@ cdef class Polynomial(CommutativePolynomial): Traceback (most recent call last): ... ValueError: no irreducible factor of degree 2 could be computed from x^4 + 162*x^3 + 7*x^2 + 154*x + 2 - sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) If we assume that the polynomial is product of irreducible polynomials of the same degree, we must also supply the degree:: @@ -2322,12 +2321,12 @@ cdef class Polynomial(CommutativePolynomial): sage: F = GF(163) sage: R. = F[] sage: h = (x + 57) * (x + 98) * (x + 117) * (x + 145) + sage: h.any_irreducible_factor(degree=1, assume_distinct_deg=True) # random + x + 98 sage: h.any_irreducible_factor(assume_distinct_deg=True) Traceback (most recent call last): ... ValueError: degree must be known if distinct degree factorisation is assumed - sage: h.any_irreducible_factor(degree=1, assume_distinct_deg=True) # random - x + 98 Also works for extension fields and even characteristic:: From 32baddd6ff2cf43f5baeee8a26de68d360e62481 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Thu, 1 Feb 2024 17:44:15 +0000 Subject: [PATCH 27/30] Clarify TODO in CZ function --- src/sage/rings/polynomial/polynomial_element.pyx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index f02c60b5961..7108c7b15f8 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2160,9 +2160,14 @@ cdef class Polynomial(CommutativePolynomial): # We expect to succeed with greater than 1/2 probability, # so if we try 1000 times and fail, there's a bug somewhere. for _ in range(1000): - # Sample a polynomial uniformly from R - # TODO: once #37118 has been merged, we can call - # R.random_element(degree=(2*degree+1), monic=True) + # Sample a polynomial "uniformly" from R + # TODO: once #37118 has been merged, this can be made cleaner, + # as we will actually have access to uniform sampling. + # At the moment, we make an ugly call for polynomials of + # degree exactly 2*degree + 1, instead of polynomials of degree + # less than `self.degree()` as the original desc. of C-Z. + # Additionally, we can use the `monic` flag: + # R.random_element(degree=(self.degree() - 1), monic=True) T = R.random_element(2*degree + 1).monic() # Need to handle odd and even characteristic separately From ba8eeef254ac50521cd8b1c28575e68df0199a2b Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Fri, 2 Feb 2024 13:18:02 +0000 Subject: [PATCH 28/30] Fix failing doctests --- src/sage/rings/finite_rings/homset.py | 2 +- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/finite_rings/homset.py b/src/sage/rings/finite_rings/homset.py index f9d523199ec..5c122a1c6b7 100644 --- a/src/sage/rings/finite_rings/homset.py +++ b/src/sage/rings/finite_rings/homset.py @@ -334,7 +334,7 @@ def _an_element_(self): TESTS:: - sage: Hom(GF(3^3, 'a'), GF(3^6, 'b')).an_element() + sage: Hom(GF(3^3, 'a'), GF(3^6, 'b')).an_element() # random Ring morphism: From: Finite Field in a of size 3^3 To: Finite Field in b of size 3^6 diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 7108c7b15f8..b749a379d40 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2065,7 +2065,7 @@ cdef class Polynomial(CommutativePolynomial): sage: R. = F[] sage: f = (x + 162) * (x^3 + 7*x + 161) * (x^7 + 9*x + 161) sage: list(f._distinct_degree_factorisation_squarefree()) - [(x + 162, 1), (1, 2), (x^3 + 7*x + 161, 3), (x^7 + 9*x + 161, 7)] + [(x + 162, 1), (x^3 + 7*x + 161, 3), (x^7 + 9*x + 161, 7)] :: From c37baf9d466f930ab512b2b911052075a8ab1d23 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 5 Feb 2024 13:17:13 +0000 Subject: [PATCH 29/30] Remove some random tags from doctests and fix reported bug --- src/sage/rings/finite_rings/hom_finite_field.pyx | 4 ++-- src/sage/rings/finite_rings/hom_finite_field_givaro.pyx | 2 +- src/sage/rings/polynomial/polynomial_quotient_ring_element.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index babac2568f4..8da5d0bdbba 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -33,7 +33,7 @@ map which is the inverse of `f` on the image of `f`:: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) # random + sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 sage: g(T) Traceback (most recent call last): @@ -130,7 +130,7 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g(f(t^3+t^2+1)) # random + sage: g(f(t^3+t^2+1)) t^3 + t^2 + 1 sage: g(T) diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index 75249339c6c..078c1f57a41 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -105,7 +105,7 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ sage: K. = GF(3^4) sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) sage: g = f.section() - sage: g(f(t+1)) # random + sage: g(f(t+1)) t + 1 sage: g(T) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 57947c345bf..0754ed31e7c 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -714,7 +714,7 @@ def minpoly(self): sage: (u + 1).minpoly() # needs sage.modules x^6 + 425*x^5 + 19*x^4 + 125*x^3 + 189*x^2 + 239*x + 302 sage: ext = F6.over(F2) # needs sage.modules - sage: ext(u + 1).minpoly() # indirect doctest # needs sage.modules + sage: ext(u + 1).minpoly() # indirect doctest # needs sage.modules # random x^3 + (396*i + 428)*x^2 + (80*i + 39)*x + 9*i + 178 TESTS: From 490ffcb3439a0becdad06f0bb78177a24599e4a2 Mon Sep 17 00:00:00 2001 From: Giacomo Pope Date: Mon, 5 Feb 2024 23:45:24 +0000 Subject: [PATCH 30/30] More robust doctests --- .../rings/finite_rings/hom_finite_field.pyx | 26 ++++++++++++++----- .../finite_rings/hom_finite_field_givaro.pyx | 10 +++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 8da5d0bdbba..b7bbd417273 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -24,6 +24,8 @@ Construction of an embedding:: sage: f(t) # random T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + sage: f(t) == f.im_gens()[0] + True The map `f` has a method ``section`` which returns a partially defined map which is the inverse of `f` on the image of `f`:: @@ -33,8 +35,9 @@ map which is the inverse of `f` on the image of `f`:: From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: g(f(a)) == a + True sage: g(T) Traceback (most recent call last): ... @@ -130,8 +133,9 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): sage: K. = GF(3^21) sage: f = FiniteFieldHomomorphism_generic(Hom(k, K)) sage: g = f.section() - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: g(f(a)) == a + True sage: g(T) Traceback (most recent call last): @@ -207,6 +211,10 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T + sage: a = k.random_element() + sage: b = k.random_element() + sage: f(a) + f(b) == f(a + b) + True sage: k. = GF(3^6) sage: K. = GF(3^9) @@ -373,8 +381,10 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): From: Finite Field in t of size 3^7 To: Finite Field in T of size 3^21 Defn: t |--> T^20 + 2*T^18 + T^16 + 2*T^13 + T^9 + 2*T^8 + T^7 + T^6 + T^5 + T^3 + 2*T^2 + T - sage: g(f(t^3+t^2+1)) - t^3 + t^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: g(f(a) + f(b)) == a + b + True sage: g(T) Traceback (most recent call last): ... @@ -421,7 +431,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: embed = Frob.fixed_field()[1] - sage: hash(embed) # random + sage: hash(embed) # random -2441354824160407762 """ return Morphism.__hash__(self) @@ -760,6 +770,8 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): sage: tfixed = kfixed.gen() sage: embed(tfixed) # random 4*t^5 + 2*t^4 + 4*t^2 + t + sage: embed(tfixed) == embed.im_gens()[0] + True """ if self._degree_fixed == 1: k = FiniteField(self.domain().characteristic()) diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index 078c1f57a41..eebd20056e7 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -64,6 +64,10 @@ cdef class SectionFiniteFieldHomomorphism_givaro(SectionFiniteFieldHomomorphism_ From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 Defn: t |--> 2*T^3 + 2*T^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: g(f(a) + f(b)) == g(f(a)) + g(f(b)) == a + b + True """ if not isinstance(inverse, FiniteFieldHomomorphism_givaro): raise TypeError("The given map is not an instance of FiniteFieldHomomorphism_givaro") @@ -145,6 +149,10 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): From: Finite Field in t of size 3^2 To: Finite Field in T of size 3^4 Defn: t |--> 2*T^3 + 2*T^2 + 1 + sage: a = k.random_element() + sage: b = k.random_element() + sage: f(a) + f(b) == f(a + b) + True sage: k. = GF(3^10) sage: K. = GF(3^20) @@ -184,6 +192,8 @@ cdef class FiniteFieldHomomorphism_givaro(FiniteFieldHomomorphism_generic): sage: f = FiniteFieldHomomorphism_givaro(Hom(k, K)) sage: f(t) # random 2*T^3 + 2*T^2 + 1 + sage: f(t) == f.im_gens()[0] + True """ if x.parent() != self.domain(): raise TypeError("%s is not in %s" % (x, self.domain()))