From 20794e74789acc3562de07ffa97c7936db565ee4 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Wed, 17 Aug 2016 16:41:46 +0530 Subject: [PATCH 01/12] added factoring and irreducibility based methods as is, from the original #13215 ticket --- .../skew_polynomial_finite_field.pxd | 15 + .../skew_polynomial_finite_field.pyx | 1111 +++++++++++++++++ .../rings/polynomial/skew_polynomial_ring.py | 84 ++ 3 files changed, 1210 insertions(+) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd index ccde51c591f..6a17e43d17c 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -3,6 +3,13 @@ from sage.matrix.matrix_dense cimport Matrix_dense cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): + cdef Polynomial _norm + cdef _norm_factor + cdef _optbound + cdef dict _rdivisors + cdef dict _types + cdef _factorization + cdef SkewPolynomial_finite_field_dense _rgcd(self,SkewPolynomial_finite_field_dense other) cdef void _inplace_lrem(self, SkewPolynomial_finite_field_dense other) cdef void _inplace_rrem(self, SkewPolynomial_finite_field_dense other) @@ -14,8 +21,16 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef Py_ssize_t _val_inplace_unit(self) cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other) + cdef Matrix_dense _matphir_c(self) cdef Matrix_dense _matmul_c(self) + # Finding divisors + cdef SkewPolynomial_finite_field_dense _rdivisor_c(P, CenterSkewPolynomial_generic_dense N) + + # Finding factorizations + cdef _factor_c(self) + cdef _factor_uniform_c(self) + # Karatsuba #cpdef RingElement _mul_karatsuba(self, RingElement other, cutoff=*) cpdef SkewPolynomial_finite_field_dense _mul_central(self, SkewPolynomial_finite_field_dense right) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index e193e2f5a3b..a5054fd39e9 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -829,6 +829,1117 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): yc[k] = zero return self._new_c(res,skew_ring,1) + def is_irreducible(self): + """ + Return true if this skew polynomial is irreducible. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + + sage: a = x^2 + t*x + 1 + sage: a.is_irreducible() + False + sage: a.factor() + (x + 4*t^2 + 4*t + 1) * (x + 3*t + 2) + + sage: a = x^2 + t*x + t + 1 + sage: a.is_irreducible() + True + sage: a.factor() + x^2 + t*x + t + 1 + + Skew polynomials of degree `1` are of course irreducible:: + + sage: a = x + t + sage: a.is_irreducible() + True + + A random irreducible skew polynomial is irreducible:: + + sage: a = S.random_irreducible(degree=4,monic=True); a # random + x^4 + (t + 1)*x^3 + (3*t^2 + 2*t + 3)*x^2 + 3*t*x + 3*t + sage: a.is_irreducible() + True + + By convention, constant skew polynomials are not irreducible:: + + sage: S(1).is_irreducible() + False + sage: S(0).is_irreducible() + False + """ + return self.reduced_norm().is_irreducible() + + + def type(self,N): + """ + INPUT: + + - ``N`` -- an irreducible polynomial in the + center of the underlying skew polynomial ring + + OUTPUT: + + The `N`-type of this skew polynomial + + .. NOTE:: + + The result is cached. + + DEFINITION: + + The `N`-type of a skew polynomial `a` is the Partition + `(t_0, t_1, t_2, ...)` defined by + + .. MATH:: + + t_0 + \cdots + t_i = \frac{\deg gcd(a,N^i)}{\deg N} + + where `\deg N` is the degree of `N` considered as an + element in the center. + + This notion has an important mathematic interest because + it corresponds to the Jordan type of the `N`-typical part + of the associated Galois representation. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: Z = S.center(); x3 = Z.gen() + + sage: a = x^4 + x^3 + (4*t^2 + 4)*x^2 + (t^2 + 2)*x + 4*t^2 + sage: N = x3^2 + x3 + 1 + sage: a.type(N) + [1] + sage: N = x3 + 1 + sage: a.type(N) + [2] + + sage: a = x^3 + (3*t^2 + 1)*x^2 + (3*t^2 + t + 1)*x + t + 1 + sage: N = x3 + 1 + sage: a.type(N) + [2, 1] + + If `N` does not divide the reduced map of `a`, the type + is empty:: + + sage: N = x3 + 2 + sage: a.type(N) + [] + + If `a = N`, the type is just `[r]` where `r` is the order + of the twist map ``Frob``:: + + sage: N = x3^2 + x3 + 1 + sage: S(N).type(N) + [3] + + `N` must be irreducible:: + + sage: N = (x3 + 1) * (x3 + 2) + sage: a.type(N) + Traceback (most recent call last): + ... + ValueError: N is not irreducible + """ + try: + return self._types[N] + except (KeyError, TypeError): + if not N.is_irreducible(): + raise ValueError("N is not irreducible") + skew_ring = self._parent + if self._norm_factor is None: + m = -1 + else: + i = [ n for n,_ in self._norm_factor ].index(N) + m = self._norm_factor[i][1] + NS = skew_ring(N) + type = [ ] + degN = N.degree() + while True: + d = self.gcd(NS) + deg = d.degree()/degN + if deg == 0: + break + if m >= 0: + if deg == 1: + type += m * [1] + break + m -= deg + self = self // d + type.append(deg) + type = Partition(type) + if self._types is None: + self._types = { N: type } + else: + self._types[N] = type + return type + + + # Finding divisors + # ---------------- + + cdef SkewPolynomial_finite_field_dense _rdivisor_c(P, CenterSkewPolynomial_generic_dense N): + """ + cython procedure computing an irreducible monic right divisor + of `P` whose reduced norm is `N` + + .. WARNING:: + + `N` needs to be an irreducible factor of the + reduced norm of `P`. This function does not check + this (and his behaviour is not defined if the + require property doesn't hold). + """ + cdef skew_ring = P._parent + cdef Py_ssize_t d = N.degree() + cdef Py_ssize_t e = P.degree()/d + cdef SkewPolynomial_finite_field_dense D + if e == 1: + D = P._new_c(list(P.__coeffs),skew_ring) + D._inplace_rmonic() + return D + + E = N.parent().base_ring().extension(N,name='xr') + PE = PolynomialRing(E,name='T') + cdef Integer exp + if skew_ring.characteristic() != 2: + exp = Integer((E.cardinality()-1)/2) + cdef SkewPolynomial_finite_field_dense NS = skew_ring(N) + cdef SkewPolynomial_finite_field_dense Q = (NS // P) + cdef SkewPolynomial_finite_field_dense R, X + cdef Matrix_dense M = MatrixSpace(E,e,e)(0) + cdef Matrix_dense V = MatrixSpace(E,e,1)(0) + cdef Matrix_dense W + cdef Py_ssize_t i, j, t, r = skew_ring._order + cdef Polynomial dd, xx, yy, zz + + while 1: + R = skew_ring.random_element((e*r-1,e*r-1)) + R._inplace_lmul(Q) + X = Q._new_c(Q.__coeffs[:],Q._parent) + for j from 0 <= j < e: + for i from 0 <= i < e: + M.set_unsafe(i, j, E([skew_ring._retraction(X[t*r+i]) for t in range(d)])) + X._inplace_lmul(R) + X._inplace_rrem(NS) + for i from 0 <= i < e: + V.set_unsafe(i, 0, E([skew_ring._retraction(X[t*r+i]) for t in range(d)])) + W = M._solve_right_nonsingular_square(V) + if M*W != V: + skew_ring._new_retraction_map() + continue + xx = PE(W.list()+[E(-1)]) + if skew_ring.characteristic() == 2: + yy = PE.gen() + zz = PE.gen() + for i from 1 <= i < d: + zz = (zz*zz) % xx + yy += zz + dd = xx.gcd(yy) + if dd.degree() != 1: continue + else: + yy = PE.gen().__pow__(exp,xx) - 1 + dd = xx.gcd(yy) + if dd.degree() != 1: + yy += 2 + dd = xx.gcd(yy) + if dd.degree() != 1: continue + D = P._rgcd(R + skew_ring.center()((dd[0]/dd[1]).list())) + if D.degree() == 0: + continue + D._inplace_rmonic() + D._init_cache() + return D + + + def irreducible_divisor(self,side=Right,distribution=None): + """ + INPUT: + + - ``side`` -- ``Left`` or ``Right`` (default: Right) + + - ``distribution`` -- None (default) or ``uniform`` + + - None: no particular specification + + - ``uniform``: the returned irreducible divisor is + uniformly distributed + + .. NOTE:: + + ``uniform`` is a little bit slower. + + OUTPUT: + + - an irreducible monic ``side`` divisor of ``self`` + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^6 + 3*t*x^5 + (3*t + 1)*x^3 + (4*t^2 + 3*t + 4)*x^2 + (t^2 + 2)*x + 4*t^2 + 3*t + 3 + + sage: dr = a.irreducible_divisor(); dr # random + x^3 + (2*t^2 + t + 4)*x^2 + (4*t + 1)*x + 4*t^2 + t + 1 + sage: a.is_divisible_by(dr) + True + + sage: dl = a.irreducible_divisor(side=Left); dl # random + x^3 + (2*t^2 + t + 1)*x^2 + (4*t^2 + 3*t + 3)*x + 4*t^2 + 2*t + 1 + sage: a.is_divisible_by(dl,side=Left) + True + + Right divisors are cached. Hence, if we ask again for a + right divisor, we will get the same answer:: + + sage: a.irreducible_divisor() # random + x^3 + (2*t^2 + t + 4)*x^2 + (4*t + 1)*x + 4*t^2 + t + 1 + + However the algorithm is probabilistic. Hence, if we first + reinitialiaze `a`, we may get a different answer:: + + sage: a = x^6 + 3*t*x^5 + (3*t + 1)*x^3 + (4*t^2 + 3*t + 4)*x^2 + (t^2 + 2)*x + 4*t^2 + 3*t + 3 + sage: a.irreducible_divisor() # random + x^3 + (t^2 + 3*t + 4)*x^2 + (t + 2)*x + 4*t^2 + t + 1 + + We can also generate uniformly distributed irreducible monic + divisors as follows:: + + sage: a.irreducible_divisor(distribution="uniform") # random + x^3 + (4*t + 2)*x^2 + (2*t^2 + 2*t + 2)*x + 2*t^2 + 2 + sage: a.irreducible_divisor(distribution="uniform") # random + x^3 + (t^2 + 2)*x^2 + (3*t^2 + 1)*x + 4*t^2 + 2*t + sage: a.irreducible_divisor(distribution="uniform") # random + x^3 + x^2 + (4*t^2 + 2*t + 4)*x + t^2 + 3 + + By convention, the zero skew polynomial has no irreducible + divisor: + + sage: S(0).irreducible_divisor() + Traceback (most recent call last): + ... + ValueError: 0 has no irreducible divisor + """ + if self.is_zero(): + raise ValueError("0 has no irreducible divisor") + if not (distribution is None or distribution == "uniform"): + raise ValueError("distribution must be None or 'uniform'") + if distribution == "uniform": + skew_ring = self._parent + center = skew_ring.center() + cardcenter = center.base_ring().cardinality() + gencenter = center.gen() + count = [ ] + total = 0 + F = self.reduced_norm_factor() + for n,_ in F: + if n == gencenter: + total += 1 + else: + degn = n.degree() + P = self.gcd(skew_ring(n)) + m = P.degree()/degn + cardL = cardcenter**degn + total += (cardL**m - 1)/(cardL - 1) + count.append(total) + if total == 0: + raise ValueError("No irreducible divisor having given reduced norm") + random = ZZ.random_element(total) + for i in range(len(F)): + if random < count[i]: + N = F[i][0] + break + else: + N = self.reduced_norm_factor()[0][0] + return self.irreducible_divisor_with_norm(N,side=side,distribution=distribution) + + + def irreducible_divisor_with_norm(self,N,side=Right,distribution=None): # Ajouter side + """ + INPUT: + + - ``N`` -- an irreducible polynomial in the center + of the underlying skew polynomial ring + + - ``side`` -- ``Left`` or ``Right`` (default: Right) + + - ``distribution`` -- None (default) or ``uniform`` + + - None: no particular specification + + - ``uniform``: the returned irreducible divisor is + uniformly distributed + + .. NOTE:: + + ``uniform`` is a little bit slower. + + OUTPUT: + + - an irreducible monic ``side`` divisor of ``self`` + whose reduced norm is similar to `N` (i.e. `N` times + a unit). + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: Z = S.center(); x3 = Z.gen() + sage: a = x^6 + 3*x^3 + 2 + + sage: d1 = a.irreducible_divisor_with_norm(x3+1); d1 # random + x + t^2 + 3*t + sage: a.is_divisible_by(d1) + True + sage: d1.reduced_norm() + (x^3) + 1 + + sage: d2 = a.irreducible_divisor_with_norm(x3+2); d2 # random + x + 2*t^2 + 3*t + 2 + sage: a.is_divisible_by(d2) + True + sage: d2.reduced_norm() + (x^3) + 2 + + sage: d3 = a.irreducible_divisor_with_norm(x3+3) + Traceback (most recent call last): + ... + ValueError: No irreducible divisor having given reduced norm + + We can also generate uniformly distributed irreducible monic + divisors as follows:: + + sage: a.irreducible_divisor_with_norm(x3+1,distribution="uniform") # random + x + 3*t^2 + 3*t + 1 + sage: a.irreducible_divisor_with_norm(x3+1,distribution="uniform") # random + x + 1 + sage: a.irreducible_divisor_with_norm(x3+1,distribution="uniform") # random + x + 2*t^2 + 4*t + """ + cdef SkewPolynomial_finite_field_dense cP1 + cdef CenterSkewPolynomial_generic_dense cN + if self.is_zero(): + raise "No irreducible divisor having given reduced norm" + skew_ring = self._parent + center = skew_ring.center() + try: + N = center(N) + except TypeError: + raise TypeError("N must be a polynomial in the center") + cardcenter = center.base_ring().cardinality() + gencenter = center.gen() + + if N == gencenter: + if self[0] == 0: + return skew_ring.gen() + else: + raise ValueError("No irreducible divisor having given reduced norm") + + D = None + try: + D = self._rdivisors[N] + except (KeyError, TypeError): + if N.is_irreducible(): + cP1 = self._rgcd(self._parent(N)) + cN = N + if cP1.degree() > 0: + D = cP1._rdivisor_c(cN) + if self._rdivisors is None: + self._rdivisors = { N: D } + else: + self._rdivisors[N] = D + distribution = "" + if D is None: + raise ValueError("No irreducible divisor having given reduced norm") + + NS = self._parent(N) + degN = N.degree() + if side is Right: + if distribution == "uniform": + P1 = self._rgcd(NS) + if P1.degree() != degN: + Q1 = NS // P1 + deg = P1.degree()-1 + while True: + R = Q1*skew_ring.random_element((deg,deg)) + if P1.gcd(R) == 1: + break + D = P1.gcd(D*R) + return D + else: + deg = NS.degree()-1 + P1 = self.lgcd(NS) + while True: + if distribution == "uniform": + while True: + R = skew_ring.random_element((deg,deg)) + if NS.gcd(R) == 1: + break + D = NS.gcd(D*R) + Dp = NS // D + LDp = P1.gcd(Dp) + LD = P1 // LDp + if LD.degree() == degN: + return LD + distribution = "uniform" + + + def irreducible_divisors(self,side=Right): + """ + INPUT: + + - ``side`` -- ``Left`` or ``Right`` (default: Right) + + OUTPUT: + + An iterator over all irreducible monic ``side`` divisors + of this skew polynomial + + EXAMPLES: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^4 + 2*t*x^3 + 3*t^2*x^2 + (t^2 + t + 1)*x + 4*t + 3 + sage: iter = a.irreducible_divisors(); iter + + sage: iter.next() # random + x + 2*t^2 + 4*t + 4 + sage: iter.next() # random + x + 3*t^2 + 4*t + 1 + + We can use this function to build the list of all monic + irreducible divisors of `a`:: + + sage: rightdiv = [ d for d in a.irreducible_divisors() ] + sage: leftdiv = [ d for d in a.irreducible_divisors(side=Left) ] + + We do some checks:: + + sage: len(rightdiv) == a.count_irreducible_divisors() + True + sage: len(rightdiv) == len(Set(rightdiv)) # check no duplicates + True + sage: for d in rightdiv: + ... if not a.is_divisible_by(d): + ... print "Found %s which is not a right divisor" % d + ... elif not d.is_irreducible(): + ... print "Found %s which is not irreducible" % d + + sage: len(leftdiv) == a.count_irreducible_divisors(side=Left) + True + sage: len(leftdiv) == len(Set(leftdiv)) # check no duplicates + True + sage: for d in leftdiv: + ... if not a.is_divisible_by(d,side=Left): + ... print "Found %s which is not a left divisor" % d + ... elif not d.is_irreducible(): + ... print "Found %s which is not irreducible" % d + + Note that left divisors and right divisors differ:: + + sage: Set(rightdiv) == Set(leftdiv) + False + + Note that the algorithm is probabilistic. As a consequence, if we + build again the list of right monic irreducible divisors of `a`, we + may get a different ordering:: + + sage: rightdiv2 = [ d for d in a.irreducible_divisors() ] + sage: rightdiv == rightdiv2 + False + sage: Set(rightdiv) == Set(rightdiv2) + True + """ + return self._irreducible_divisors(side) + + + def _irreducible_divisors(self,side): # prendre side en compte + """ + Return an iterator over all irreducible monic + divisors of this skew polynomial. + + Do not use this function. Use instead + ``self.irreducible_divisors()``. + """ + if self.is_zero(): + return + skew_ring = self._parent + center = skew_ring.center() + kfixed = center.base_ring() + F = self.reduced_norm_factor() + oppside = side.opposite() + for N,_ in F: + if N == center.gen(): + yield skew_ring.gen() + continue + degN = N.degree() + NS = skew_ring(N) + P = self.gcd(NS,side=side) + m = P.degree()/degN + if m == 1: + yield P + continue + degrandom = P.degree() - 1 + Q,_ = NS.quo_rem(P,side=side) + P1 = self.irreducible_divisor_with_norm(N,side=side) + Q1,_ = P.quo_rem(P1,side=side) + while True: + R = skew_ring.random_element((degrandom,degrandom)) + if side is Right: + g = (R*Q).rem(P,side=Left) + else: + g = (Q*R).rem(P) + if g.gcd(P,side=oppside) != 1: continue + L = Q1 + V = L + for i in range(1,m): + if side is Right: + L = (g*L).gcd(P,side=Left) + else: + L = (L*g).gcd(P) + V = V.gcd(L,side=oppside) + if V == 1: break + rng = xmrange_iter([kfixed]*degN,center) + for i in range(m): + for pol in xmrange_iter([rng]*i): + f = skew_ring(1) + for j in range(i): + coeff = pol.pop() + f = (g*f+coeff).rem(P,side=oppside) + if side is Right: + d = (f*Q1).gcd(P,side=Left) + else: + d = (Q1*f).gcd(P) + d,_ = P.quo_rem(d,side=oppside) + yield d + + + def count_irreducible_divisors(self,side=Right): + """ + INPUT: + + - ``side`` -- ``Left`` or ``Right`` (default: Right) + + OUTPUT: + + The number of irreducible monic ``side`` divisors of + this skew polynomial. + + .. NOTE:: + + Actually, one can prove that there are always as + many left irreducible monic divisors as right + irreducible monic divisors. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + + We illustrate that a skew polynomial may have a number of irreducible + divisors greater than its degree. + + sage: a = x^4 + (4*t + 3)*x^3 + t^2*x^2 + (4*t^2 + 3*t)*x + 3*t + sage: a.count_irreducible_divisors() + 12 + sage: a.count_irreducible_divisors(side=Left) + 12 + + We illustrate that an irreducible polynomial in the center have + in general a lot of irreducible divisors in the skew polynomial + ring:: + + sage: Z = S.center(); x3 = Z.gen() + sage: N = x3^5 + 4*x3^4 + 4*x3^2 + 4*x3 + 3; N + (x^3)^5 + 4*(x^3)^4 + 4*(x^3)^2 + 4*(x^3) + 3 + sage: N.is_irreducible() + True + sage: S(N).count_irreducible_divisors() + 9768751 + """ + if self.is_zero(): + return 0 + skew_ring = self.parent() + cardcenter = skew_ring.center().base_ring().cardinality() + gencenter = skew_ring.center().gen() + F = self.reduced_norm_factor() + val = self.valuation() + self >>= val + count = 0 + if val > 0: + count = 1 + for N,_ in F: + if N == gencenter: + continue + degN = N.degree() + P = self.gcd(skew_ring(N), side=side) + m = P.degree()/degN + cardL = cardcenter**degN + count += (cardL**m - 1)/(cardL - 1) + return count + + + # Finding factorizations + # ---------------------- + + cdef _factor_c(self): + """ + Compute a factorization of ``self`` + """ + cdef skew_ring = self._parent + cdef Py_ssize_t degQ, degrandom, m, mP, i + cdef CenterSkewPolynomial_generic_dense N + cdef SkewPolynomial_finite_field_dense poly = self.rmonic() + cdef val = poly._val_inplace_unit() + if val == -1: + return Factorization([], sort=False, unit=skew_ring.zero_element()) + cdef list factors = [ (skew_ring.gen(), val) ] + cdef SkewPolynomial_finite_field_dense P, Q, P1, NS, g, right, Pn + cdef SkewPolynomial_finite_field_dense right2 = skew_ring(1) << val + cdef RingElement unit = self.leading_coefficient() + cdef Polynomial gencenter = skew_ring.center().gen() + cdef Py_ssize_t p = skew_ring.characteristic() + cdef F = self.reduced_norm_factor() + + for N,m in F: + if N == gencenter: + continue + degN = N.degree() + if poly.degree() == degN: + factors.append((poly,1)) + break + NS = skew_ring(N) + P1 = None + while 1: + P = poly._rgcd(NS) + P._inplace_rmonic() + mP = P.degree() / degN + if mP == 0: break + if mP == 1: + factors.append((P,1)) + poly._inplace_rfloordiv(P) + for i from 1 <= i < m: + if poly.degree() == degN: + factors.append((poly,1)) + break + P = poly._rgcd(NS) + P._inplace_rmonic() + factors.append((P,1)) + poly._inplace_rfloordiv(P) + break + if P1 is None: + P1 = P._rdivisor_c(N) + Q = NS._new_c(NS.__coeffs[:], NS._parent) + Q._inplace_rfloordiv(P) + Q._inplace_lmul(P1) + factors.append((P1,1)) + right = P1._new_c(P1.__coeffs[:], P1._parent) + m -= (mP-1) + degrandom = P.degree() + while mP > 2: + while 1: + g = skew_ring.random_element((degrandom,degrandom)) + g._inplace_lmul(Q) + g._inplace_rgcd(P) + Pn = right._coeff_llcm(g) + if len(Pn.__coeffs)-1 == degN: break + Pn._inplace_rmonic() + factors.append((Pn,1)) + right._inplace_lmul(Pn) + degrandom -= degN + mP -= 1 + poly._inplace_rfloordiv(right) + P1,_ = P.rquo_rem(right) + factors.reverse() + return Factorization(factors, sort=False, unit=unit) + + + cdef _factor_uniform_c(self): + """ + Compute a uniformly distrbuted factorization of ``self`` + """ + skew_ring = self._parent + cdef Integer cardE, cardcenter = skew_ring.center().base_ring().cardinality() + cdef CenterSkewPolynomial_generic_dense gencenter = skew_ring.center().gen() + cdef SkewPolynomial_finite_field_dense gen = skew_ring.gen() + + cdef list factorsN = [ ] + cdef dict dict_divisor = { } + cdef dict dict_type = { } + cdef dict dict_right = { } + cdef CenterSkewPolynomial_generic_dense N + cdef Py_ssize_t m + cdef list type + + for N,m in self.reduced_norm_factor(): + factorsN += m * [N] + if N == gencenter: continue + type = list(self.type(N)) + dict_type[N] = type + if type[0] > 1: + dict_divisor[N] = self.irreducible_divisor_with_norm(N) + dict_right[N] = skew_ring(1) + cdef list indices = list(Permutations(len(factorsN)).random_element()) + + cdef RingElement unit = self.leading_coefficient() + cdef SkewPolynomial_finite_field_dense left = self._new_c(self.__coeffs[:],skew_ring) + left._inplace_rmonic() + cdef SkewPolynomial_finite_field_dense right = skew_ring(1) + cdef SkewPolynomial_finite_field_dense L, R + cdef SkewPolynomial_finite_field_dense NS, P, Q, D, D1, D2, d + cdef list factors = [ ] + cdef list maxtype + cdef Py_ssize_t i, j, degN, deg + cdef count, maxcount + + for i in indices: + N = factorsN[i-1] + if N == gencenter: + D1 = gen + else: + type = dict_type[N] + NS = skew_ring(N) + P = left.gcd(NS) + if type[0] == 1: + D1 = P + else: + R = right._new_c(right.__coeffs[:],skew_ring) + R._inplace_rfloordiv(dict_right[N]) + D = R._coeff_llcm(dict_divisor[N]) + maxtype = list(type) + maxtype[-1] -= 1 + degN = N.degree() + cardE = cardcenter ** degN + maxcount = q_jordan(Partition(maxtype),cardE) + Q = NS // P + deg = P.degree()-1 + while 1: + while 1: + R = skew_ring.random_element((deg,deg)) + R._inplace_lmul(Q) + if P._rgcd(R).degree() == 0: + break + D1 = P._rgcd(D*R) + D1._inplace_rmonic() + + L = left._new_c(list(left.__coeffs),skew_ring) + L._inplace_rfloordiv(D1) + degN = N.degree() + for j in range(len(type)): + if type[j] == 1: + newtype = type[:-1] + break + d = L._rgcd(NS) + d._inplace_rmonic() + deg = d.degree() / degN + if deg < type[j]: + newtype = type[:] + newtype[j] = deg + break + L._inplace_rfloordiv(d) + count = q_jordan(Partition(newtype),cardE) + if ZZ.random_element(maxcount) < count: + break + dict_type[N] = newtype + + D2 = D._new_c(list(D.__coeffs),skew_ring) + D2._inplace_rmonic() + while D2 == D1: + while 1: + R = skew_ring.random_element((deg,deg)) + R._inplace_lmul(Q) + if P._rgcd(R).degree() == 0: + break + D2 = P._rgcd(D*R) + D2._inplace_rmonic() + dict_divisor[N] = D1._coeff_llcm(D2) + factors.append((D1,1)) + left._inplace_rfloordiv(D1) + right._inplace_lmul(D1) + dict_right[N] = right._new_c(list(right.__coeffs),skew_ring) + + factors.reverse() + return Factorization(factors,sort=False,unit=unit) + + + def factor(self,distribution=None): + """ + Return a factorization of this skew polynomial. + + INPUT: + + - ``distribution`` -- None (default) or ``uniform`` + + - None: no particular specification + + - ``uniform``: the returned factorization is uniformly + distributed among all possible factorizations + + .. NOTE:: + + ``uniform`` is a little bit slower. + + OUTPUT: + + - ``Factorization`` -- a factorization of self as a + product of a unit and a product of irreducible monic + factors + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^3 + (t^2 + 4*t + 2)*x^2 + (3*t + 3)*x + t^2 + 1 + sage: F = a.factor(); F # random + (x + t^2 + 4) * (x + t + 3) * (x + t) + sage: F.value() == a + True + + The result of the factorization is cached. Hence, if we try + again to factor `a`, we will get the same answer:: + + sage: a.factor() # random + (x + t^2 + 4) * (x + t + 3) * (x + t) + + However, the algorithm is probabilistic. Hence if we first + reinitialiaze `a`, we may get a different answer:: + + sage: a = x^3 + (t^2 + 4*t + 2)*x^2 + (3*t + 3)*x + t^2 + 1 + sage: F = a.factor(); F # random + (x + t^2 + t + 2) * (x + 2*t^2 + t + 4) * (x + t) + sage: F.value() == a + True + + There is no guarantee on the distribution of the factorizations + we get that way. (For this particular `a` for example, we get the + uniform distribution on the subset of all factorizations ending + by the factor `x + t`.) + + If we rather want uniform distribution among all factorizations, + we need to specify it as follows:: + + sage: a.factor(distribution="uniform") # random + (x + t^2 + 4) * (x + t) * (x + t + 3) + sage: a.factor(distribution="uniform") # random + (x + 2*t^2) * (x + t^2 + t + 1) * (x + t^2 + t + 2) + sage: a.factor(distribution="uniform") # random + (x + 2*t^2 + 3*t) * (x + 4*t + 2) * (x + 2*t + 2) + + By convention, the zero skew polynomial has no factorization: + + sage: S(0).factor() + Traceback (most recent call last): + ... + ValueError: factorization of 0 not defined + """ + if not (distribution is None or distribution == "uniform"): + raise ValueError("distribution must be None or 'uniform'") + if self.is_zero(): + raise ValueError("factorization of 0 not defined") + sig_on() + if distribution is None: + if self._factorization is None: + self._factorization = self._factor_c() + F = self._factorization + else: + F = self._factor_uniform_c() + if self._factorization is None: + self._factorization = F + sig_off() + return F + + + def count_factorizations(self): + """ + Return the number of factorizations (as a product of a + unit and a product of irreducible monic factors) of this + skew polynomial. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^4 + (4*t + 3)*x^3 + t^2*x^2 + (4*t^2 + 3*t)*x + 3*t + sage: a.count_factorizations() + 216 + + We illustrate that an irreducible polynomial in the center have + in general a lot of distinct factorizations in the skew polynomial + ring:: + + sage: Z = S.center(); x3 = Z.gen() + sage: N = x3^5 + 4*x3^4 + 4*x3^2 + 4*x3 + 3; N + (x^3)^5 + 4*(x^3)^4 + 4*(x^3)^2 + 4*(x^3) + 3 + sage: N.is_irreducible() + True + sage: S(N).count_factorizations() + 30537115626 + """ + if self.is_zero(): + raise ValueError("factorization of 0 not defined") + cardcenter = self._parent.center().base_ring().cardinality() + gencenter = self._parent.center().gen() + F = self.reduced_norm_factor() + summ = 0 + count = 1 + for N,m in F: + summ += m + if m == 1: continue + if N != gencenter: + count *= q_jordan(self.type(N),cardcenter**N.degree()) + count /= factorial(m) + return count * factorial(summ) + + def count_factorisations(self): + """ + Return the number of factorisations (as a product of a + unit and a product of irreducible monic factors) of this + skew polynomial. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^4 + (4*t + 3)*x^3 + t^2*x^2 + (4*t^2 + 3*t)*x + 3*t + sage: a.count_factorisations() + 216 + + We illustrate that an irreducible polynomial in the center have + in general a lot of distinct factorisations in the skew polynomial + ring:: + + sage: Z = S.center(); x3 = Z.gen() + sage: N = x3^5 + 4*x3^4 + 4*x3^2 + 4*x3 + 3; N + (x^3)^5 + 4*(x^3)^4 + 4*(x^3)^2 + 4*(x^3) + 3 + sage: N.is_irreducible() + True + sage: S(N).count_factorisations() + 30537115626 + """ + return self.count_factorizations() + + + # Not optimized (many calls to reduced_norm, reduced_norm_factor,_rdivisor_c, which are slow) + def factorizations(self): + """ + Return an iterator over all factorizations (as a product + of a unit and a product of irreducible monic factors) of + this skew polynomial. + + .. NOTE:: + + The algorithm is probabilistic. As a consequence, if + we execute two times with the same input we can get + the list of all factorizations in two differents orders. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^3 + (t^2 + 1)*x^2 + (2*t + 3)*x + t^2 + t + 2 + sage: iter = a.factorizations(); iter + + sage: iter.next() # random + (x + 3*t^2 + 4*t) * (x + 2*t^2) * (x + 4*t^2 + 4*t + 2) + sage: iter.next() # random + (x + 3*t^2 + 4*t) * (x + 3*t^2 + 2*t + 2) * (x + 4*t^2 + t + 2) + + We can use this function to build the list of factorizations + of `a`:: + + sage: factorizations = [ F for F in a.factorizations() ] + + We do some checks:: + + sage: len(factorizations) == a.count_factorizations() + True + sage: len(factorizations) == len(Set(factorizations)) # check no duplicates + True + sage: for F in factorizations: + ... if F.value() != a: + ... print "Found %s which is not a correct factorization" % d + ... continue + ... for d,_ in F: + ... if not d.is_irreducible(): + ... print "Found %s which is not a correct factorization" % d + """ + if self.is_zero(): + raise ValueError("factorization of 0 not defined") + unit = self.leading_coefficient() + poly = self.rmonic() + for factors in self._factorizations_rec(): + yield Factorization(factors,sort=False,unit=unit) + def _factorizations_rec(self): + if self.is_irreducible(): + yield [ (self,1) ] + else: + for div in self._irreducible_divisors(Right): + poly = self // div + # Here, we should update poly._norm, poly._norm_factor, poly._rdivisors + for factors in poly._factorizations_rec(): + factors.append((div,1)) + yield factors + + + def factorisations(self): + """ + Return an iterator over all factorisations (as a product + of a unit and a product of irreducible monic factors) of + this skew polynomial. + + .. NOTE:: + + The algorithm is probabilistic. As a consequence, if + we execute two times with the same input we can get + the list of all factorizations in two differents orders. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^3 + (t^2 + 1)*x^2 + (2*t + 3)*x + t^2 + t + 2 + sage: iter = a.factorisations(); iter + + sage: iter.next() # random + (x + 3*t^2 + 4*t) * (x + 2*t^2) * (x + 4*t^2 + 4*t + 2) + sage: iter.next() # random + (x + 3*t^2 + 4*t) * (x + 3*t^2 + 2*t + 2) * (x + 4*t^2 + t + 2) + + We can use this function to build the list of factorizations + of `a`:: + + sage: factorisations = [ F for F in a.factorisations() ] + + We do some checks:: + + sage: len(factorisations) == a.count_factorisations() + True + sage: len(factorisations) == len(Set(factorisations)) # check no duplicates + True + sage: for F in factorisations: + ... if F.value() != a: + ... print "Found %s which is not a correct factorization" % d + ... continue + ... for d,_ in F: + ... if not d.is_irreducible(): + ... print "Found %s which is not a correct factorization" % d + """ + return self.factorizations() cpdef RingElement _mul_(self, RingElement right): """ diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 4172801c961..d60c26cf13a 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -919,6 +919,57 @@ def random_element(self, degree=2, monic=False, *args, **kwds): return self ([ R.random_element (*args, **kwds) for _ in range (degree) ] + [ R.one() ]) else: return self ([ R.random_element (*args, **kwds) for _ in range (degree+1) ]) + + def random_irreducible(self, degree=2, monic=True, *args, **kwds): + r""" + Return a random irreducible skew polynomial. + + .. WARNING:: + + Elements of this skew polynomial ring need to have a method + is_irreducible(). Currently, this method is implemented only + when the base ring is a finite field. + + INPUT: + + - ``degree`` - Integer with degree (default: 2) + or a tuple of integers with minimum and maximum degrees + + - ``monic`` - if True, returns a monic skew polynomial + (default: True) + + - ``*args, **kwds`` - Passed on to the ``random_element`` method for + the base ring + + OUTPUT: + + - A random skew polynomial + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: A = S.random_irreducible(); A + x^2 + (4*t^2 + 3*t + 4)*x + 4*t^2 + t + sage: A.is_irreducible() + True + sage: B = S.random_irreducible(degree=3,monic=False); B # random + (4*t + 1)*x^3 + (t^2 + 3*t + 3)*x^2 + (3*t^2 + 2*t + 2)*x + 3*t^2 + 3*t + 1 + sage: B.is_irreducible() + True + """ + R = self.base_ring() + if isinstance(degree, (list, tuple)): + if len(degree) != 2: + raise ValueError("degree argument must be an integer or a tuple of 2 integers (min_degree, max_degree)") + if degree[0] > degree[1]: + raise ValueError("minimum degree must be less or equal than maximum degree") + degree = randint(*degree) + while True: + irred = self.random_element((degree,degree), monic=monic) + if irred.is_irreducible(): + return irred def is_commutative(self): """ @@ -1209,3 +1260,36 @@ def twist_map(self, n=1): Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 """ return self._maps[n%self._order] + + def _new_retraction_map(self,alea=None): + """ + This is an internal function used in factorization. + """ + k = self.base_ring() + base = k.base_ring() + (kfixed,embed) = self._maps[1].fixed_points() + section = embed.section() + if not kfixed.has_coerce_map_from(base): + raise NotImplementedError("No coercion map from %s to %s" % (base,kfixed)) + if alea is None: + alea = k.random_element() + self._alea_retraction = alea + trace = [ ] + elt = alea + for _ in range(k.degree()): + x = elt + tr = elt + for _ in range(1,self._order): + x = self._map(x) + tr += x + elt *= k.gen() + trace.append(section(tr)) + self._matrix_retraction = MatrixSpace(kfixed,1,k.degree())(trace) + + def _retraction(self,x,newmap=False,alea=None): # Better to return the retraction map but more difficult + """ + This is an internal function used in factorization. + """ + if newmap or alea is not None or self._matrix_retraction is None: + self._new_retraction_map() + return (self._matrix_retraction*self.base_ring()(x)._vector_())[0] \ No newline at end of file From 80038ea1bd0253a48d22e30954b918f46eeed924 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 6 Apr 2020 14:17:42 +0200 Subject: [PATCH 02/12] remove duplicates and Karatsuba multiplication --- .../skew_polynomial_finite_field.pyx | 1066 +---------------- .../rings/polynomial/skew_polynomial_ring.py | 76 +- 2 files changed, 14 insertions(+), 1128 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index a5054fd39e9..9e44d7dc3f0 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -34,97 +34,7 @@ from sage.structure.element cimport RingElement from polynomial_ring_constructor import PolynomialRing from skew_polynomial_element cimport SkewPolynomial_generic_dense -cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): - """ - A generic, dense, univariate skew polynomial over a finite field. - - DEFINITION: - - Let `R` be a commutative ring and let `\sigma` be the Frobenius - Endomorphism over `R` defined by `\sigma(r) = r^p` where `r` is - an element of `R` and `p` is the prime characteristic of `R`. - - Then, a formal skew polynomial is given by the equation: - `F(X) = a_{n}X^{n} + ... + a_0` - where the coefficients `a_{i}` belong to `R` and `X` is a formal variable. - - Addition between two skew polynomials is defined by the usual addition - operation and the modified multiplication is defined by the rule - `X a = \sigma(a) X` for all `a` in `R`. - - Skew polynomials are thus non-commutative and the degree of a product - is equal to the sum of the degree of its factors. The ring of such skew - polynomials over `R` equipped with `\sigma` is denoted by `S = R[X, \sigma]` - and it is an additive group. - - .. NOTE:: - - #. `S` is a left (resp. right) euclidean noncommutative ring - - #. in particular, every left (resp. right) ideal is principal - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob]; S - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 - - .. SEE ALSO:: - - :mod:`sage.rings.polynomial.skew_polynomial_element.SkewPolynomial_generic_dense` - :mod:`sage.rings.polynomial.skew_polynomial_element.SkewPolynomial` - :mod:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_field` - - .. TODO:: - - Try to replace as possible ``finite field`` by ``field - endowed with a finite order twist morphism``. It may cause - new phenomena due to the non trivality of the Brauer group. - """ - def __init__(self, parent, x=None, int check=1, is_gen=False, int construct=0, **kwds): - """ - This method constructs a generic dense skew polynomial over a finite field. - - INPUT:: - - - ``parent`` -- parent of ``self`` - - - ``x`` -- list of coefficients from which ``self`` can be constructed - - - ``check`` -- flag variable to normalize the polynomial - - - ``is_gen`` -- boolean (default: ``False``) - - - ``construct`` -- boolean (default: ``False``) - - TESTS:: - - sage: R. = GF(5^3) - sage: Frob = R.frobenius_endomorphism() - sage: S. = R['x',Frob]; S - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 - - We create a skew polynomial from a list:: - - sage: S([t,1]) - x + t - - from another skew polynomial:: - - sage: S(x^2 + t) - x^2 + t - - from a constant:: - - sage: x = S(t^2 + 1); x - t^2 + 1 - sage: x.parent() is S - True - - """ - SkewPolynomial_generic_dense.__init__ (self, parent, x, check, is_gen, construct, **kwds) - +cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): cdef SkewPolynomial_finite_field_dense _rgcd(self, SkewPolynomial_finite_field_dense other): """ Fast creation of the right gcd of ``self`` and ``other``. @@ -336,131 +246,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): val += 1 return val - cdef Matrix_dense _matmul_c(self): - r""" - Return the matrix of the multiplication by ``self`` on - `K[X,\sigma]` considered as a free module over `K[X^r]` - (here `r` is the order of `\sigma`). - - .. WARNING:: - - Does not work if self is not monic. - """ - cdef Py_ssize_t i, j, deb, k, r = self.parent()._order - cdef Py_ssize_t d = self.degree () - cdef Ring base_ring = self.parent().base_ring() - cdef RingElement minusone = base_ring(-1) - cdef RingElement zero = base_ring(0) - cdef Polk = PolynomialRing (base_ring, 'xr') - cdef Matrix_dense M = zero_matrix(Polk,r,r) - cdef list l = self.list() - for j from 0 <= j < r: - for i from 0 <= i < r: - if i < j: - pol = [ zero ] - deb = i-j+r - else: - pol = [ ] - deb = i-j - for k from deb <= k <= d by r: - pol.append(l[k]) - M.set_unsafe(i,j,Polk(pol)) - - for i from 0 <= i <= d: - l[i] = self._parent.twist_map()(l[i]) - return M - - def reduced_norm(self): - r""" - Return the reduced norm of this skew polynomial. - - .. NOTE:: - - The result is cached. - - ALGORITHM: - - If `r` (= the order of the twist map) is small compared - to `d` (= the degree of this skew polynomial), the reduced - norm is computed as the determinant of the multiplication - by `P` (= this skew polynomial) acting on `K[X,\sigma]` - (= the underlying skew ring) viewed as a free module of - rank `r` over `K[X^r]`. - - Otherwise, the reduced norm is computed as the characteristic - polynomial (considered as a polynomial of the variable `X^r`) - of the left multiplication by `X` on the quotient - `K[X,\sigma] / K[X,\sigma]*P` (which is a `K`-vector space - of dimension `d`). - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = S.random_element(degree=3,monic=True); a - x^3 + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 - sage: N = a.reduced_norm(); N - (x^3)^3 + 4*(x^3)^2 + 4 - - Note that the parent of `N` is the center of the `S` - (and not `S` itself):: - - sage: N.parent() - Center of Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5: - Univariate Polynomial Ring in (x^3) over Finite Field of size 5 - sage: N.parent() == S.center() - True - - In any case, coercion works fine:: - - sage: S(N) - x^9 + 4*x^6 + 4 - sage: N + a - x^9 + 4*x^6 + x^3 + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 1 - - We check that `N` is a multiple of `a`:: - - sage: S(N).is_divisible_by(a) - True - sage: S(N).is_divisible_by(a,side=Left) - True - - .. NOTE:: - - We really need to coerce first `N` into `S`. Otherwise an - error occurs:: - - sage: N.is_divisible_by(a) - Traceback (most recent call last): - ... - AttributeError: 'sage.rings.polynomial.skew_polynomial_element.CenterSkewPolynomial_generic_dense' object has no attribute 'is_divisible_by' - - We check that the reduced norm is a multiplicative map:: - - sage: a = S.random_element(degree=5) - sage: b = S.random_element(degree=7) - sage: a.reduced_norm() * b.reduced_norm() == (a*b).reduced_norm() - True - """ - if self._norm is None: - center = self.parent().center() - if self.is_zero(): - self._norm = center(0) - else: - section = center._embed_basering.section() - exp = (self.parent().base_ring().cardinality() - 1) / (center.base_ring().cardinality() - 1) - order = self.parent()._order - lc = section(self.leading_coefficient()**exp) - if order < self.degree(): - M = self._matmul_c() - self._norm = center([ lc*section(x) for x in M.determinant().monic().list() ]) - else: - charpoly = self._matphir_c().characteristic_polynomial() - self._norm = center([ lc*section(x) for x in charpoly.list() ]) - return self._norm - - def reduced_norm_factor(self): """ Return the reduced norm of this polynomial @@ -480,259 +265,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): self._norm_factor = self.reduced_norm().factor() return self._norm_factor - - def is_central(self): - """ - Return True if this skew polynomial lies in the center. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - - sage: x.is_central() - False - sage: (t*x^3).is_central() - False - sage: (x^6 + x^3).is_central() - True - """ - center = self.parent().center() - try: - center(self) - return True - except ValueError: - return False - - - def bound(self): - """ - Return a bound of this skew polynomial (i.e. a multiple - of this skew polynomial lying in the center). - - .. NOTE:: - - Since `b` is central, it divides a skew polynomial - on the left iff it divides it on the right - - ALGORITHM: - - #. Sage first checks whether ``self`` is itself in the - center. It if is, it returns ``self`` - - #. If an optimal bound was previously computed and - cached, Sage returns it - - #. Otherwise, Sage returns the reduced norm of ``self`` - - As a consequence, the output of this function may depend - on previous computations (an example is given below). - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: Z = S.center() - - sage: a = x^2 + (4*t + 2)*x + 4*t^2 + 3 - sage: b = a.bound(); b - (x^3)^2 + (x^3) + 4 - - Note that the parent of `b` is the center of the `S` - (and not `S` itself):: - - sage: b.parent() - Center of Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5: - Univariate Polynomial Ring in (x^3) over Finite Field of size 5 - sage: b.parent() == Z - True - - We check that `b` is divisible by `a`:: - - sage: S(b).is_divisible_by(a) - True - sage: S(b).is_divisible_by(a,side=Left) - True - - Actually, `b` is the reduced norm of `a`:: - - sage: b == a.reduced_norm() - True - - Now, we compute the optimal bound of `a` and see that - it affects the behaviour of ``bound()``:: - - sage: a.optimal_bound() - (x^3) + 3 - sage: a.bound() - (x^3) + 3 - - We finally check that if `a` is a central skew polynomial, - then ``a.bound()`` returns simply `a`:: - - sage: a = S(Z.random_element(degree=4)); a - 2*x^12 + x^9 + 2*x^3 - sage: b = a.bound(); b - 2*(x^3)^4 + (x^3)^3 + 2*(x^3) - sage: a == b - True - """ - center = self.parent().center() - try: - return center(self) - except ValueError: - pass - if not self._optbound is None: - return center(self._optbound) - return self.reduced_norm() - - - def optimal_bound(self): - """ - Return the optimal bound of this skew polynomial (i.e. - the monic multiple of this skew polynomial of minimal - degree lying in the center). - - .. NOTE:: - - The result is cached. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: Z = S.center() - - sage: a = x^2 + (4*t + 2)*x + 4*t^2 + 3 - sage: b = a.optimal_bound(); b - (x^3) + 3 - - Note that the parent of `b` is the center of the `S` - (and not `S` itself):: - - sage: b.parent() - Center of Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5: - Univariate Polynomial Ring in (x^3) over Finite Field of size 5 - sage: b.parent() == Z - True - - We check that `b` is divisible by `a`:: - - sage: S(b).is_divisible_by(a) - True - sage: S(b).is_divisible_by(a,side=Left) - True - """ - center = self.parent().center() - if self._optbound is None: - try: - self._optbound = center(self).monic() - except ValueError: - bound = self._matphir_c().minimal_polynomial() - section = center._embed_basering.section() - self._optbound = [ section(x) for x in bound.list() ] - return center(self._optbound) - - def _mul_karatsuba(self,right,cutoff=None): - """ - Karatsuba multiplication - - INPUT: - - - ``right`` -- an other skew polynomial in the same ring - - - ``cutoff`` -- ``None``, an integer or Infinity (default: None) - - .. WARNING:: - - ``cutoff`` need to be greater than or equal to the order of the - twist map acting on the base ring of the underlying skew polynomial - ring. - - OUTPUT: - - The result of the product self*right (computed by a variant of - Karatsuba`s algorithm) - - .. NOTE:: - - if ``cutoff`` is None, use the default cutoff which is the - maximum between 150 and the order of the twist map. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = S.random_element(5000) - sage: b = S.random_element(5000) - sage: timeit("c = a._mul_karatsuba(b)") # random, long time - 5 loops, best of 3: 659 ms per loop - sage: timeit("c = a._mul_classical(b)") # random, long time - 5 loops, best of 3: 1.9 s per loop - sage: a._mul_karatsuba(b) == a._mul_classical(b) - True - - The operator ``*`` performs Karatsuba multiplication:: - - sage: timeit("c = a*b") # random, long time - 5 loops, best of 3: 653 ms per loop - """ - karatsuba_class = self._parent._karatsuba_class - if cutoff != None: - save_cutoff = karatsuba_class.get_cutoff() - karatsuba_class.set_cutoff(cutoff) - res = karatsuba_class.mul(self,right) - if cutoff != None: - karatsuba_class.set_cutoff(save_cutoff) - return res - - - def _mul_karatsuba_matrix(self,right): - """ - Karatsuba multiplication with multiplication step based - on an isomorphism between a quotient of the underlying - skew polynomial ring and a ring of matrices. - - INPUT: - - - ``right`` -- an other skew polynomial in the same ring - - OUTPUT: - - The result of the product self*right - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = S.random_element(degree=20) - sage: b = S.random_element(degree=20) - sage: a._mul_karatsuba_matrix(b) == a*b - True - - This routine is only efficient when the twisting map (here - ``Frob``) has a large order `r` and the degrees of ``self`` - and ``other`` have a very special shape (just below a power - of `2` times `r * floor(r/2)`):: - - sage: k. = GF(5^40) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = S.random_element(degree=799) - sage: b = S.random_element(degree=799) - sage: timeit("c = a*b") # random, long time - 5 loops, best of 3: 23.1 s per loop - sage: timeit("c = a._mul_karatsuba_matrix(b)") # random, long time - 5 loops, best of 3: 12.2 s per loop - """ - karatsuba_class = self._parent._karatsuba_class - return karatsuba_class.mul_matrix(self,right) - cpdef SkewPolynomial_finite_field_dense _mul_central(self, SkewPolynomial_finite_field_dense right): r""" Return self * right @@ -1883,6 +1415,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): poly = self.rmonic() for factors in self._factorizations_rec(): yield Factorization(factors,sort=False,unit=unit) + def _factorizations_rec(self): if self.is_irreducible(): yield [ (self,1) ] @@ -1940,598 +1473,3 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): ... print "Found %s which is not a correct factorization" % d """ return self.factorizations() - - cpdef RingElement _mul_(self, RingElement right): - """ - Compute self * right (in this order) - - .. NOTE:: - - Use skew Karatsuba's algorithm for skew - polynomials of large degrees. - - INPUT: - - - right -- a skew polynomial in the same ring - - TESTS:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^2 + t; a - x^2 + t - sage: b = x^2 + (t + 1)*x; b - x^2 + (t + 1)*x - sage: a * b - x^4 + (3*t^2 + 2)*x^3 + t*x^2 + (t^2 + t)*x - sage: a * b == b * a - False - """ - return self._parent._karatsuba_class.mul(self,right) - - - def _mul_classical(self,right): - """ - Compute self * right (in this order) using the - skew SchoolBook algorithm. - - INPUT: - - - ``right`` -- a skew polynomial in the same ring - - TESTS:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^2 + t; a - x^2 + t - sage: b = x^2 + (t + 1)*x; b - x^2 + (t + 1)*x - sage: a._mul_classical(b) - x^4 + (3*t^2 + 2)*x^3 + t*x^2 + (t^2 + t)*x - sage: a * b == b * a - False - """ - karatsuba_class = self._parent._karatsuba_class - save_cutoff = karatsuba_class.get_cutoff() - karatsuba_class.set_cutoff(Infinity) - res = karatsuba_class.mul(self,right) - karatsuba_class.set_cutoff(save_cutoff) - return res - - - cpdef rquo_rem_karatsuba(self, RingElement other, cutoff=None): - """ - Right euclidean division based on Karatsuba's algorithm. - - DO NOT USE THIS! It is not efficient for usual degrees! - - .. TODO:: - - Try to understand why... - - TESTS:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - - sage: a = S.random_element(2000) - sage: b = S.random_element(1000) - sage: timeit("q,r = a.rquo_rem_karatsuba(b)") # random, long time - 5 loops, best of 3: 104 ms per loop - sage: timeit("q,r = a.rquo_rem(b)") # random, long time - 5 loops, best of 3: 79.6 ms per loop - sage: a.rquo_rem(b) == a.rquo_rem_karatsuba(b) - True - - sage: a = S.random_element(10000) - sage: b = S.random_element(5000) - sage: timeit("q,r = a.rquo_rem_karatsuba(b)") # random, long time - 5 loops, best of 3: 1.79 s per loop - sage: timeit("q,r = a.rquo_rem(b)") # random, long time - 5 loops, best of 3: 1.93 s per loop - sage: a.rquo_rem(b) == a.rquo_rem_karatsuba(b) - True - """ - karatsuba_class = self._parent._karatsuba_class - if cutoff != None: - save_cutoff = karatsuba_class.get_cutoff() - karatsuba_class.set_cutoff(cutoff) - res = karatsuba_class.div(self,other) - if cutoff != None: - karatsuba_class.set_cutoff(save_cutoff) - return res - -# Karatsuba class -################# - -cdef class SkewPolynomial_finite_field_karatsuba: - """ - A special class implementing Karatsuba multiplication and - euclidean division on skew polynomial rings over finite fields. - - Only for internal use! - - TESTS:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - - An instance of this class is automatically created when the skew - ring is created. It is accessible via the key work ``_karatsuba_class``:: - - sage: KarClass = S._karatsuba_class; KarClass - Special class for Karatsuba multiplication and euclidean division on - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 - - Each instance of this class is equipped with a ``cutoff`` variable. The - default value is the maximum between 150 and the order of the twist map - acting on the finite field:: - - sage: KarClass.get_cutoff() - 150 - - We can set a new value to this variable as follows:: - - sage: KarClass.set_cutoff(27) - sage: KarClass.get_cutoff() - 27 - """ - def __init__(self,parent,cutoff=0): - """ - Initialize a new instance of this class. - """ - self._parent = parent - self._order = parent._order - self._zero = parent.base_ring()(0) - self.set_cutoff(cutoff) - self._algo_matrix = 0 - self._t = None - self._T = None - self._Tinv = None - - - def __repr__(self): - """ - Return a representation of ``self`` - """ - return "Special class for Karatsuba multiplication and euclidean division on\n%s" % self._parent - - - def set_cutoff(self,cutoff=0): - """ - Set a new cutoff for all Karatsuba multiplications - and euclidean divisions performed by this class. - - INPUT: - - - ``cutoff`` -- a nonnegative integer or +Infinity - (default: 0) - - .. NOTE:: - - If ``cutoff`` is `0`, the new cutoff is set to the - default value which is the maximum between 150 and - the order of the twist map acting on the underlying - finite field. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: KarClass = S._karatsuba_class - - sage: KarClass.set_cutoff(27) - sage: KarClass.get_cutoff() - 27 - - sage: KarClass.set_cutoff(Infinity) - sage: KarClass.get_cutoff() - +Infinity - - sage: KarClass.set_cutoff(0) # set the default value - sage: KarClass.get_cutoff() - 150 - - The cutoff can't be less than the order of the twist map:: - - sage: KarClass.set_cutoff(2) - Traceback (most recent call last): - ... - ValueError: cutoff must be 0 or >= 3 - """ - if cutoff == 0: - self._cutoff = max(150,self._order) - else: - if cutoff < self._order: - raise ValueError("cutoff must be 0 or >= %s" % self._order) - if cutoff == Infinity: - self._cutoff = -1 - else: - self._cutoff = cutoff - - - def get_cutoff(self): - """ - Return the current cutoff - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: KarClass = S._karatsuba_class - sage: KarClass.get_cutoff() - 150 - sage: KarClass.set_cutoff(27) - sage: KarClass.get_cutoff() - 27 - """ - if self._cutoff < 0: - return Infinity - else: - return self._cutoff - - - def mul(self,left,right): - """ - INPUT: - - - ``left`` -- a skew polynomial in the skew polynomial - right attached to the class - - - ``right`` -- a skew polynomial in the skew polynomial - right attached to the class - - OUTPUT: - - The product left * right (computed by Karatsuba's algorithm) - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: KarClass = S._karatsuba_class - - sage: a = S.random_element(degree=500) - sage: b = S.random_element(degree=500) - sage: c = KarClass.mul(a,b) - sage: c == a*b - True - - .. NOTE:: - - Behind the scene, the operator ``*`` calls actually - the function ``KarClass.mul()``. - """ - cdef Py_ssize_t dx = left.degree(), dy = right.degree() - cdef list x = left.list() - cdef list y = right.list() - cdef list res - if self._cutoff < 0: - res = self.mul_step(x,y) - else: - if dx < dy: - res = self.mul_iter(y,x,1) - else: - res = self.mul_iter(x,y,0) - return self._parent(res) - - - def mul_matrix(self,left,right): - """ - INPUT: - - - ``left`` -- an element in the skew polynomial ring - attached to the class - - - ``right`` -- an element in the skew polynomial ring - attached to the class - - OUTPUT: - - The product left * right (computed by Karatsuba-matrix - algorithm) - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: KarClass = S._karatsuba_class - - sage: a = S.random_element(degree=500) - sage: b = S.random_element(degree=500) - sage: c = KarClass.mul_matrix(a,b) - sage: c == a*b - True - """ - cdef Py_ssize_t dx = left.degree(), dy = right.degree() - cdef list x = left.list() - cdef list y = right.list() - cdef list res - cdef Py_ssize_t save_cutoff = self._cutoff - cdef Py_ssize_t i, j - self._cutoff = int(self._order * self._order / 2) - self._algo_matrix = 1 - if self._t is None: - self._t = self._parent.base_ring().gen() - if self._T is None: - self._T = zero_matrix(self._parent.base_ring(),self._order) - for i from 0 <= i < self._order: - map = self._parent.twist_map(-i) - for j from 0 <= j < self._order: - self._T.set_unsafe(i,j,map(self._t**j)) - if self._Tinv is None: - self._Tinv = self._T.inverse() - if dx < dy: - res = self.mul_iter(y,x,1) - else: - res = self.mul_iter(x,y,0) - self._cutoff = save_cutoff - self._algo_matrix = 0 - return self._parent(res) - - - def mul_list(self,x,y): - """ - INPUT: - - - ``x`` -- a list of coefficients - - - ``y`` -- a list of coefficients - - OUTPUT: - - The list of coefficients of the product `a * b` - where `a` (resp. `b`) is the skew polynomial whose - coefficients are given by `x` (resp. `y`). - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['u',Frob] - sage: KarClass = S._karatsuba_class - - sage: x = [ k.random_element() for _ in range(500) ] - sage: y = [ k.random_element() for _ in range(500) ] - sage: z = KarClass.mul_list(x,y) - sage: S(z) == S(x)*S(y) - True - """ - if self._cutoff < 0: - return self.mul_step(x,y) - cdef Py_ssize_t dx = len(x)-1, dy = len(y)-1 - if dx < dy: - return self.mul_iter(y,x,1) - else: - return self.mul_iter(x,y,0) - - - cdef list mul_step(self, list x, list y): - """ - Multiplication step in Karatsuba algorithm - """ - cdef Py_ssize_t dx = len(x)-1, dy = len(y) - 1 - if dx < 0 or dy < 0: return [ ] - cdef Py_ssize_t i, j, start, end = dx if dx < self._order else self._order-1 - cdef list twists = [ y ] - for i from 0 <= i < end: - twists.append([ self._parent.twist_map()(a) for a in twists[i] ]) - cdef list res = [ ] - for j from 0 <= j <= dx+dy: - start = 0 if j <= dy else j-dy - end = j if j <= dx else dx - sum = x[start] * twists[start % self._order][j-start] - for i from start < i <= end: - sum += x[i] * twists[i % self._order][j-i] - res.append(sum) - return res - - - cdef list mul_step_matrix(self, list x, list y): - r""" - Multiplication step in Karatsuba-matrix algorithm. It - is based on the ring isomorphism: - - .. MATH:: - - S / NS \simeq M_r(k^\sigma) - - with the standard notations:: - - - `S` is the underlying skew polynomial ring - - - `k` is the base ring of `S` (it's a finite field) - - - `\sigma` is the twisting automorphism on `k` - - - `r` is the order of `\sigma` - - - `N` is a polynomial of definition of the extension - `k / k^\sigma` - - .. WARNING:: - - The polynomials `x` and `y` must have degree - at most `r^2/2` - """ - cdef Py_ssize_t dx = len(x)-1, dy = len(y) - 1 - cdef Py_ssize_t i, j - cdef Py_ssize_t r = self._order - cdef Mx, My, M - cdef list row - cdef RingElement c - k = self._parent.base_ring() - Mx = self._T * matrix(k, r, r, x + [self._zero] * (r*r-len(x))) - My = self._T * matrix(k, r, r, y + [self._zero] * (r*r-len(y))) - for i from 0 <= i < r: - frob = self._parent.twist_map(i) - row = Mx.row(i).list() - row = map(frob,row) - row = [ self._t*c for c in row[r-i:] ] + row[:r-i] - Mx.set_row(i,row) - row = My.row(i).list() - row = map(frob,row) - row = [ self._t*c for c in row[r-i:] ] + row[:r-i] - My.set_row(i,row) - M = Mx * My - for i from 0 <= i < r: - frob = self._parent.twist_map(-i) - row = M.row(i).list() - row = row[i:] + [ c/self._t for c in row[:i] ] - row = map(frob,row) - M.set_row(i,row) - M = self._Tinv * M - return M.list()[:dx+dy+1] - - - cdef list mul_iter(self, list x, list y, char flag): # Assume dx >= dy - """ - Karatsuba's recursive iteration - - .. WARNING:: - - This function assumes that len(x) >= len(y). - """ - cdef Py_ssize_t i, j, k - cdef Py_ssize_t dx = len(x)-1, dy = len(y)-1 - if self._algo_matrix: - if dx < self._cutoff: - if flag: - return self.mul_step_matrix(y,x) - else: - return self.mul_step_matrix(x,y) - else: - if dy < self._cutoff: - if flag: - return self.mul_step(y,x) - else: - return self.mul_step(x,y) - cdef Py_ssize_t dp = self._order * (1 + ceil(dx/self._order/2)) - cdef list x1 = x[:dp], x2 = x[dp:] - cdef list y1 = y[:dp], y2 = y[dp:] - cdef list p1, p2, res - res = self.mul_iter(x1,y1,flag) - if dy >= dp: - res.append(self._zero) - p1 = self.mul_iter(x2,y2,flag) - res.extend(p1) - for i from 0 <= i <= dx-dp: x1[i] += x2[i] - for i from 0 <= i <= dy-dp: y1[i] += y2[i] - p2 = self.mul_iter(x1,y1,flag) - j = dx + dy - 2*dp - k = min(2*dp-2, len(res)-dp-1) - for i from k >= i > j: - res[i+dp] += p2[i] - res[i] - for i from j >= i >= 0: - res[i+dp] += p2[i] - p1[i] - res[i] - else: - if dx-dp < dy: - p2 = self.mul_iter(y1,x2,not flag) - else: - p2 = self.mul_iter(x2,y1,flag) - for i from 0 <= i < dy: - res[i+dp] += p2[i] - res.extend(p2[dy:]) - return res - - - def div(self,left,right): - """ - INPUT: - - - ``left`` -- a skew polynomial in the skew polynomial - right attached to the class - - - ``right`` -- a skew polynomial in the skew polynomial - right attached to the class - - OUTPUT: - - The quotient and the remainder of the right euclidien division - of left by right (computed by Karatsuba's algorithm). - - .. WARNING:: - - This algorithm is only efficient for very very large - degrees. Do not use it! - - .. TODO:: - - Try to understand why... - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: KarClass = S._karatsuba_class - - sage: a = S.random_element(degree=1000) - sage: b = S.random_element(degree=500) - sage: q,r = KarClass.div(a,b) - sage: q2,r2 = a.quo_rem(b) - sage: q == q2 - True - sage: r == r2 - True - """ - cdef list a = left.list() - cdef list b = right.list() - cdef Py_ssize_t da = len(a)-1, db = len(b)-1, i - cdef list q - self._twinv = [ ~b[db] ] - for i from 0 <= i < min(da-db,self._order-1): - self._twinv.append(self._parent.twist_map()(self._twinv[i])) - if self._cutoff < 0: - q = self.div_step(a,0,da,b,0,db) - else: - q = self.div_iter(a,0,da,b,0,db) - return self._parent(q), self._parent(a[:db]) - - - cdef list div_step(self, list a, Py_ssize_t ia, Py_ssize_t da, list b, Py_ssize_t ib, Py_ssize_t db): - """ - Division step in Karatsuba's algorithm - """ - cdef Py_ssize_t i, j - cdef list q = [ ] - cdef list twb = [ b[ib:ib+db] ] - for i from 0 <= i < min(da-db,self._order-1): - twb.append([ self._parent.twist_map()(x) for x in twb[i] ]) - for i from da-db >= i >= 0: - c = self._twinv[i%self._order] * a[ia+i+db] - for j from 0 <= j < db: - a[ia+i+j] -= c * twb[i%self._order][j] - q.append(c) - q.reverse() - return q - - - cdef list div_iter(self, list a, Py_ssize_t ia, Py_ssize_t da, list b, Py_ssize_t ib, Py_ssize_t db): - """ - Karatsuba recursive iteration - """ - cdef Py_ssize_t delta = da - db - if delta < self._cutoff: - return self.div_step(a,ia,da,b,ib,db) - cdef Py_ssize_t i - cdef Py_ssize_t dp = self._order * (1 + int(delta/self._order/2)) - cdef list q = self.div_iter(a,ia+db,delta,b,ib+db-dp,dp), pr - if db < delta: - pr = self.mul_iter(q,b[ib:ib+db-dp],0) - else: - pr = self.mul_iter(b[ib:ib+db-dp],q,1) - for i from 0 <= i < len(pr): - a[i+ia+dp] -= pr[i] - cdef list qq = self.div_iter(a,ia,db+dp-1,b,ib,db) - qq.extend(q) - return qq diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index f168bb1869f..0ff1f996016 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -1581,7 +1581,7 @@ def centre(self, names=None, name=None): # Special class for skew polynomial over finite fields ###################################################### -class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): +class SkewPolynomialRing_finite_field(SkewPolynomialRing_finite_order): """ A specialized class for skew polynomial rings over finite fields. @@ -1596,17 +1596,7 @@ class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): Add methods related to center of skew polynomial ring, irreducibility, karatsuba multiplication and factorization. """ - @staticmethod - def __classcall__(cls, base_ring, map, name=None, sparse=False, element_class=None): - if not element_class: - if sparse: - raise NotImplementedError("sparse skew polynomials are not implemented") - else: - from sage.rings.polynomial import skew_polynomial_finite_field - element_class = skew_polynomial_finite_field.SkewPolynomial_finite_field_dense - return super(SkewPolynomialRing_general,cls).__classcall__(cls,base_ring,map,name,sparse,element_class) - - def __init__(self, base_ring, map, name, sparse, element_class): + def __init__(self, base_ring, twist_map, names, sparse, category=None, element_class=None): """ This method is a constructor for a general, dense univariate skew polynomial ring over a finite field. @@ -1634,64 +1624,21 @@ def __init__(self, base_ring, map, name, sparse, element_class): sage: T. = k['x', Frob]; T Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 """ - self._order = -1 - try: - self._order = map.order() - except (AttributeError,NotImplementedError): - pass - if self._order < 0: - try: - if map.is_identity(): - self._order = 1 - except (AttributeError,NotImplementedError): - pass - if self._order < 0: - raise NotImplementedError("unable to determine the order of %s" % map) - SkewPolynomialRing_general.__init__ (self, base_ring, map, name, sparse, element_class) - self._maps = [ map**i for i in range(self._order) ] - - def twist_map(self, n=1): - """ - Return the twist map, otherwise known as the automorphism over the base ring of - ``self``, iterated `n` times. - - INPUT: - - - ``n`` - a relative integer (default: 1) - - OUTPUT: - - - The `n`-th iterative of the twist map of this skew polynomial ring. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: S.twist_map() - Frobenius endomorphism t |--> t^5 on Finite Field in t of size 5^3 - sage: S.twist_map(11) - Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 - sage: S.twist_map(3) - Identity endomorphism of Finite Field in t of size 5^3 - - It also works if `n` is negative:: - - sage: S.twist_map(-1) - Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 - """ - return self._maps[n%self._order] + if element_class is None: + from sage.rings.polynomial.skew_polynomial_finite_field import SkewPolynomial_finite_field + element_class = SkewPolynomial_finite_field + SkewPolynomialRing_finite_order.__init__(self, base_ring, twist_map, name, sparse, category, element_class) - def _new_retraction_map(self,alea=None): + def _new_retraction_map(self, alea=None): """ This is an internal function used in factorization. """ k = self.base_ring() base = k.base_ring() - (kfixed,embed) = self._maps[1].fixed_points() + (kfixed, embed) = self._maps[1].fixed_points() section = embed.section() if not kfixed.has_coerce_map_from(base): - raise NotImplementedError("No coercion map from %s to %s" % (base,kfixed)) + raise NotImplementedError("No coercion map from %s to %s" % (base, kfixed)) if alea is None: alea = k.random_element() self._alea_retraction = alea @@ -1705,9 +1652,10 @@ def _new_retraction_map(self,alea=None): tr += x elt *= k.gen() trace.append(section(tr)) - self._matrix_retraction = MatrixSpace(kfixed,1,k.degree())(trace) + self._matrix_retraction = MatrixSpace(kfixed, 1, k.degree())(trace) - def _retraction(self,x,newmap=False,alea=None): # Better to return the retraction map but more difficult + def _retraction(self, x, newmap=False, alea=None): + # Better to return the retraction map but more difficult """ This is an internal function used in factorization. """ From 0f00c63b909409675f6f8565e900e26e7f803a58 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 6 Apr 2020 16:47:28 +0200 Subject: [PATCH 03/12] make things compile --- src/module_list.py | 3 + .../skew_polynomial_finite_field.pxd | 38 +-- .../skew_polynomial_finite_field.pyx | 217 ++++++++++-------- .../rings/polynomial/skew_polynomial_ring.py | 30 ++- 4 files changed, 141 insertions(+), 147 deletions(-) diff --git a/src/module_list.py b/src/module_list.py index c74615f75db..310f0c6d71b 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1531,6 +1531,9 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.skew_polynomial_finite_order', sources = ['sage/rings/polynomial/skew_polynomial_finite_order.pyx']), + Extension('sage.rings.polynomial.skew_polynomial_finite_field', + sources = ['sage/rings/polynomial/skew_polynomial_finite_field.pyx']), + # Note that weil_polynomials includes distutils directives in order to support # conditional OpenMP compilation (by uncommenting lines) Extension('sage.rings.polynomial.weil.weil_polynomials', diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd index 6a17e43d17c..e6c4d3e28d1 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -1,11 +1,9 @@ -from skew_polynomial_element cimport SkewPolynomial_generic_dense +from sage.rings.polynomial.skew_polynomial_finite_order cimport SkewPolynomial_finite_order_dense +from sage.rings.polynomial.polynomial_element cimport Polynomial as CenterSkewPolynomial_generic_dense from sage.matrix.matrix_dense cimport Matrix_dense -cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): - - cdef Polynomial _norm +cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_finite_order_dense): cdef _norm_factor - cdef _optbound cdef dict _rdivisors cdef dict _types cdef _factorization @@ -21,35 +19,9 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef Py_ssize_t _val_inplace_unit(self) cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other) - cdef Matrix_dense _matphir_c(self) - cdef Matrix_dense _matmul_c(self) - - # Finding divisors + # Finding divisors cdef SkewPolynomial_finite_field_dense _rdivisor_c(P, CenterSkewPolynomial_generic_dense N) - # Finding factorizations + # Finding factorizations cdef _factor_c(self) cdef _factor_uniform_c(self) - - # Karatsuba - #cpdef RingElement _mul_karatsuba(self, RingElement other, cutoff=*) - cpdef SkewPolynomial_finite_field_dense _mul_central(self, SkewPolynomial_finite_field_dense right) - cpdef RingElement _mul_(self, RingElement right) - cpdef rquo_rem_karatsuba(self, RingElement other, cutoff=*) - - cdef class SkewPolynomial_finite_field_karatsuba: - cdef _parent - cdef Py_ssize_t _order - cdef Py_ssize_t _cutoff - cdef RingElement _zero - cdef _twist - cdef char _algo_matrix - cdef RingElement _t - cdef Matrix_dense _T, _Tinv - - cdef list mul_step (self, list x, list y) - cdef list mul_step_matrix(self, list x, list y) - cdef list mul_iter(self, list x, list y, char flag) - cdef list _twinv - cdef list div_step(self, list a, Py_ssize_t ia, Py_ssize_t da, list b, Py_ssize_t ib, Py_ssize_t db) - cdef list div_iter(self, list a, Py_ssize_t ia, Py_ssize_t da, list b, Py_ssize_t ib, Py_ssize_t db) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 9e44d7dc3f0..a279f228cc7 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -23,18 +23,32 @@ AUTHOR:: # http://www.gnu.org/licenses/ #**************************************************************************** +from cysignals.signals cimport sig_on, sig_off + import copy import cysignals from sage.matrix.constructor import zero_matrix from sage.rings.ring cimport Ring from sage.matrix.matrix_dense cimport Matrix_dense -from polynomial_element cimport Polynomial +from sage.matrix.matrix_space import MatrixSpace +from sage.rings.all import ZZ +from sage.rings.polynomial.polynomial_element cimport Polynomial +from sage.rings.polynomial.polynomial_element cimport Polynomial as CenterSkewPolynomial_generic_dense from sage.rings.integer cimport Integer from sage.structure.element cimport RingElement -from polynomial_ring_constructor import PolynomialRing -from skew_polynomial_element cimport SkewPolynomial_generic_dense +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.skew_polynomial_element cimport SkewPolynomial_generic_dense +from sage.rings.polynomial.skew_polynomial_finite_order cimport SkewPolynomial_finite_order_dense + +from sage.combinat.permutation import Permutation, Permutations +from sage.combinat.partition import Partition +from sage.structure.factorization import Factorization +from sage.misc.mrange import xmrange_iter +from sage.arith.misc import factorial +from sage.combinat.q_analogues import q_jordan -cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): + +cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef SkewPolynomial_finite_field_dense _rgcd(self, SkewPolynomial_finite_field_dense other): """ Fast creation of the right gcd of ``self`` and ``other``. @@ -265,101 +279,101 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): self._norm_factor = self.reduced_norm().factor() return self._norm_factor - cpdef SkewPolynomial_finite_field_dense _mul_central(self, SkewPolynomial_finite_field_dense right): - r""" - Return self * right + #cpdef SkewPolynomial_finite_field_dense _mul_central(self, SkewPolynomial_finite_field_dense right): + # r""" + # Return self * right - .. WARNING:: + # .. WARNING:: - Do you use this function! It is very slow due to a quite - slow interface with ``polynomial_zz_pex``. + # Do you use this function! It is very slow due to a quite + # slow interface with ``polynomial_zz_pex``. - ALGORITHM:: + # ALGORITHM:: - Notations:: + # Notations:: - - `S` is the underlyling skew polynomial ring + # - `S` is the underlyling skew polynomial ring - - `x` is the variable on `S` + # - `x` is the variable on `S` - - `k` is the base ring of `S` (it is a finite field) + # - `k` is the base ring of `S` (it is a finite field) - - `\sigma` is the twisting automorphism acting on `k` + # - `\sigma` is the twisting automorphism acting on `k` - - `r` is the order of `\sigma` + # - `r` is the order of `\sigma` - - `t` is a generator of `k` over `k^\sigma` + # - `t` is a generator of `k` over `k^\sigma` - #. We decompose the polynomial ``right`` as follows:: + # #. We decompose the polynomial ``right`` as follows:: - .. MATH:: + # .. MATH:: - right = \sum_{i=0}^{r-1} \sum_{j=0}^{r-1} y_{i,j} t^j x^i + # right = \sum_{i=0}^{r-1} \sum_{j=0}^{r-1} y_{i,j} t^j x^i - where `y_{i,j}` are polynomials in the center `k^\sigma[x^r]`. + # where `y_{i,j}` are polynomials in the center `k^\sigma[x^r]`. - #. We compute all products `z_{i,j} = left * y_{i,j}`; since - all `y_{i,j}` lie in the center, we can compute all these - products as if `left` was a commutative polynomial (and we - can therefore use fast algorithms like FFT and/or fast - implementations) + # #. We compute all products `z_{i,j} = left * y_{i,j}`; since + # all `y_{i,j}` lie in the center, we can compute all these + # products as if `left` was a commutative polynomial (and we + # can therefore use fast algorithms like FFT and/or fast + # implementations) - #. We compute and return the sum + # #. We compute and return the sum - .. MATH:: + # .. MATH:: - \sum_{i=0}^{r-1} \sum_{j=0}^{r-1} z_{i,j} t^j x^i + # \sum_{i=0}^{r-1} \sum_{j=0}^{r-1} z_{i,j} t^j x^i - EXAMPLES:: + # EXAMPLES:: - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = S.random_element(degree=10) - sage: b = S.random_element(degree=10) - sage: a._mul_central(b) == a*b - True + # sage: k. = GF(5^3) + # sage: Frob = k.frobenius_endomorphism() + # sage: S. = k['x',Frob] + # sage: a = S.random_element(degree=10) + # sage: b = S.random_element(degree=10) + # sage: a._mul_central(b) == a*b + # True - TESTS:: + # TESTS:: - Here is an example where `k^\sigma` is not a prime field:: + # Here is an example where `k^\sigma` is not a prime field:: - sage: k. = GF(5^6) - sage: Frob = k.frobenius_endomorphism(2) - sage: S. = k['x',Frob] - sage: a = S.random_element(degree=10) - sage: b = S.random_element(degree=10) - sage: a._mul_central(b) == a*b - True - """ - skew_ring = self._parent - base_ring = skew_ring.base_ring() - commutative_ring = PolynomialRing(skew_ring.base_ring(),name='x') - cdef RingElement c - cdef RingElement zero = base_ring(0) - cdef Py_ssize_t i, j, k - cdef Py_ssize_t order = skew_ring._order - cdef Py_ssize_t degree = base_ring.degree() - - left = commutative_ring(self.__coeffs) - cdef list y = [ c.polynomial() for c in right.__coeffs ] - cdef Py_ssize_t leny = len(y) - cdef list yc = leny * [zero] - cdef list res = (leny + len(self.__coeffs) - 1) * [zero] - cdef list term - cdef list twist = [ base_ring.gen() ] - for i from 0 <= i < order-1: - twist.append(skew_ring.twist_map(1)(twist[i])) - for i from 0 <= i < order: - for j from 0 <= j < degree: - for k from i <= k < leny by order: - yc[k] = y[k][j] - term = (left * commutative_ring(yc)).list() - for k from i <= k < len(term): - res[k] += term[k] * twist[(k-i)%order]**j - for k from i <= k < leny by order: - yc[k] = zero - return self._new_c(res,skew_ring,1) + # sage: k. = GF(5^6) + # sage: Frob = k.frobenius_endomorphism(2) + # sage: S. = k['x',Frob] + # sage: a = S.random_element(degree=10) + # sage: b = S.random_element(degree=10) + # sage: a._mul_central(b) == a*b + # True + # """ + # skew_ring = self._parent + # base_ring = skew_ring.base_ring() + # commutative_ring = PolynomialRing(skew_ring.base_ring(),name='x') + # cdef RingElement c + # cdef RingElement zero = base_ring(0) + # cdef Py_ssize_t i, j, k + # cdef Py_ssize_t order = skew_ring._order + # cdef Py_ssize_t degree = base_ring.degree() + + # left = commutative_ring(self.__coeffs) + # cdef list y = [ c.polynomial() for c in right.__coeffs ] + # cdef Py_ssize_t leny = len(y) + # cdef list yc = leny * [zero] + # cdef list res = (leny + len(self.__coeffs) - 1) * [zero] + # cdef list term + # cdef list twist = [ base_ring.gen() ] + # for i from 0 <= i < order-1: + # twist.append(skew_ring.twist_map(1)(twist[i])) + # for i from 0 <= i < order: + # for j from 0 <= j < degree: + # for k from i <= k < leny by order: + # yc[k] = y[k][j] + # term = (left * commutative_ring(yc)).list() + # for k from i <= k < len(term): + # res[k] += term[k] * twist[(k-i)%order]**j + # for k from i <= k < leny by order: + # yc[k] = zero + # return self._new_c(res,skew_ring,1) def is_irreducible(self): """ @@ -505,7 +519,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): m -= deg self = self // d type.append(deg) - type = Partition(type) + # type = Partition(type) if self._types is None: self._types = { N: type } else: @@ -590,7 +604,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): return D - def irreducible_divisor(self,side=Right,distribution=None): + def irreducible_divisor(self,right=True,distribution=None): """ INPUT: @@ -690,10 +704,10 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): break else: N = self.reduced_norm_factor()[0][0] - return self.irreducible_divisor_with_norm(N,side=side,distribution=distribution) + return self.irreducible_divisor_with_norm(N,right=right,distribution=distribution) - def irreducible_divisor_with_norm(self,N,side=Right,distribution=None): # Ajouter side + def irreducible_divisor_with_norm(self,N,right=True,distribution=None): """ INPUT: @@ -794,7 +808,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): NS = self._parent(N) degN = N.degree() - if side is Right: + if right: if distribution == "uniform": P1 = self._rgcd(NS) if P1.degree() != degN: @@ -824,7 +838,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): distribution = "uniform" - def irreducible_divisors(self,side=Right): + def irreducible_divisors(self,right=True): """ INPUT: @@ -891,10 +905,10 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): sage: Set(rightdiv) == Set(rightdiv2) True """ - return self._irreducible_divisors(side) + return self._irreducible_divisors(right) - def _irreducible_divisors(self,side): # prendre side en compte + def _irreducible_divisors(self, right): """ Return an iterator over all irreducible monic divisors of this skew polynomial. @@ -908,37 +922,36 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): center = skew_ring.center() kfixed = center.base_ring() F = self.reduced_norm_factor() - oppside = side.opposite() for N,_ in F: if N == center.gen(): yield skew_ring.gen() continue degN = N.degree() NS = skew_ring(N) - P = self.gcd(NS,side=side) + P = self.gcd(NS,right=right) m = P.degree()/degN if m == 1: yield P continue degrandom = P.degree() - 1 - Q,_ = NS.quo_rem(P,side=side) - P1 = self.irreducible_divisor_with_norm(N,side=side) - Q1,_ = P.quo_rem(P1,side=side) + Q,_ = NS.quo_rem(P,right=right) + P1 = self.irreducible_divisor_with_norm(N,right=right) + Q1,_ = P.quo_rem(P1,right=right) while True: R = skew_ring.random_element((degrandom,degrandom)) - if side is Right: - g = (R*Q).rem(P,side=Left) + if right: + g = (R*Q).rem(P, right=False) else: g = (Q*R).rem(P) - if g.gcd(P,side=oppside) != 1: continue + if g.gcd(P, right=not right) != 1: continue L = Q1 V = L for i in range(1,m): - if side is Right: - L = (g*L).gcd(P,side=Left) + if right: + L = (g*L).gcd(P, right=False) else: L = (L*g).gcd(P) - V = V.gcd(L,side=oppside) + V = V.gcd(L, right=not right) if V == 1: break rng = xmrange_iter([kfixed]*degN,center) for i in range(m): @@ -946,16 +959,16 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): f = skew_ring(1) for j in range(i): coeff = pol.pop() - f = (g*f+coeff).rem(P,side=oppside) - if side is Right: - d = (f*Q1).gcd(P,side=Left) + f = (g*f+coeff).rem(P, right=not right) + if right: + d = (f*Q1).gcd(P, right=False) else: d = (Q1*f).gcd(P) - d,_ = P.quo_rem(d,side=oppside) + d,_ = P.quo_rem(d, right=not right) yield d - def count_irreducible_divisors(self,side=Right): + def count_irreducible_divisors(self,right=True): """ INPUT: @@ -1014,7 +1027,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): if N == gencenter: continue degN = N.degree() - P = self.gcd(skew_ring(N), side=side) + P = self.gcd(skew_ring(N), right=right) m = P.degree()/degN cardL = cardcenter**degN count += (cardL**m - 1)/(cardL - 1) @@ -1420,7 +1433,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order): if self.is_irreducible(): yield [ (self,1) ] else: - for div in self._irreducible_divisors(Right): + for div in self._irreducible_divisors(True): poly = self // div # Here, we should update poly._norm, poly._norm_factor, poly._rdivisors for factors in poly._factorizations_rec(): diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 267be26cdbb..e4f4d7db879 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -409,18 +409,23 @@ def __classcall_private__(cls, base_ring, twist_map=None, names=None, sparse=Fal except IndexError: raise NotImplementedError("multivariate skew polynomials rings not supported") - # We check if the twisting morphism has finite order + # We find the best constructor + constructor = None if base_ring in Fields(): try: order = twist_map.order() if order is not Infinity: - from sage.rings.polynomial.skew_polynomial_ring import SkewPolynomialRing_finite_order - return SkewPolynomialRing_finite_order(base_ring, twist_map, names, sparse) + if base_ring.is_finite(): + constructor = SkewPolynomialRing_finite_field + else: + constructor = SkewPolynomialRing_finite_order except AttributeError: pass - - # We fallback to generic implementation - return cls.__classcall__(cls, base_ring, twist_map, names, sparse) + if constructor is not None: + return constructor(base_ring, twist_map, names, sparse) + else: + # We fallback to generic implementation + return cls.__classcall__(cls, base_ring, twist_map, names, sparse) def __init__(self, base_ring, twist_map, name, sparse, category=None): r""" @@ -1544,8 +1549,9 @@ def __init__(self, base_ring, twist_map, name, sparse, category=None): sage: TestSuite(S).run() """ - from sage.rings.polynomial.skew_polynomial_finite_order import SkewPolynomial_finite_order_dense - self.Element = SkewPolynomial_finite_order_dense + if not hasattr(self, 'Element') or self.Element is None: + from sage.rings.polynomial.skew_polynomial_finite_order import SkewPolynomial_finite_order_dense + self.Element = SkewPolynomial_finite_order_dense SkewPolynomialRing.__init__(self, base_ring, twist_map, name, sparse, category) self._order = twist_map.order() @@ -1730,7 +1736,7 @@ class SkewPolynomialRing_finite_field(SkewPolynomialRing_finite_order): Add methods related to center of skew polynomial ring, irreducibility, karatsuba multiplication and factorization. """ - def __init__(self, base_ring, twist_map, names, sparse, category=None, element_class=None): + def __init__(self, base_ring, twist_map, names, sparse, category=None): """ This method is a constructor for a general, dense univariate skew polynomial ring over a finite field. @@ -1758,10 +1764,10 @@ def __init__(self, base_ring, twist_map, names, sparse, category=None, element_c sage: T. = k['x', Frob]; T Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 """ - if element_class is None: + if not hasattr(self, 'Element') or self.Element is None: from sage.rings.polynomial.skew_polynomial_finite_field import SkewPolynomial_finite_field - element_class = SkewPolynomial_finite_field - SkewPolynomialRing_finite_order.__init__(self, base_ring, twist_map, name, sparse, category, element_class) + self.Element = SkewPolynomial_finite_field + SkewPolynomialRing_finite_order.__init__(self, base_ring, twist_map, name, sparse, category) def _new_retraction_map(self, alea=None): """ From f505adc35ccdb6ba294bb6dee196b547ed29a380 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 6 Apr 2020 17:09:11 +0200 Subject: [PATCH 04/12] small fixes --- .../rings/polynomial/skew_polynomial_finite_field.pxd | 2 +- .../rings/polynomial/skew_polynomial_finite_field.pyx | 10 ++++++---- src/sage/rings/polynomial/skew_polynomial_ring.py | 11 +++++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd index e6c4d3e28d1..89f895cca02 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -1,5 +1,5 @@ from sage.rings.polynomial.skew_polynomial_finite_order cimport SkewPolynomial_finite_order_dense -from sage.rings.polynomial.polynomial_element cimport Polynomial as CenterSkewPolynomial_generic_dense +from sage.rings.polynomial.skew_polynomial_element cimport CenterSkewPolynomial_generic_dense from sage.matrix.matrix_dense cimport Matrix_dense cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_finite_order_dense): diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index a279f228cc7..11eff323132 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -23,7 +23,9 @@ AUTHOR:: # http://www.gnu.org/licenses/ #**************************************************************************** -from cysignals.signals cimport sig_on, sig_off +#from cysignals.signals cimport sig_on, sig_off +def sig_on(): pass +def sig_off(): pass import copy import cysignals @@ -33,7 +35,7 @@ from sage.matrix.matrix_dense cimport Matrix_dense from sage.matrix.matrix_space import MatrixSpace from sage.rings.all import ZZ from sage.rings.polynomial.polynomial_element cimport Polynomial -from sage.rings.polynomial.polynomial_element cimport Polynomial as CenterSkewPolynomial_generic_dense +from sage.rings.polynomial.skew_polynomial_element cimport CenterSkewPolynomial_generic_dense from sage.rings.integer cimport Integer from sage.structure.element cimport RingElement from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -1044,7 +1046,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef skew_ring = self._parent cdef Py_ssize_t degQ, degrandom, m, mP, i cdef CenterSkewPolynomial_generic_dense N - cdef SkewPolynomial_finite_field_dense poly = self.rmonic() + cdef SkewPolynomial_finite_field_dense poly = self.right_monic() cdef val = poly._val_inplace_unit() if val == -1: return Factorization([], sort=False, unit=skew_ring.zero_element()) @@ -1425,7 +1427,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if self.is_zero(): raise ValueError("factorization of 0 not defined") unit = self.leading_coefficient() - poly = self.rmonic() + poly = self.right_monic() for factors in self._factorizations_rec(): yield Factorization(factors,sort=False,unit=unit) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index e4f4d7db879..a04225ffea7 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -388,7 +388,7 @@ class SkewPolynomialRing(Algebra, UniqueRepresentation): - Multivariate Skew Polynomial Ring - Add derivations. """ - Element = sage.rings.polynomial.skew_polynomial_element.SkewPolynomial_generic_dense + # Element = sage.rings.polynomial.skew_polynomial_element.SkewPolynomial_generic_dense def __classcall_private__(cls, base_ring, twist_map=None, names=None, sparse=False): if base_ring not in CommutativeRings(): @@ -455,6 +455,9 @@ def __init__(self, base_ring, twist_map, name, sparse, category=None): 0 sage: TestSuite(S).run() """ + if not hasattr(self, 'Element') or self.Element is None: + from sage.rings.polynomial.skew_polynomial_element import SkewPolynomial_generic_dense + self.Element = SkewPolynomial_generic_dense self.__is_sparse = sparse self._map = twist_map self._maps = {0: IdentityMorphism(base_ring), 1: self._map} @@ -1765,9 +1768,9 @@ def __init__(self, base_ring, twist_map, names, sparse, category=None): Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 """ if not hasattr(self, 'Element') or self.Element is None: - from sage.rings.polynomial.skew_polynomial_finite_field import SkewPolynomial_finite_field - self.Element = SkewPolynomial_finite_field - SkewPolynomialRing_finite_order.__init__(self, base_ring, twist_map, name, sparse, category) + from sage.rings.polynomial.skew_polynomial_finite_field import SkewPolynomial_finite_field_dense + self.Element = SkewPolynomial_finite_field_dense + SkewPolynomialRing_finite_order.__init__(self, base_ring, twist_map, names, sparse, category) def _new_retraction_map(self, alea=None): """ From badee70ec457d3d9f5cdda97fd0dc4577640e6fe Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 6 Apr 2020 18:37:25 +0200 Subject: [PATCH 05/12] remove inplace stuff --- .../skew_polynomial_finite_field.pxd | 11 - .../skew_polynomial_finite_field.pyx | 472 ++++-------------- 2 files changed, 91 insertions(+), 392 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd index 89f895cca02..946023c6bdd 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -8,17 +8,6 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_finite_order_dense) cdef dict _types cdef _factorization - cdef SkewPolynomial_finite_field_dense _rgcd(self,SkewPolynomial_finite_field_dense other) - cdef void _inplace_lrem(self, SkewPolynomial_finite_field_dense other) - cdef void _inplace_rrem(self, SkewPolynomial_finite_field_dense other) - cdef void _inplace_lfloordiv(self, SkewPolynomial_finite_field_dense other) - cdef void _inplace_rfloordiv(self, SkewPolynomial_finite_field_dense other) - cdef void _inplace_lmonic(self) - cdef void _inplace_rmonic(self) - cdef void _inplace_rgcd(self,SkewPolynomial_finite_field_dense other) - cdef Py_ssize_t _val_inplace_unit(self) - cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other) - # Finding divisors cdef SkewPolynomial_finite_field_dense _rdivisor_c(P, CenterSkewPolynomial_generic_dense N) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 11eff323132..a9e5303af58 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -51,217 +51,6 @@ from sage.combinat.q_analogues import q_jordan cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): - cdef SkewPolynomial_finite_field_dense _rgcd(self, SkewPolynomial_finite_field_dense other): - """ - Fast creation of the right gcd of ``self`` and ``other``. - """ - cdef SkewPolynomial_finite_field_dense A = self - cdef SkewPolynomial_finite_field_dense B = other - cdef SkewPolynomial_finite_field_dense swap - if len(B._coeffs): - A = self._new_c(A._coeffs[:],A._parent) - B = B._new_c(B._coeffs[:],B._parent) - while len(B._coeffs): - A._inplace_rrem(B) - swap = A; A = B; B = swap - return A - else: - return self - - cdef void _inplace_lrem(self, SkewPolynomial_finite_field_dense other): - """ - Replace ``self`` by the remainder in the left euclidean division - of ``self`` by ``other`` (only for internal use). - """ - cdef list a = (self)._coeffs - cdef list b = (other)._coeffs - cdef Py_ssize_t da = len(a)-1, db = len(b)-1 - cdef Py_ssize_t i, j - cdef RingElement c, inv - parent = self._parent - if db < 0: - raise ZeroDivisionError - if da >= db: - inv = ~b[db] - for i from da-db >= i >= 0: - c = parent.twist_map(-db)(inv*a[i+db]) - for j from 0 <= j < db: - a[i+j] -= b[j] * parent.twist_map(j)(c) - del a[db:] - self.__normalize() - - cdef void _inplace_rrem(self, SkewPolynomial_finite_field_dense other): - """ - Replace ``self`` by the remainder in the right euclidean division - of ``self`` by ``other`` (only for internal use). - """ - cdef list a = (self)._coeffs - cdef list b = (other)._coeffs - cdef Py_ssize_t da = len(a)-1, db = len(b)-1 - cdef Py_ssize_t i, j, order - cdef RingElement c, x, inv - cdef list twinv, twb - parent = self._parent - if db < 0: - raise ZeroDivisionError - if da >= db: - order = parent._order - inv = ~b[db] - twinv = [ inv ] - for i from 0 <= i < min(da-db,order-1): - twinv.append(parent.twist_map()(twinv[i])) - twb = (other)._conjugates - for i from len(twb)-1 <= i < min(da-db,order-1): - twb.append([ parent.twist_map()(x) for x in twb[i] ]) - for i from da-db >= i >= 0: - c = twinv[i%order] * a[i+db] - for j from 0 <= j < db: - a[i+j] -= c * twb[i%order][j] - del a[db:] - self.__normalize() - - cdef void _inplace_lfloordiv(self, SkewPolynomial_finite_field_dense other): - """ - Replace ``self`` by the quotient in the left euclidean division - of ``self`` by ``other`` (only for internal use). - """ - cdef list a = (self)._coeffs - cdef list b = (other)._coeffs - cdef Py_ssize_t da = len(a)-1, db = len(b)-1 - cdef Py_ssize_t i, j, deb - cdef RingElement c, inv - parent = self._parent - if db < 0: - raise ZeroDivisionError - if da < db: - (self)._coeffs = [ ] - else: - inv = ~b[db] - for i from da-db >= i >= 0: - c = a[i+db] = parent.twist_map(-db)(inv*a[i+db]) - if i < db: deb = db - else: deb = i - for j from deb <= j < db+i: - a[j] -= b[j-i] * parent.twist_map(j-i)(c) - del a[:db] - self.__normalize() - - cdef void _inplace_rfloordiv(self, SkewPolynomial_finite_field_dense other): - """ - Replace ``self`` by the quotient in the right euclidean division - of ``self`` by ``other`` (only for internal use). - """ - cdef list a = (self)._coeffs - cdef list b = (other)._coeffs - cdef Py_ssize_t da = len(a)-1, db = len(b)-1 - cdef Py_ssize_t i, j, deb, order - cdef RingElement c, x, inv - parent = self._parent - if db < 0: - raise ZeroDivisionError - if da < db: - (self)._coeffs = [ ] - else: - order = parent._order - inv = ~b[db] - twinv = [ inv ] - for i from 0 <= i < min(da-db,order-1): - twinv.append(parent.twist_map()(twinv[i])) - twb = (other)._conjugates - for i from len(twb)-1 <= i < min(da-db,order-1): - twb.append([ parent.twist_map()(x) for x in twb[i] ]) - for i from da-db >= i >= 0: - c = a[i+db] = twinv[i%order] * a[i+db] - if i < db: deb = db - else: deb = i - for j from deb <= j < db+i: - a[j] -= c * twb[i%order][j-i] - del a[:db] - self.__normalize() - - cdef void _inplace_lmonic(self): - """ - Replace ``self`` by ``self.lmonic()`` (only for internal use). - """ - cdef list a = (self)._coeffs - cdef Py_ssize_t da = len(a)-1, i - cdef RingElement inv = ~a[da] - parent = self._parent - a[da] = parent.base_ring()(1) - for i from 0 <= i < da: - a[i] *= parent.twist_map(i-da)(inv) - - cdef void _inplace_rmonic(self): - """ - Replace ``self`` by ``self.rmonic()`` (only for internal use). - """ - cdef list a = (self)._coeffs - cdef Py_ssize_t da = len(a)-1, i - cdef RingElement inv = ~a[da] - a[da] = self._parent.base_ring()(1) - for i from 0 <= i < da: - a[i] *= inv - - cdef void _inplace_rgcd(self,SkewPolynomial_finite_field_dense other): - """ - Replace ``self`` by its right gcd with ``other`` (only for internal use). - """ - cdef SkewPolynomial_finite_field_dense B - cdef list swap - if len(other._coeffs): - B = self._new_c(other._coeffs[:],other._parent) - while len(B._coeffs): - B._conjugates = [ B._coeffs ] - self._inplace_rrem(B) - swap = self._coeffs - self._coeffs = B._coeffs - B._coeffs = swap - - - cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other): - """ - Replace ``self`` by the remainder in the right euclidean division - of ``self`` by ``other`` and return the quotient (only for internal use). - """ - cdef list a = (self)._coeffs - cdef list b = (other)._coeffs - cdef Py_ssize_t da = len(a)-1, db = len(b)-1 - cdef Py_ssize_t i, j - cdef RingElement c, inv - cdef list q - parent = self._parent - if db < 0: - raise ZeroDivisionError - if da < db: - r = self._new_c([],self._parent) - return r - inv = ~b[db] - q = [ ] - for i from da-db >= i >= 0: - c = parent.twist_map(i)(inv) * a[i+db] - q.append(c) - for j from 0 <= j < db: - a[i+j] -= c * parent.twist_map(i)(b[j]) - del a[db:] - self.__normalize() - q.reverse() - r = self._new_c(q,self._parent) - return r - - cdef Py_ssize_t _val_inplace_unit(self): - """ - Return `v` the valuation of ``self`` and replace ``self`` by - `self >> v` (only for internal use). - """ - cdef list a = (self)._coeffs - cdef Py_ssize_t val = 0 - if len(a) < 0: - return -1 - while a[0].is_zero(): - del a[0] - val += 1 - return val - def reduced_norm_factor(self): """ Return the reduced norm of this polynomial @@ -281,102 +70,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): self._norm_factor = self.reduced_norm().factor() return self._norm_factor - #cpdef SkewPolynomial_finite_field_dense _mul_central(self, SkewPolynomial_finite_field_dense right): - # r""" - # Return self * right - - # .. WARNING:: - - # Do you use this function! It is very slow due to a quite - # slow interface with ``polynomial_zz_pex``. - - # ALGORITHM:: - - # Notations:: - - # - `S` is the underlyling skew polynomial ring - - # - `x` is the variable on `S` - - # - `k` is the base ring of `S` (it is a finite field) - - # - `\sigma` is the twisting automorphism acting on `k` - - # - `r` is the order of `\sigma` - - # - `t` is a generator of `k` over `k^\sigma` - - # #. We decompose the polynomial ``right`` as follows:: - - # .. MATH:: - - # right = \sum_{i=0}^{r-1} \sum_{j=0}^{r-1} y_{i,j} t^j x^i - - # where `y_{i,j}` are polynomials in the center `k^\sigma[x^r]`. - - # #. We compute all products `z_{i,j} = left * y_{i,j}`; since - # all `y_{i,j}` lie in the center, we can compute all these - # products as if `left` was a commutative polynomial (and we - # can therefore use fast algorithms like FFT and/or fast - # implementations) - - # #. We compute and return the sum - - # .. MATH:: - - # \sum_{i=0}^{r-1} \sum_{j=0}^{r-1} z_{i,j} t^j x^i - - # EXAMPLES:: - - # sage: k. = GF(5^3) - # sage: Frob = k.frobenius_endomorphism() - # sage: S. = k['x',Frob] - # sage: a = S.random_element(degree=10) - # sage: b = S.random_element(degree=10) - # sage: a._mul_central(b) == a*b - # True - - # TESTS:: - - # Here is an example where `k^\sigma` is not a prime field:: - - # sage: k. = GF(5^6) - # sage: Frob = k.frobenius_endomorphism(2) - # sage: S. = k['x',Frob] - # sage: a = S.random_element(degree=10) - # sage: b = S.random_element(degree=10) - # sage: a._mul_central(b) == a*b - # True - # """ - # skew_ring = self._parent - # base_ring = skew_ring.base_ring() - # commutative_ring = PolynomialRing(skew_ring.base_ring(),name='x') - # cdef RingElement c - # cdef RingElement zero = base_ring(0) - # cdef Py_ssize_t i, j, k - # cdef Py_ssize_t order = skew_ring._order - # cdef Py_ssize_t degree = base_ring.degree() - - # left = commutative_ring(self.__coeffs) - # cdef list y = [ c.polynomial() for c in right.__coeffs ] - # cdef Py_ssize_t leny = len(y) - # cdef list yc = leny * [zero] - # cdef list res = (leny + len(self.__coeffs) - 1) * [zero] - # cdef list term - # cdef list twist = [ base_ring.gen() ] - # for i from 0 <= i < order-1: - # twist.append(skew_ring.twist_map(1)(twist[i])) - # for i from 0 <= i < order: - # for j from 0 <= j < degree: - # for k from i <= k < leny by order: - # yc[k] = y[k][j] - # term = (left * commutative_ring(yc)).list() - # for k from i <= k < len(term): - # res[k] += term[k] * twist[(k-i)%order]**j - # for k from i <= k < leny by order: - # yc[k] = zero - # return self._new_c(res,skew_ring,1) - def is_irreducible(self): """ Return true if this skew polynomial is irreducible. @@ -510,7 +203,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): type = [ ] degN = N.degree() while True: - d = self.gcd(NS) + d = self.right_gcd(NS) deg = d.degree()/degN if deg == 0: break @@ -549,8 +242,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef Py_ssize_t e = P.degree()/d cdef SkewPolynomial_finite_field_dense D if e == 1: - D = P._new_c(list(P.__coeffs),skew_ring) - D._inplace_rmonic() + D = P._new_c(list(P.__coeffs), skew_ring) + D = D.right_monic() return D E = N.parent().base_ring().extension(N,name='xr') @@ -569,13 +262,14 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): while 1: R = skew_ring.random_element((e*r-1,e*r-1)) - R._inplace_lmul(Q) + R = R*Q # R._inplace_lmul(Q) X = Q._new_c(Q.__coeffs[:],Q._parent) for j from 0 <= j < e: for i from 0 <= i < e: M.set_unsafe(i, j, E([skew_ring._retraction(X[t*r+i]) for t in range(d)])) - X._inplace_lmul(R) - X._inplace_rrem(NS) + X = (X*R) % NS + # X._inplace_lmul(R) + # X._inplace_rrem(NS) for i from 0 <= i < e: V.set_unsafe(i, 0, E([skew_ring._retraction(X[t*r+i]) for t in range(d)])) W = M._solve_right_nonsingular_square(V) @@ -589,20 +283,19 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): for i from 1 <= i < d: zz = (zz*zz) % xx yy += zz - dd = xx.gcd(yy) + dd = xx.right_gcd(yy) if dd.degree() != 1: continue else: yy = PE.gen().__pow__(exp,xx) - 1 - dd = xx.gcd(yy) + dd = xx.right_gcd(yy) if dd.degree() != 1: yy += 2 - dd = xx.gcd(yy) + dd = xx.right_gcd(yy) if dd.degree() != 1: continue - D = P._rgcd(R + skew_ring.center()((dd[0]/dd[1]).list())) + D = P.right_gcd(R + skew_ring.center()((dd[0]/dd[1]).list())) if D.degree() == 0: continue - D._inplace_rmonic() - D._init_cache() + D = D.right_monic() return D @@ -692,7 +385,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): total += 1 else: degn = n.degree() - P = self.gcd(skew_ring(n)) + P = self.right_gcd(skew_ring(n)) m = P.degree()/degn cardL = cardcenter**degn total += (cardL**m - 1)/(cardL - 1) @@ -796,7 +489,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): D = self._rdivisors[N] except (KeyError, TypeError): if N.is_irreducible(): - cP1 = self._rgcd(self._parent(N)) + cP1 = self.right_gcd(self._parent(N)) cN = N if cP1.degree() > 0: D = cP1._rdivisor_c(cN) @@ -812,28 +505,28 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): degN = N.degree() if right: if distribution == "uniform": - P1 = self._rgcd(NS) + P1 = self.right_gcd(NS) if P1.degree() != degN: Q1 = NS // P1 deg = P1.degree()-1 while True: R = Q1*skew_ring.random_element((deg,deg)) - if P1.gcd(R) == 1: + if P1.right_gcd(R) == 1: break - D = P1.gcd(D*R) + D = P1.right_gcd(D*R) return D else: - deg = NS.degree()-1 - P1 = self.lgcd(NS) + deg = NS.degree() - 1 + P1 = self.left_gcd(NS) while True: if distribution == "uniform": while True: R = skew_ring.random_element((deg,deg)) - if NS.gcd(R) == 1: + if NS.right_gcd(R) == 1: break - D = NS.gcd(D*R) + D = NS.right_gcd(D*R) Dp = NS // D - LDp = P1.gcd(Dp) + LDp = P1.right_gcd(Dp) LD = P1 // LDp if LD.degree() == degN: return LD @@ -930,30 +623,40 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): continue degN = N.degree() NS = skew_ring(N) - P = self.gcd(NS,right=right) + if right: + P = self.right_gcd(NS) + else: + P = self.left_gcd(NS) m = P.degree()/degN if m == 1: yield P continue degrandom = P.degree() - 1 - Q,_ = NS.quo_rem(P,right=right) - P1 = self.irreducible_divisor_with_norm(N,right=right) - Q1,_ = P.quo_rem(P1,right=right) + if right: + Q,_ = NS.right_quo_rem(P) + P1 = self.irreducible_divisor_with_norm(N,right=right) + Q1,_ = P.right_quo_rem(P1) + else: + Q,_ = NS.left_quo_rem(P) + P1 = self.irreducible_divisor_with_norm(N,right=right) + Q1,_ = P.left_quo_rem(P1) while True: R = skew_ring.random_element((degrandom,degrandom)) if right: - g = (R*Q).rem(P, right=False) + _, g = (R*Q).left_quo_rem(P) + if g.left_gcd(P) != 1: continue else: - g = (Q*R).rem(P) - if g.gcd(P, right=not right) != 1: continue + _, g = (R*Q).right_quo_rem(P) + if g.right_gcd(P) != 1: continue L = Q1 V = L for i in range(1,m): if right: - L = (g*L).gcd(P, right=False) + L = (g*L).left_gcd(P) + V = V.left_gcd(L) else: - L = (L*g).gcd(P) - V = V.gcd(L, right=not right) + L = (L*g).right_gcd(P) + V = V.right_gcd(L) if V == 1: break rng = xmrange_iter([kfixed]*degN,center) for i in range(m): @@ -963,10 +666,11 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): coeff = pol.pop() f = (g*f+coeff).rem(P, right=not right) if right: - d = (f*Q1).gcd(P, right=False) + d = (f*Q1).left_gcd(P) + d, _ = P.left_quo_rem(d) else: - d = (Q1*f).gcd(P) - d,_ = P.quo_rem(d, right=not right) + d = (Q1*f).right_gcd(P) + d, _ = P.right_quo_rem(d) yield d @@ -1029,7 +733,10 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if N == gencenter: continue degN = N.degree() - P = self.gcd(skew_ring(N), right=right) + if right: + P = self.right_gcd(skew_ring(N)) + else: + P = self.left_gcd(skew_ring(N)) m = P.degree()/degN cardL = cardcenter**degN count += (cardL**m - 1)/(cardL - 1) @@ -1044,12 +751,17 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): Compute a factorization of ``self`` """ cdef skew_ring = self._parent + cdef list a = (self)._coeffs + cdef Py_ssize_t val = 0 + if len(a) < 0: + return Factorization([], sort=False, unit=skew_ring.zero_element()) + while a[0].is_zero(): + del a[0] + val += 1 + cdef Py_ssize_t degQ, degrandom, m, mP, i cdef CenterSkewPolynomial_generic_dense N cdef SkewPolynomial_finite_field_dense poly = self.right_monic() - cdef val = poly._val_inplace_unit() - if val == -1: - return Factorization([], sort=False, unit=skew_ring.zero_element()) cdef list factors = [ (skew_ring.gen(), val) ] cdef SkewPolynomial_finite_field_dense P, Q, P1, NS, g, right, Pn cdef SkewPolynomial_finite_field_dense right2 = skew_ring(1) << val @@ -1068,27 +780,27 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): NS = skew_ring(N) P1 = None while 1: - P = poly._rgcd(NS) - P._inplace_rmonic() + P = poly.right_gcd(NS) + P = P.right_monic() mP = P.degree() / degN if mP == 0: break if mP == 1: factors.append((P,1)) - poly._inplace_rfloordiv(P) + poly = poly // P # poly._inplace_rfloordiv(P) for i from 1 <= i < m: if poly.degree() == degN: factors.append((poly,1)) break - P = poly._rgcd(NS) - P._inplace_rmonic() + P = poly.right_gcd(NS).right_monic() factors.append((P,1)) - poly._inplace_rfloordiv(P) + poly = poly // P break if P1 is None: P1 = P._rdivisor_c(N) Q = NS._new_c(NS.__coeffs[:], NS._parent) - Q._inplace_rfloordiv(P) - Q._inplace_lmul(P1) + Q = (Q // P) * P1 + # Q._inplace_rfloordiv(P) + # Q._inplace_lmul(P1) factors.append((P1,1)) right = P1._new_c(P1.__coeffs[:], P1._parent) m -= (mP-1) @@ -1096,17 +808,18 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): while mP > 2: while 1: g = skew_ring.random_element((degrandom,degrandom)) - g._inplace_lmul(Q) - g._inplace_rgcd(P) - Pn = right._coeff_llcm(g) - if len(Pn.__coeffs)-1 == degN: break - Pn._inplace_rmonic() + g = (g*Q).right_gcd(P) + #g._inplace_lmul(Q) + #g._inplace_rgcd(P) + Pn = right.left_lcm(g) + if Pn.degree() == degN: break + Pn = Pn.right_monic() factors.append((Pn,1)) - right._inplace_lmul(Pn) + right = right * Pn # right._inplace_lmul(Pn) degrandom -= degN mP -= 1 - poly._inplace_rfloordiv(right) - P1,_ = P.rquo_rem(right) + poly = poly // right # ._inplace_rfloordiv(right) + P1,_ = P.right_quo_rem(right) factors.reverse() return Factorization(factors, sort=False, unit=unit) @@ -1140,7 +853,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef RingElement unit = self.leading_coefficient() cdef SkewPolynomial_finite_field_dense left = self._new_c(self.__coeffs[:],skew_ring) - left._inplace_rmonic() + left = left.right_monic() cdef SkewPolynomial_finite_field_dense right = skew_ring(1) cdef SkewPolynomial_finite_field_dense L, R cdef SkewPolynomial_finite_field_dense NS, P, Q, D, D1, D2, d @@ -1156,12 +869,12 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): else: type = dict_type[N] NS = skew_ring(N) - P = left.gcd(NS) + P = left.right_gcd(NS) if type[0] == 1: D1 = P else: R = right._new_c(right.__coeffs[:],skew_ring) - R._inplace_rfloordiv(dict_right[N]) + R = R // dict_right[N] # R._inplace_rfloordiv(dict_right[N]) D = R._coeff_llcm(dict_divisor[N]) maxtype = list(type) maxtype[-1] -= 1 @@ -1173,46 +886,43 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): while 1: while 1: R = skew_ring.random_element((deg,deg)) - R._inplace_lmul(Q) - if P._rgcd(R).degree() == 0: + R = R*Q # R._inplace_lmul(Q) + if P.right_gcd(R).degree() == 0: break - D1 = P._rgcd(D*R) - D1._inplace_rmonic() + D1 = P.right_gcd(D*R).right_monic() # D1._inplace_rmonic() L = left._new_c(list(left.__coeffs),skew_ring) - L._inplace_rfloordiv(D1) + L = L // D1 # L._inplace_rfloordiv(D1) degN = N.degree() for j in range(len(type)): if type[j] == 1: newtype = type[:-1] break - d = L._rgcd(NS) - d._inplace_rmonic() + d = L.right_gcd(NS).right_monic() # d._inplace_rmonic() deg = d.degree() / degN if deg < type[j]: newtype = type[:] newtype[j] = deg break - L._inplace_rfloordiv(d) + L = L // d # L._inplace_rfloordiv(d) count = q_jordan(Partition(newtype),cardE) if ZZ.random_element(maxcount) < count: break dict_type[N] = newtype D2 = D._new_c(list(D.__coeffs),skew_ring) - D2._inplace_rmonic() + D2 = D2.right_monic() while D2 == D1: while 1: R = skew_ring.random_element((deg,deg)) - R._inplace_lmul(Q) - if P._rgcd(R).degree() == 0: + R = Q*R # R._inplace_lmul(Q) + if P.right_gcd(R).degree() == 0: break - D2 = P._rgcd(D*R) - D2._inplace_rmonic() - dict_divisor[N] = D1._coeff_llcm(D2) + D2 = P.right_gcd(D*R).right_monic() + dict_divisor[N] = D1.left_lcm(D2) factors.append((D1,1)) - left._inplace_rfloordiv(D1) - right._inplace_lmul(D1) + left = left // D1 # left_inplace_rfloordiv(D1) + right = right * D1 # right._inplace_lmul(D1) dict_right[N] = right._new_c(list(right.__coeffs),skew_ring) factors.reverse() From e6cdb1e1eb47552fa452afd3cc060e87ea175f93 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 7 Apr 2020 01:03:17 +0200 Subject: [PATCH 06/12] factor() works --- .../skew_polynomial_finite_field.pyx | 98 +++++++++---------- .../rings/polynomial/skew_polynomial_ring.py | 10 +- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index a9e5303af58..0b045dafd7e 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -242,12 +242,13 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef Py_ssize_t e = P.degree()/d cdef SkewPolynomial_finite_field_dense D if e == 1: - D = P._new_c(list(P.__coeffs), skew_ring) + D = P._new_c(list(P._coeffs), skew_ring) D = D.right_monic() return D - E = N.parent().base_ring().extension(N,name='xr') - PE = PolynomialRing(E,name='T') + Z = PolynomialRing(N.parent().base_ring(), name='xr') + E = Z.quo(Z(N.list())) + PE = PolynomialRing(E, name='T') cdef Integer exp if skew_ring.characteristic() != 2: exp = Integer((E.cardinality()-1)/2) @@ -260,16 +261,16 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef Py_ssize_t i, j, t, r = skew_ring._order cdef Polynomial dd, xx, yy, zz - while 1: + while True: R = skew_ring.random_element((e*r-1,e*r-1)) - R = R*Q # R._inplace_lmul(Q) - X = Q._new_c(Q.__coeffs[:],Q._parent) + R = Q*R + X = Q._new_c(Q._coeffs[:],Q._parent) for j from 0 <= j < e: for i from 0 <= i < e: - M.set_unsafe(i, j, E([skew_ring._retraction(X[t*r+i]) for t in range(d)])) - X = (X*R) % NS - # X._inplace_lmul(R) - # X._inplace_rrem(NS) + coeffs = [skew_ring._retraction(X[t*r+i]) for t in range(d)] + value = E(coeffs) + M.set_unsafe(i, j, value) + X = (R*X) % NS for i from 0 <= i < e: V.set_unsafe(i, 0, E([skew_ring._retraction(X[t*r+i]) for t in range(d)])) W = M._solve_right_nonsingular_square(V) @@ -283,14 +284,14 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): for i from 1 <= i < d: zz = (zz*zz) % xx yy += zz - dd = xx.right_gcd(yy) + dd = xx.gcd(yy) if dd.degree() != 1: continue else: yy = PE.gen().__pow__(exp,xx) - 1 - dd = xx.right_gcd(yy) + dd = xx.gcd(yy) if dd.degree() != 1: yy += 2 - dd = xx.right_gcd(yy) + dd = xx.gcd(yy) if dd.degree() != 1: continue D = P.right_gcd(R + skew_ring.center()((dd[0]/dd[1]).list())) if D.degree() == 0: @@ -399,10 +400,10 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): break else: N = self.reduced_norm_factor()[0][0] - return self.irreducible_divisor_with_norm(N,right=right,distribution=distribution) + return self.irreducible_divisor_with_norm(N, right=right, distribution=distribution) - def irreducible_divisor_with_norm(self,N,right=True,distribution=None): + def irreducible_divisor_with_norm(self, N, right=True, distribution=None): """ INPUT: @@ -770,56 +771,53 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef Py_ssize_t p = skew_ring.characteristic() cdef F = self.reduced_norm_factor() - for N,m in F: + for N, m in F: if N == gencenter: continue degN = N.degree() if poly.degree() == degN: - factors.append((poly,1)) + factors.append((poly, 1)) break NS = skew_ring(N) P1 = None - while 1: + while True: P = poly.right_gcd(NS) P = P.right_monic() mP = P.degree() / degN if mP == 0: break if mP == 1: factors.append((P,1)) - poly = poly // P # poly._inplace_rfloordiv(P) + poly = poly // P for i from 1 <= i < m: if poly.degree() == degN: factors.append((poly,1)) break P = poly.right_gcd(NS).right_monic() - factors.append((P,1)) + factors.append((P, 1)) poly = poly // P break if P1 is None: P1 = P._rdivisor_c(N) - Q = NS._new_c(NS.__coeffs[:], NS._parent) - Q = (Q // P) * P1 - # Q._inplace_rfloordiv(P) - # Q._inplace_lmul(P1) - factors.append((P1,1)) - right = P1._new_c(P1.__coeffs[:], P1._parent) + Q = NS._new_c(NS._coeffs[:], NS._parent) + Q = P1 * (Q // P) + factors.append((P1, 1)) + right = P1._new_c(P1._coeffs[:], P1._parent) m -= (mP-1) degrandom = P.degree() while mP > 2: - while 1: - g = skew_ring.random_element((degrandom,degrandom)) - g = (g*Q).right_gcd(P) - #g._inplace_lmul(Q) - #g._inplace_rgcd(P) + while True: + g = skew_ring.random_element((degrandom, degrandom)) + g = (Q*g).right_gcd(P) Pn = right.left_lcm(g) + Pn, _ = Pn.right_quo_rem(right) if Pn.degree() == degN: break Pn = Pn.right_monic() - factors.append((Pn,1)) - right = right * Pn # right._inplace_lmul(Pn) + factors.append((Pn, 1)) + right = Pn * right degrandom -= degN mP -= 1 - poly = poly // right # ._inplace_rfloordiv(right) - P1,_ = P.right_quo_rem(right) + poly = poly // right + P1, _ = P.right_quo_rem(right) factors.reverse() return Factorization(factors, sort=False, unit=unit) @@ -852,7 +850,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef list indices = list(Permutations(len(factorsN)).random_element()) cdef RingElement unit = self.leading_coefficient() - cdef SkewPolynomial_finite_field_dense left = self._new_c(self.__coeffs[:],skew_ring) + cdef SkewPolynomial_finite_field_dense left = self._new_c(self._coeffs[:],skew_ring) left = left.right_monic() cdef SkewPolynomial_finite_field_dense right = skew_ring(1) cdef SkewPolynomial_finite_field_dense L, R @@ -873,8 +871,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if type[0] == 1: D1 = P else: - R = right._new_c(right.__coeffs[:],skew_ring) - R = R // dict_right[N] # R._inplace_rfloordiv(dict_right[N]) + R = right._new_c(right._coeffs[:],skew_ring) + R = R // dict_right[N] D = R._coeff_llcm(dict_divisor[N]) maxtype = list(type) maxtype[-1] -= 1 @@ -886,44 +884,44 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): while 1: while 1: R = skew_ring.random_element((deg,deg)) - R = R*Q # R._inplace_lmul(Q) + R = Q*R if P.right_gcd(R).degree() == 0: break - D1 = P.right_gcd(D*R).right_monic() # D1._inplace_rmonic() + D1 = P.right_gcd(D*R).right_monic() - L = left._new_c(list(left.__coeffs),skew_ring) - L = L // D1 # L._inplace_rfloordiv(D1) + L = left._new_c(list(left._coeffs),skew_ring) + L = L // D1 degN = N.degree() for j in range(len(type)): if type[j] == 1: newtype = type[:-1] break - d = L.right_gcd(NS).right_monic() # d._inplace_rmonic() + d = L.right_gcd(NS).right_monic() deg = d.degree() / degN if deg < type[j]: newtype = type[:] newtype[j] = deg break - L = L // d # L._inplace_rfloordiv(d) + L = L // d count = q_jordan(Partition(newtype),cardE) if ZZ.random_element(maxcount) < count: break dict_type[N] = newtype - D2 = D._new_c(list(D.__coeffs),skew_ring) + D2 = D._new_c(list(D._coeffs),skew_ring) D2 = D2.right_monic() while D2 == D1: - while 1: + while True: R = skew_ring.random_element((deg,deg)) - R = Q*R # R._inplace_lmul(Q) + R = Q*R if P.right_gcd(R).degree() == 0: break D2 = P.right_gcd(D*R).right_monic() dict_divisor[N] = D1.left_lcm(D2) factors.append((D1,1)) - left = left // D1 # left_inplace_rfloordiv(D1) - right = right * D1 # right._inplace_lmul(D1) - dict_right[N] = right._new_c(list(right.__coeffs),skew_ring) + left = left // D1 + right = D1 * right + dict_right[N] = right._new_c(list(right._coeffs),skew_ring) factors.reverse() return Factorization(factors,sort=False,unit=unit) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index a04225ffea7..3883887bd73 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -1371,9 +1371,9 @@ def __init__ (self, skew_ring, names=None, sparse=False): self._latex_variable_name = skew_ring.latex_variable_names()[0] self._parenthesis = False else: - self._variable_name = skew_ring.variable_name() + "^" + str(order) - self._latex_variable_name = skew_ring.latex_variable_names()[0] + "^{" + str (order) + "}" - self._parenthesis = True + self._variable_name = skew_ring.variable_name() + str(order) + self._latex_variable_name = skew_ring.latex_variable_names()[0] + "_{" + str (order) + "}" + self._parenthesis = False else: self._pickling_variable_name = self._variable_name = Algebra.variable_name(self) self._latex_variable_name = Algebra.latex_variable_names(self)[0] @@ -1771,6 +1771,7 @@ def __init__(self, base_ring, twist_map, names, sparse, category=None): from sage.rings.polynomial.skew_polynomial_finite_field import SkewPolynomial_finite_field_dense self.Element = SkewPolynomial_finite_field_dense SkewPolynomialRing_finite_order.__init__(self, base_ring, twist_map, names, sparse, category) + self._matrix_retraction = None def _new_retraction_map(self, alea=None): """ @@ -1778,7 +1779,7 @@ def _new_retraction_map(self, alea=None): """ k = self.base_ring() base = k.base_ring() - (kfixed, embed) = self._maps[1].fixed_points() + (kfixed, embed) = self._maps[1].fixed_field() section = embed.section() if not kfixed.has_coerce_map_from(base): raise NotImplementedError("No coercion map from %s to %s" % (base, kfixed)) @@ -1795,6 +1796,7 @@ def _new_retraction_map(self, alea=None): tr += x elt *= k.gen() trace.append(section(tr)) + from sage.matrix.matrix_space import MatrixSpace self._matrix_retraction = MatrixSpace(kfixed, 1, k.degree())(trace) def _retraction(self, x, newmap=False, alea=None): From 86b0bdc9f346b3549028d72267f15af5b5c05b98 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 7 Apr 2020 15:35:13 +0200 Subject: [PATCH 07/12] factorize methods _left_lcm_cofactor and _right_lcm_cofactor --- .../polynomial/skew_polynomial_element.pyx | 100 +++++++++++++----- .../skew_polynomial_finite_field.pyx | 10 +- 2 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 28f07165ef2..679270913bf 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -1386,6 +1386,71 @@ cdef class SkewPolynomial(AlgebraElement): A = A.left_monic() return A + def _left_lcm_cofactor(self, other): + R = self._parent + U = R.one() + G = self + V1 = R.zero() + V3 = other + while not V3.is_zero(): + Q, R = G.right_quo_rem(V3) + T = U - Q*V1 + U = V1 + G = V3 + V1 = T + V3 = R + return V1 + + @coerce_binop + def left_xlcm(self, other, monic=True): + r""" + Return the left lcm of ``self`` and ``other`` together + with two skew polynomials `u` and `v` such that + `u \cdot \text{self} = v \cdot \text{other} = \text{llcm`` + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + if self.is_zero() or other.is_zero(): + raise ZeroDivisionError("division by zero is not valid") + V1 = self._left_lcm_cofactor(other) + L = V1 * self + if monic: + s = ~(L.leading_coefficient()) + L = s * L + V1 = s * V1 + return L, V1, L // other + + def _right_lcm_cofactor(self, other): + R = self._parent + U = R.one() + G = self + V1 = R.zero() + V3 = other + while not V3.is_zero(): + Q, R = G.left_quo_rem(V3) + T = U - V1*Q + U = V1 + G = V3 + V1 = T + V3 = R + return V1 + + @coerce_binop + def right_xlcm(self, other, monic=True): + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + if self.is_zero() or other.is_zero(): + raise ZeroDivisionError("division by zero is not valid") + V1 = self._right_lcm_cofactor(other) + L = self * V1 + if monic: + s = self._parent.twist_map(-self.degree())(~(L.leading_coefficient())) + L = L * s + V1 = V1 * s + W1, _ = L.left_quo_rem(other) + return L, V1, W1 + + @coerce_binop def left_lcm(self, other, monic=True): r""" @@ -1449,21 +1514,10 @@ cdef class SkewPolynomial(AlgebraElement): raise TypeError("the base ring must be a field") if self.is_zero() or other.is_zero(): raise ZeroDivisionError("division by zero is not valid") - U = self._parent.one() - G = self - V1 = self._parent.zero() - V3 = other - while not V3.is_zero(): - Q, R = G.right_quo_rem(V3) - T = U - Q*V1 - U = V1 - G = V3 - V1 = T - V3 = R - V1 = V1 * self + L = self._left_lcm_cofactor(other) * self if monic: - V1 = V1.right_monic() - return V1 + L = L.right_monic() + return L @coerce_binop def right_lcm(self, other, monic=True): @@ -1544,22 +1598,10 @@ cdef class SkewPolynomial(AlgebraElement): raise TypeError("the base ring must be a field") if self.is_zero() or other.is_zero(): raise ZeroDivisionError("division by zero is not valid") - R = self.parent() - U = R.one() - G = self - V1 = R.zero() - V3 = other - while not V3.is_zero(): - Q, R = G.left_quo_rem(V3) - T = U - V1*Q - U = V1 - G = V3 - V1 = T - V3 = R - V1 = self * V1 + L = self * self._right_lcm_cofactor(other) if monic: - V1 = V1.left_monic() - return V1 + L = L.left_monic() + return L def _repr_(self, name=None): r""" diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 0b045dafd7e..44ea8a9b3ce 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -752,7 +752,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): Compute a factorization of ``self`` """ cdef skew_ring = self._parent - cdef list a = (self)._coeffs + cdef SkewPolynomial_finite_field_dense poly = self.right_monic() + cdef list a = poly._coeffs cdef Py_ssize_t val = 0 if len(a) < 0: return Factorization([], sort=False, unit=skew_ring.zero_element()) @@ -762,10 +763,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef Py_ssize_t degQ, degrandom, m, mP, i cdef CenterSkewPolynomial_generic_dense N - cdef SkewPolynomial_finite_field_dense poly = self.right_monic() cdef list factors = [ (skew_ring.gen(), val) ] cdef SkewPolynomial_finite_field_dense P, Q, P1, NS, g, right, Pn - cdef SkewPolynomial_finite_field_dense right2 = skew_ring(1) << val cdef RingElement unit = self.leading_coefficient() cdef Polynomial gencenter = skew_ring.center().gen() cdef Py_ssize_t p = skew_ring.characteristic() @@ -808,8 +807,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): while True: g = skew_ring.random_element((degrandom, degrandom)) g = (Q*g).right_gcd(P) - Pn = right.left_lcm(g) - Pn, _ = Pn.right_quo_rem(right) + Pn = right._left_lcm_cofactor(g) if Pn.degree() == degN: break Pn = Pn.right_monic() factors.append((Pn, 1)) @@ -873,7 +871,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): else: R = right._new_c(right._coeffs[:],skew_ring) R = R // dict_right[N] - D = R._coeff_llcm(dict_divisor[N]) + D = R._left_lcm_cofactor(dict_divisor[N]) maxtype = list(type) maxtype[-1] -= 1 degN = N.degree() From 81228f59a62025ea5569ab77bfaaeb2fd523dc03 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 9 Apr 2020 08:55:35 +0200 Subject: [PATCH 08/12] remove CenterSkewPolynomial_generic_dense and duplicate methods --- .../skew_polynomial_finite_field.pxd | 3 +- .../skew_polynomial_finite_field.pyx | 120 +++--------------- 2 files changed, 21 insertions(+), 102 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd index 946023c6bdd..06a90f4f590 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -1,5 +1,4 @@ from sage.rings.polynomial.skew_polynomial_finite_order cimport SkewPolynomial_finite_order_dense -from sage.rings.polynomial.skew_polynomial_element cimport CenterSkewPolynomial_generic_dense from sage.matrix.matrix_dense cimport Matrix_dense cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_finite_order_dense): @@ -9,7 +8,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_finite_order_dense) cdef _factorization # Finding divisors - cdef SkewPolynomial_finite_field_dense _rdivisor_c(P, CenterSkewPolynomial_generic_dense N) + cdef SkewPolynomial_finite_field_dense _rdivisor_c(P, N) # Finding factorizations cdef _factor_c(self) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 44ea8a9b3ce..d4e77880aff 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -35,7 +35,6 @@ from sage.matrix.matrix_dense cimport Matrix_dense from sage.matrix.matrix_space import MatrixSpace from sage.rings.all import ZZ from sage.rings.polynomial.polynomial_element cimport Polynomial -from sage.rings.polynomial.skew_polynomial_element cimport CenterSkewPolynomial_generic_dense from sage.rings.integer cimport Integer from sage.structure.element cimport RingElement from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -225,7 +224,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): # Finding divisors # ---------------- - cdef SkewPolynomial_finite_field_dense _rdivisor_c(P, CenterSkewPolynomial_generic_dense N): + cdef SkewPolynomial_finite_field_dense _rdivisor_c(P, N): """ cython procedure computing an irreducible monic right divisor of `P` whose reduced norm is `N` @@ -246,8 +245,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): D = D.right_monic() return D - Z = PolynomialRing(N.parent().base_ring(), name='xr') - E = Z.quo(Z(N.list())) + center = N.parent() + E = center.quo(N) PE = PolynomialRing(E, name='T') cdef Integer exp if skew_ring.characteristic() != 2: @@ -293,7 +292,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): yy += 2 dd = xx.gcd(yy) if dd.degree() != 1: continue - D = P.right_gcd(R + skew_ring.center()((dd[0]/dd[1]).list())) + D = P.right_gcd(R + skew_ring(center((dd[0]/dd[1]).list()))) if D.degree() == 0: continue D = D.right_monic() @@ -467,7 +466,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): x + 2*t^2 + 4*t """ cdef SkewPolynomial_finite_field_dense cP1 - cdef CenterSkewPolynomial_generic_dense cN if self.is_zero(): raise "No irreducible divisor having given reduced norm" skew_ring = self._parent @@ -491,7 +489,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): except (KeyError, TypeError): if N.is_irreducible(): cP1 = self.right_gcd(self._parent(N)) - cN = N + cN = N if cP1.degree() > 0: D = cP1._rdivisor_c(cN) if self._rdivisors is None: @@ -762,7 +760,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): val += 1 cdef Py_ssize_t degQ, degrandom, m, mP, i - cdef CenterSkewPolynomial_generic_dense N + cdef N cdef list factors = [ (skew_ring.gen(), val) ] cdef SkewPolynomial_finite_field_dense P, Q, P1, NS, g, right, Pn cdef RingElement unit = self.leading_coefficient() @@ -826,14 +824,13 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): """ skew_ring = self._parent cdef Integer cardE, cardcenter = skew_ring.center().base_ring().cardinality() - cdef CenterSkewPolynomial_generic_dense gencenter = skew_ring.center().gen() + cdef gencenter = skew_ring.center().gen() cdef SkewPolynomial_finite_field_dense gen = skew_ring.gen() cdef list factorsN = [ ] cdef dict dict_divisor = { } cdef dict dict_type = { } cdef dict dict_right = { } - cdef CenterSkewPolynomial_generic_dense N cdef Py_ssize_t m cdef list type @@ -1055,37 +1052,9 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): count /= factorial(m) return count * factorial(summ) - def count_factorisations(self): - """ - Return the number of factorisations (as a product of a - unit and a product of irreducible monic factors) of this - skew polynomial. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^4 + (4*t + 3)*x^3 + t^2*x^2 + (4*t^2 + 3*t)*x + 3*t - sage: a.count_factorisations() - 216 - - We illustrate that an irreducible polynomial in the center have - in general a lot of distinct factorisations in the skew polynomial - ring:: - - sage: Z = S.center(); x3 = Z.gen() - sage: N = x3^5 + 4*x3^4 + 4*x3^2 + 4*x3 + 3; N - (x^3)^5 + 4*(x^3)^4 + 4*(x^3)^2 + 4*(x^3) + 3 - sage: N.is_irreducible() - True - sage: S(N).count_factorisations() - 30537115626 - """ - return self.count_factorizations() - - # Not optimized (many calls to reduced_norm, reduced_norm_factor,_rdivisor_c, which are slow) + # Not optimized: + # many calls to reduced_norm, reduced_norm_factor,_rdivisor_c, which are slow def factorizations(self): """ Return an iterator over all factorizations (as a product @@ -1132,65 +1101,16 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): """ if self.is_zero(): raise ValueError("factorization of 0 not defined") - unit = self.leading_coefficient() - poly = self.right_monic() - for factors in self._factorizations_rec(): + def factorizations_rec(P): + if P.is_irreducible(): + yield [ (P,1) ] + else: + for div in P._irreducible_divisors(True): + poly = self // div + # Here, we should update poly._norm, poly._norm_factor, poly._rdivisors + for factors in P._factorizations_rec(): + factors.append((div,1)) + yield factors + for factors in factorizations_rec(self): yield Factorization(factors,sort=False,unit=unit) - def _factorizations_rec(self): - if self.is_irreducible(): - yield [ (self,1) ] - else: - for div in self._irreducible_divisors(True): - poly = self // div - # Here, we should update poly._norm, poly._norm_factor, poly._rdivisors - for factors in poly._factorizations_rec(): - factors.append((div,1)) - yield factors - - - def factorisations(self): - """ - Return an iterator over all factorisations (as a product - of a unit and a product of irreducible monic factors) of - this skew polynomial. - - .. NOTE:: - - The algorithm is probabilistic. As a consequence, if - we execute two times with the same input we can get - the list of all factorizations in two differents orders. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^3 + (t^2 + 1)*x^2 + (2*t + 3)*x + t^2 + t + 2 - sage: iter = a.factorisations(); iter - - sage: iter.next() # random - (x + 3*t^2 + 4*t) * (x + 2*t^2) * (x + 4*t^2 + 4*t + 2) - sage: iter.next() # random - (x + 3*t^2 + 4*t) * (x + 3*t^2 + 2*t + 2) * (x + 4*t^2 + t + 2) - - We can use this function to build the list of factorizations - of `a`:: - - sage: factorisations = [ F for F in a.factorisations() ] - - We do some checks:: - - sage: len(factorisations) == a.count_factorisations() - True - sage: len(factorisations) == len(Set(factorisations)) # check no duplicates - True - sage: for F in factorisations: - ... if F.value() != a: - ... print "Found %s which is not a correct factorization" % d - ... continue - ... for d,_ in F: - ... if not d.is_irreducible(): - ... print "Found %s which is not a correct factorization" % d - """ - return self.factorizations() From e220d9666185c82245585e97b856f4b319a4c197 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 12 Apr 2020 08:42:39 +0200 Subject: [PATCH 09/12] clean some doctests --- .../skew_polynomial_finite_field.pyx | 101 ++++++++++-------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 1c80084f678..9c90af49cdf 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -224,13 +224,13 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef SkewPolynomial_finite_field_dense _rdivisor_c(self, N): """ - cython procedure computing an irreducible monic right divisor - of this skew polynomial whose reduced norm is `N` + Return a right divisor of this skew polynomial whose + reduced norm is `N` .. WARNING:: `N` needs to be an irreducible factor of the - reduced norm of `P`. This function does not check + reduced norm. This function does not check this (and his behaviour is not defined if the require property doesn't hold). """ @@ -297,6 +297,12 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def _reduced_norm_factor_uniform(self): + r""" + Return a factor of the reduced norm of this skew + polynomial, the probability of a given factor to + show up being proportional to the number of irreducible + divisors of ``self`` having this norm + """ skew_ring = self._parent F = self.reduced_norm_factor() center = F[0][0].parent() @@ -321,6 +327,27 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def _irreducible_divisor_with_norm(self, N, right, uniform): + r""" + Return an irreducible divisor of this skew polynomial + with norm `N` + + INPUT: + + - ``N`` -- an irreducible polynomial lying in the center + + - ``right`` -- a boolean; if ``True``, return a right divisor, + otherwise, return a left divisor + + - ``uniform`` -- a boolean; whether the output irreducible + divisor should be uniformly distributed among all possibilities + + .. WARNING:: + + `N` needs to be an irreducible factor of the + reduced norm. This function does not check + this (and his behaviour is not defined if the + require property doesn't hold). + """ skew_ring = self._parent NS = skew_ring(N) degN = N.degree() @@ -357,10 +384,16 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def _irreducible_divisors(self, right): """ Return an iterator over all irreducible monic - divisors of this skew polynomial. + divisors of this skew polynomial Do not use this function. Use instead - ``self.irreducible_divisors()``. + :meth:`right_irreducible_divisors` and + :meth:`left_irreducible_divisors`. + + INPUT: + + - ``right`` -- a boolean; if ``True``, return right divisors, + otherwise, return left divisors """ if self.is_zero(): return @@ -375,7 +408,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): gcd = SkewPolynomial_finite_field_dense.right_gcd def mul(a,b): return b*a skew_ring = self._parent - center = skew_ring.center() + center = skew_ring.center(coerce=False) kfixed = center.base_ring() F = self.reduced_norm_factor() for N,_ in F: @@ -423,24 +456,13 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def right_irreducible_divisor(self, uniform=False): """ - INPUT: + Return a right irreducible divisor of this skew polynomial - - ``side`` -- ``Left`` or ``Right`` (default: Right) - - - ``distribution`` -- None (default) or ``uniform`` - - - None: no particular specification - - - ``uniform``: the returned irreducible divisor is - uniformly distributed - - .. NOTE:: - - ``uniform`` is a little bit slower. - - OUTPUT: + INPUT: - - an irreducible monic ``side`` divisor of ``self`` + - ``uniform`` -- a boolean (default: ``False``); whether the + output irreducible divisor should be uniformly distributed + among all possibilities EXAMPLES:: @@ -449,37 +471,32 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: S. = k['x',Frob] sage: a = x^6 + 3*t*x^5 + (3*t + 1)*x^3 + (4*t^2 + 3*t + 4)*x^2 + (t^2 + 2)*x + 4*t^2 + 3*t + 3 - sage: dr = a.irreducible_divisor(); dr # random + sage: dr = a.right_irreducible_divisor(); dr # random x^3 + (2*t^2 + t + 4)*x^2 + (4*t + 1)*x + 4*t^2 + t + 1 - sage: a.is_divisible_by(dr) - True - - sage: dl = a.irreducible_divisor(side=Left); dl # random - x^3 + (2*t^2 + t + 1)*x^2 + (4*t^2 + 3*t + 3)*x + 4*t^2 + 2*t + 1 - sage: a.is_divisible_by(dl,side=Left) + sage: a.is_right_divisible_by(dr) True Right divisors are cached. Hence, if we ask again for a right divisor, we will get the same answer:: - sage: a.irreducible_divisor() # random + sage: a.right_irreducible_divisor() # random x^3 + (2*t^2 + t + 4)*x^2 + (4*t + 1)*x + 4*t^2 + t + 1 However the algorithm is probabilistic. Hence, if we first - reinitialiaze `a`, we may get a different answer:: + reinitialize `a`, we may get a different answer:: sage: a = x^6 + 3*t*x^5 + (3*t + 1)*x^3 + (4*t^2 + 3*t + 4)*x^2 + (t^2 + 2)*x + 4*t^2 + 3*t + 3 - sage: a.irreducible_divisor() # random + sage: a.right_irreducible_divisor() # random x^3 + (t^2 + 3*t + 4)*x^2 + (t + 2)*x + 4*t^2 + t + 1 We can also generate uniformly distributed irreducible monic divisors as follows:: - sage: a.irreducible_divisor(distribution="uniform") # random + sage: a.right_irreducible_divisor(distribution="uniform") # random x^3 + (4*t + 2)*x^2 + (2*t^2 + 2*t + 2)*x + 2*t^2 + 2 - sage: a.irreducible_divisor(distribution="uniform") # random + sage: a.right_irreducible_divisor(distribution="uniform") # random x^3 + (t^2 + 2)*x^2 + (3*t^2 + 1)*x + 4*t^2 + 2*t - sage: a.irreducible_divisor(distribution="uniform") # random + sage: a.right_irreducible_divisor(distribution="uniform") # random x^3 + x^2 + (4*t^2 + 2*t + 4)*x + t^2 + 3 By convention, the zero skew polynomial has no irreducible @@ -606,8 +623,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if self.is_zero(): return 0 skew_ring = self.parent() - cardcenter = skew_ring.center().base_ring().cardinality() - gencenter = skew_ring.center().gen() + cardcenter = skew_ring.center(coerce=False).base_ring().cardinality() + gencenter = skew_ring.center(coerce=False).gen() F = self.reduced_norm_factor() val = self.valuation() self >>= val @@ -647,7 +664,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef list factors = [ (skew_ring.gen(), val) ] cdef SkewPolynomial_finite_field_dense P, Q, P1, NS, g, right, Pn cdef RingElement unit = self.leading_coefficient() - cdef Polynomial gencenter = skew_ring.center().gen() + cdef Polynomial gencenter = skew_ring.center(coerce=False).gen() cdef Py_ssize_t p = skew_ring.characteristic() cdef F = self.reduced_norm_factor() @@ -706,8 +723,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): Compute a uniformly distrbuted factorization of ``self`` """ skew_ring = self._parent - cdef Integer cardE, cardcenter = skew_ring.center().base_ring().cardinality() - cdef gencenter = skew_ring.center().gen() + cdef Integer cardE, cardcenter = skew_ring.center(coerce=False).base_ring().cardinality() + cdef gencenter = skew_ring.center(coerce=False).gen() cdef SkewPolynomial_finite_field_dense gen = skew_ring.gen() cdef list factorsN = [ ] @@ -919,8 +936,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): """ if self.is_zero(): raise ValueError("factorization of 0 not defined") - cardcenter = self._parent.center().base_ring().cardinality() - gencenter = self._parent.center().gen() + cardcenter = self._parent.center(coerce=False).base_ring().cardinality() + gencenter = self._parent.center(coerce=False).gen() F = self.reduced_norm_factor() summ = 0 count = 1 From 1bcdf9a110a38d9b9f9c86e046506f6fbfc8c058 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 14 Apr 2020 14:15:27 +0200 Subject: [PATCH 10/12] use working_center --- .../skew_polynomial_finite_field.pyx | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 9c90af49cdf..1d4a4e0a9e7 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -48,7 +48,7 @@ from sage.combinat.q_analogues import q_jordan cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): - def reduced_norm_factor(self): + def _reduced_norm_factor(self): """ Return the reduced norm of this polynomial factorized in the centre. @@ -64,7 +64,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): ((x^3) + 3) * ((x^3) + 2)^2 """ if self._norm_factor is None: - self._norm_factor = self.reduced_norm().factor() + N = self._parent._working_center(self.reduced_norm(var=False)) + self._norm_factor = N.factor() return self._norm_factor def is_irreducible(self): @@ -109,7 +110,10 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: S(0).is_irreducible() False """ - return self.reduced_norm().is_irreducible() + if self._norm_factor is not None: + return len(self._norm_factor) == 1 and self._norm_factor[0][1] == 1 + N = self._parent._working_center(self.reduced_norm(var=False)) + return N.is_irreducible() def type(self,N): @@ -185,12 +189,14 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): ... ValueError: N is not irreducible """ + skew_ring = self._parent + if N.parent() is not skew_ring._working_center: + N = skew_ring._working_center(N) try: return self._types[N] except (KeyError, TypeError): if not N.is_irreducible(): raise ValueError("N is not irreducible") - skew_ring = self._parent if self._norm_factor is None: m = -1 else: @@ -304,7 +310,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): divisors of ``self`` having this norm """ skew_ring = self._parent - F = self.reduced_norm_factor() + F = self._reduced_norm_factor() center = F[0][0].parent() cardcenter = center.base_ring().cardinality() gencenter = center.gen() @@ -408,9 +414,9 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): gcd = SkewPolynomial_finite_field_dense.right_gcd def mul(a,b): return b*a skew_ring = self._parent - center = skew_ring.center(coerce=False) + center = skew_ring._working_center kfixed = center.base_ring() - F = self.reduced_norm_factor() + F = self._reduced_norm_factor() for N,_ in F: if N == center.gen(): yield skew_ring.gen() @@ -512,7 +518,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if uniform: N = self._reduced_norm_factor_uniform() else: - N = self.reduced_norm_factor()[0][0] + N = self._reduced_norm_factor()[0][0] return self._irreducible_divisor_with_norm(N, True, uniform) def left_irreducible_divisor(self, uniform=False): @@ -521,7 +527,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if uniform: N = self._reduced_norm_factor_uniform() else: - N = self.reduced_norm_factor()[0][0] + N = self._reduced_norm_factor()[0][0] return self._irreducible_divisor_with_norm(N, False, uniform) @@ -623,9 +629,9 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if self.is_zero(): return 0 skew_ring = self.parent() - cardcenter = skew_ring.center(coerce=False).base_ring().cardinality() - gencenter = skew_ring.center(coerce=False).gen() - F = self.reduced_norm_factor() + cardcenter = skew_ring._working_center.base_ring().cardinality() + gencenter = skew_ring._working_center.gen() + F = self._reduced_norm_factor() val = self.valuation() self >>= val count = 0 @@ -664,9 +670,9 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef list factors = [ (skew_ring.gen(), val) ] cdef SkewPolynomial_finite_field_dense P, Q, P1, NS, g, right, Pn cdef RingElement unit = self.leading_coefficient() - cdef Polynomial gencenter = skew_ring.center(coerce=False).gen() + cdef Polynomial gencenter = skew_ring._working_center.gen() cdef Py_ssize_t p = skew_ring.characteristic() - cdef F = self.reduced_norm_factor() + cdef F = self._reduced_norm_factor() for N, m in F: if N == gencenter: @@ -723,8 +729,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): Compute a uniformly distrbuted factorization of ``self`` """ skew_ring = self._parent - cdef Integer cardE, cardcenter = skew_ring.center(coerce=False).base_ring().cardinality() - cdef gencenter = skew_ring.center(coerce=False).gen() + cdef Integer cardE, cardcenter = skew_ring._working_center.base_ring().cardinality() + cdef gencenter = skew_ring._working_center.gen() cdef SkewPolynomial_finite_field_dense gen = skew_ring.gen() cdef list factorsN = [ ] @@ -734,7 +740,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef Py_ssize_t m cdef list type - for N,m in self.reduced_norm_factor(): + for N, m in self._reduced_norm_factor(): factorsN += m * [N] if N == gencenter: continue type = list(self.type(N)) @@ -936,12 +942,12 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): """ if self.is_zero(): raise ValueError("factorization of 0 not defined") - cardcenter = self._parent.center(coerce=False).base_ring().cardinality() - gencenter = self._parent.center(coerce=False).gen() - F = self.reduced_norm_factor() + cardcenter = self._parent._working_center.base_ring().cardinality() + gencenter = self._parent._working_center.gen() + F = self._reduced_norm_factor() summ = 0 count = 1 - for N,m in F: + for N, m in F: summ += m if m == 1: continue if N != gencenter: From 440ae61974a0575720290845d1ceb7b9de24e4cc Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 15 Apr 2020 15:06:19 +0200 Subject: [PATCH 11/12] fix doctest --- .../skew_polynomial_finite_field.pyx | 72 +++++++++---------- .../rings/polynomial/skew_polynomial_ring.py | 4 +- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 1d4a4e0a9e7..9d37e303449 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -48,20 +48,19 @@ from sage.combinat.q_analogues import q_jordan cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): - def _reduced_norm_factor(self): + def _reduced_norm_factored(self): """ - Return the reduced norm of this polynomial - factorized in the centre. + Return the reduced norm of this polynomial factorized in the center. EXAMPLES: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] + sage: S. = k['x', Frob] sage: a = (x^2 + 1) * (x+3) - sage: a.reduced_norm_factor() - ((x^3) + 3) * ((x^3) + 2)^2 + sage: a._reduced_norm_factored() + (z + 3) * (z + 2)^2 """ if self._norm_factor is None: N = self._parent._working_center(self.reduced_norm(var=False)) @@ -310,7 +309,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): divisors of ``self`` having this norm """ skew_ring = self._parent - F = self._reduced_norm_factor() + F = self._reduced_norm_factored() center = F[0][0].parent() cardcenter = center.base_ring().cardinality() gencenter = center.gen() @@ -416,7 +415,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): skew_ring = self._parent center = skew_ring._working_center kfixed = center.base_ring() - F = self._reduced_norm_factor() + F = self._reduced_norm_factored() for N,_ in F: if N == center.gen(): yield skew_ring.gen() @@ -498,17 +497,17 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): We can also generate uniformly distributed irreducible monic divisors as follows:: - sage: a.right_irreducible_divisor(distribution="uniform") # random + sage: a.right_irreducible_divisor(uniform=True) # random x^3 + (4*t + 2)*x^2 + (2*t^2 + 2*t + 2)*x + 2*t^2 + 2 - sage: a.right_irreducible_divisor(distribution="uniform") # random + sage: a.right_irreducible_divisor(uniform=True) # random x^3 + (t^2 + 2)*x^2 + (3*t^2 + 1)*x + 4*t^2 + 2*t - sage: a.right_irreducible_divisor(distribution="uniform") # random + sage: a.right_irreducible_divisor(uniform=True) # random x^3 + x^2 + (4*t^2 + 2*t + 4)*x + t^2 + 3 By convention, the zero skew polynomial has no irreducible divisor: - sage: S(0).irreducible_divisor() + sage: S(0).right_irreducible_divisor() Traceback (most recent call last): ... ValueError: 0 has no irreducible divisor @@ -518,7 +517,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if uniform: N = self._reduced_norm_factor_uniform() else: - N = self._reduced_norm_factor()[0][0] + N = self._reduced_norm_factored()[0][0] return self._irreducible_divisor_with_norm(N, True, uniform) def left_irreducible_divisor(self, uniform=False): @@ -527,7 +526,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if uniform: N = self._reduced_norm_factor_uniform() else: - N = self._reduced_norm_factor()[0][0] + N = self._reduced_norm_factored()[0][0] return self._irreducible_divisor_with_norm(N, False, uniform) @@ -542,7 +541,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] sage: a = x^4 + 2*t*x^3 + 3*t^2*x^2 + (t^2 + t + 1)*x + 4*t + 3 - sage: iter = a.irreducible_right_divisors(); iter + sage: iter = a.right_irreducible_divisors(); iter sage: next(iter) # random x + 2*t^2 + 4*t + 4 @@ -552,25 +551,23 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): We can use this function to build the list of all monic irreducible divisors of `a`:: - sage: rightdiv = [ d for d in a.irreducible_divisors() ] + sage: rightdiv = [ d for d in a.right_irreducible_divisors() ] We do some checks:: sage: len(rightdiv) == a.count_irreducible_divisors() True - sage: len(rightdiv) == len(Set(rightdiv)) # check no duplicates + sage: len(rightdiv) == Set(rightdiv).cardinality() # check no duplicates True sage: for d in rightdiv: - ... if not a.is_divisible_by(d): - ... print "Found %s which is not a right divisor" % d - ... elif not d.is_irreducible(): - ... print "Found %s which is not irreducible" % d + ....: assert a.is_right_divisible_by(d), "not right divisible" + ....: assert d.is_irreducible(), "not irreducible" Note that the algorithm is probabilistic. As a consequence, if we build again the list of right monic irreducible divisors of `a`, we may get a different ordering:: - sage: rightdiv2 = [ d for d in a.irreducible_divisors() ] + sage: rightdiv2 = [ d for d in a.right_irreducible_divisors() ] sage: rightdiv == rightdiv2 False sage: Set(rightdiv) == Set(rightdiv2) @@ -611,16 +608,14 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: a = x^4 + (4*t + 3)*x^3 + t^2*x^2 + (4*t^2 + 3*t)*x + 3*t sage: a.count_irreducible_divisors() 12 - sage: a.count_irreducible_divisors(side=Left) - 12 We illustrate that an irreducible polynomial in the center have in general a lot of irreducible divisors in the skew polynomial ring:: - sage: Z = S.center(); x3 = Z.gen() + sage: Z. = S.center() sage: N = x3^5 + 4*x3^4 + 4*x3^2 + 4*x3 + 3; N - (x^3)^5 + 4*(x^3)^4 + 4*(x^3)^2 + 4*(x^3) + 3 + x3^5 + 4*x3^4 + 4*x3^2 + 4*x3 + 3 sage: N.is_irreducible() True sage: S(N).count_irreducible_divisors() @@ -631,7 +626,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): skew_ring = self.parent() cardcenter = skew_ring._working_center.base_ring().cardinality() gencenter = skew_ring._working_center.gen() - F = self._reduced_norm_factor() + F = self._reduced_norm_factored() val = self.valuation() self >>= val count = 0 @@ -672,7 +667,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef RingElement unit = self.leading_coefficient() cdef Polynomial gencenter = skew_ring._working_center.gen() cdef Py_ssize_t p = skew_ring.characteristic() - cdef F = self._reduced_norm_factor() + cdef F = self._reduced_norm_factored() for N, m in F: if N == gencenter: @@ -740,7 +735,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef Py_ssize_t m cdef list type - for N, m in self._reduced_norm_factor(): + for N, m in self._reduced_norm_factored(): factorsN += m * [N] if N == gencenter: continue type = list(self.type(N)) @@ -885,11 +880,11 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): If we rather want uniform distribution among all factorizations, we need to specify it as follows:: - sage: a.factor(distribution="uniform") # random + sage: a.factor(uniform=True) # random (x + t^2 + 4) * (x + t) * (x + t + 3) - sage: a.factor(distribution="uniform") # random + sage: a.factor(uniform=True) # random (x + 2*t^2) * (x + t^2 + t + 1) * (x + t^2 + t + 2) - sage: a.factor(distribution="uniform") # random + sage: a.factor(uniform=True) # random (x + 2*t^2 + 3*t) * (x + 4*t + 2) * (x + 2*t + 2) By convention, the zero skew polynomial has no factorization: @@ -944,7 +939,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): raise ValueError("factorization of 0 not defined") cardcenter = self._parent._working_center.base_ring().cardinality() gencenter = self._parent._working_center.gen() - F = self._reduced_norm_factor() + F = self._reduced_norm_factored() summ = 0 count = 1 for N, m in F: @@ -992,15 +987,12 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: len(factorizations) == a.count_factorizations() True - sage: len(factorizations) == len(Set(factorizations)) # check no duplicates + sage: len(factorizations) == Set(factorizations).cardinality() # check no duplicates True sage: for F in factorizations: - ... if F.value() != a: - ... print "Found %s which is not a correct factorization" % d - ... continue - ... for d,_ in F: - ... if not d.is_irreducible(): - ... print "Found %s which is not a correct factorization" % d + ....: assert F.value() == a, "factorization has a different value" + ....: for d,_ in F: + ....: assert d.is_irreducible(), "a factor is not irreducible" """ if self.is_zero(): raise ValueError("factorization of 0 not defined") diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 7029c8302bf..37c553aeece 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -409,14 +409,14 @@ def __classcall_private__(cls, base_ring, twist_map=None, names=None, sparse=Fal sage: S is T True - When the twisting morphism has finite order, a special class + When the twisting morphism is a Frobenius over a finite field, a special class is used:: sage: k. = GF(7^5) sage: Frob = k.frobenius_endomorphism(2) sage: S. = SkewPolynomialRing(k, Frob) sage: type(S) - + """ if base_ring not in CommutativeRings(): raise TypeError('base_ring must be a commutative ring') From cccfef57254ce650f8e0c0dd7809edcfc1572fe8 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 15 Apr 2020 15:11:06 +0200 Subject: [PATCH 12/12] import correctly sig_on and sig_off --- src/sage/rings/polynomial/skew_polynomial_finite_field.pyx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 9d37e303449..e2f9f134d25 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -23,9 +23,7 @@ AUTHOR:: # http://www.gnu.org/licenses/ #**************************************************************************** -#from cysignals.signals cimport sig_on, sig_off -def sig_on(): pass -def sig_off(): pass +from cysignals.signals cimport sig_on, sig_off import copy import cysignals