def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, impl=None, proof=None, check_irreducible=True, prefix=None, repr=None, elem_cache=None, **kwds): """ EXAMPLES:: sage: GF.create_key_and_extra_args(9, 'a') ((9, ('a',), x^2 + 2*x + 2, 'givaro', 3, 2, True, None, 'poly', True), {}) The order `q` can also be given as a pair `(p,n)`:: sage: GF.create_key_and_extra_args((3, 2), 'a') ((9, ('a',), x^2 + 2*x + 2, 'givaro', 3, 2, True, None, 'poly', True), {}) We do not take invalid keyword arguments and raise a value error to better ensure uniqueness:: sage: GF.create_key_and_extra_args(9, 'a', foo='value') Traceback (most recent call last): ... TypeError: ...create_key_and_extra_args() got an unexpected keyword argument 'foo' Moreover, ``repr`` and ``elem_cache`` are ignored when not using givaro:: sage: GF.create_key_and_extra_args(16, 'a', impl='ntl', repr='poly') ((16, ('a',), x^4 + x + 1, 'ntl', 2, 4, True, None, None, None), {}) sage: GF.create_key_and_extra_args(16, 'a', impl='ntl', elem_cache=False) ((16, ('a',), x^4 + x + 1, 'ntl', 2, 4, True, None, None, None), {}) sage: GF(16, impl='ntl') is GF(16, impl='ntl', repr='foo') True We handle extra arguments for the givaro finite field and create unique objects for their defaults:: sage: GF(25, impl='givaro') is GF(25, impl='givaro', repr='poly') True sage: GF(25, impl='givaro') is GF(25, impl='givaro', elem_cache=True) True sage: GF(625, impl='givaro') is GF(625, impl='givaro', elem_cache=False) True We explicitly take ``structure``, ``implementation`` and ``prec`` attributes for compatibility with :class:`~sage.categories.pushout.AlgebraicExtensionFunctor` but we ignore them as they are not used, see :trac:`21433`:: sage: GF.create_key_and_extra_args(9, 'a', structure=None) ((9, ('a',), x^2 + 2*x + 2, 'givaro', 3, 2, True, None, 'poly', True), {}) TESTS:: sage: GF.create_key_and_extra_args((6, 1), 'a') Traceback (most recent call last): ... ValueError: the order of a finite field must be a prime power sage: GF.create_key_and_extra_args((9, 1), 'a') Traceback (most recent call last): ... ValueError: the order of a finite field must be a prime power sage: GF.create_key_and_extra_args((5, 0), 'a') Traceback (most recent call last): ... ValueError: the order of a finite field must be a prime power sage: GF.create_key_and_extra_args((3, 2, 1), 'a') Traceback (most recent call last): ... ValueError: wrong input for finite field constructor """ import sage.arith.all from sage.structure.proof.all import WithProof, arithmetic if proof is None: proof = arithmetic() for key, val in kwds.items(): if key not in [ 'structure', 'implementation', 'prec', 'embedding', 'latex_names' ]: raise TypeError( "create_key_and_extra_args() got an unexpected keyword argument '%s'" % key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): raise NotImplementedError( "ring extension with prescribed %s is not implemented" % key) with WithProof('arithmetic', proof): if isinstance(order, tuple): if len(order) != 2: raise ValueError( 'wrong input for finite field constructor') p, n = order p = Integer(p) if not p.is_prime() or n < 1: raise ValueError( "the order of a finite field must be a prime power") n = Integer(n) order = p**n else: order = Integer(order) if order <= 1: raise ValueError( "the order of a finite field must be at least 2") if order.is_prime(): p = order n = Integer(1) else: p, n = order.is_prime_power(get_data=True) if n == 0: raise ValueError( "the order of a finite field must be a prime power" ) # at this point, order = p**n if n == 1: if impl is None: impl = 'modn' name = ('x', ) # Ignore name # Every polynomial of degree 1 is irreducible check_irreducible = False else: if names is not None: name = names if name is None: if prefix is None: prefix = 'z' name = prefix + str(n) if modulus is not None: raise ValueError( "no modulus may be specified if variable name not given" ) # Fpbar will have a strong reference, since algebraic_closure caches its results, # and the coefficients of modulus lie in GF(p) Fpbar = GF(p).algebraic_closure(prefix) # This will give a Conway polynomial if p,n is small enough to be in the database # and a pseudo-Conway polynomial if it's not. modulus = Fpbar._get_polynomial(n) check_irreducible = False name = normalize_names(1, name) if impl is None: if order < zech_log_bound: impl = 'givaro' elif p == 2: impl = 'ntl' else: impl = 'pari_ffelt' # Determine modulus. # For the 'modn' implementation, we use the following # optimization which we also need to avoid an infinite loop: # a modulus of None is a shorthand for x-1. if modulus is not None or impl != 'modn': R = PolynomialRing(FiniteField(p), 'x') if modulus is None: modulus = R.irreducible_element(n) if isinstance(modulus, str): # A string specifies an algorithm to find a suitable modulus. if modulus != "random" and modulus in self._modulus_cache[ order]: modulus = self._modulus_cache[order][modulus] else: self._modulus_cache[order][ modulus] = modulus = R.irreducible_element( n, algorithm=modulus) else: if sage.rings.polynomial.polynomial_element.is_Polynomial( modulus): modulus = modulus.change_variable_name('x') modulus = R(modulus).monic() if modulus.degree() != n: raise ValueError( "the degree of the modulus does not equal the degree of the field" ) if check_irreducible and not modulus.is_irreducible(): raise ValueError( "finite field modulus must be irreducible but it is not" ) # If modulus is x - 1 for impl="modn", set it to None if impl == 'modn' and modulus[0] == -1: modulus = None # Check extra arguments for givaro and setup their defaults # TODO: ntl takes a repr, but ignores it if impl == 'givaro': if repr is None: repr = 'poly' if elem_cache is None: elem_cache = (order < 500) else: # This has the effect of ignoring these keywords repr = None elem_cache = None return (order, name, modulus, impl, p, n, proof, prefix, repr, elem_cache), {}
def product(self, left, right): """ Return the product of ``left`` and ``right``. - ``left``, ``right`` -- symmetric functions written in the monomial basis ``self``. OUTPUT: - the product of ``left`` and ``right``, expanded in the monomial basis, as a dictionary whose keys are partitions and whose values are the coefficients of these partitions (more precisely, their respective monomial symmetric functions) in the product. EXAMPLES:: sage: m = SymmetricFunctions(QQ).m() sage: a = m([2,1]) sage: a^2 4*m[2, 2, 1, 1] + 6*m[2, 2, 2] + 2*m[3, 2, 1] + 2*m[3, 3] + 2*m[4, 1, 1] + m[4, 2] :: sage: QQx.<x> = QQ['x'] sage: m = SymmetricFunctions(QQx).m() sage: a = m([2,1])+x sage: 2*a # indirect doctest 2*x*m[] + 2*m[2, 1] sage: a^2 x^2*m[] + 2*x*m[2, 1] + 4*m[2, 2, 1, 1] + 6*m[2, 2, 2] + 2*m[3, 2, 1] + 2*m[3, 3] + 2*m[4, 1, 1] + m[4, 2] """ # Use symmetrica to do the multiplication # A = left.parent() # Hack due to symmetrica crashing when both of the # partitions are the empty partition # if R is ZZ or R is QQ: # return symmetrica.mult_monomial_monomial(left, right) z_elt = {} for left_m, left_c in left._monomial_coefficients.items(): for right_m, right_c in right._monomial_coefficients.items(): # Hack due to symmetrica crashing when both of the # partitions are the empty partition if not left_m and not right_m: z_elt[left_m] = left_c * right_c continue d = symmetrica.mult_monomial_monomial({ left_m: Integer(1) }, { right_m: Integer(1) }).monomial_coefficients() for m in d: if m in z_elt: z_elt[m] += left_c * right_c * d[m] else: z_elt[m] = left_c * right_c * d[m] return self._from_dict(z_elt)
def list(self): """ Return a list of all the elements in this set of field homomorphisms. EXAMPLES:: sage: K.<a> = GF(25) sage: End(K) Automorphism group of Finite Field in a of size 5^2 sage: list(End(K)) [Ring endomorphism of Finite Field in a of size 5^2 Defn: a |--> 4*a + 1, Ring endomorphism of Finite Field in a of size 5^2 Defn: a |--> a] sage: L.<z> = GF(7^6) sage: [g for g in End(L) if (g^3)(z) == z] [Ring endomorphism of Finite Field in z of size 7^6 Defn: z |--> z, Ring endomorphism of Finite Field in z of size 7^6 Defn: z |--> 5*z^4 + 5*z^3 + 4*z^2 + 3*z + 1, Ring endomorphism of Finite Field in z of size 7^6 Defn: z |--> 3*z^5 + 5*z^4 + 5*z^2 + 2*z + 3] Between isomorphic fields with different moduli:: sage: k1 = GF(1009) sage: k2 = GF(1009, modulus="primitive") sage: Hom(k1, k2).list() [ Ring morphism: From: Finite Field of size 1009 To: Finite Field of size 1009 Defn: 1 |--> 1 ] sage: Hom(k2, k1).list() [ Ring morphism: From: Finite Field of size 1009 To: Finite Field of size 1009 Defn: 11 |--> 11 ] sage: k1.<a> = GF(1009^2, modulus="first_lexicographic") sage: k2.<b> = GF(1009^2, modulus="conway") sage: Hom(k1, k2).list() [ Ring morphism: From: Finite Field in a of size 1009^2 To: Finite Field in b of size 1009^2 Defn: a |--> 290*b + 864, Ring morphism: From: Finite Field in a of size 1009^2 To: Finite Field in b of size 1009^2 Defn: a |--> 719*b + 145 ] TESTS: Check that :trac:`11390` is fixed:: sage: K = GF(1<<16,'a'); L = GF(1<<32,'b') sage: K.Hom(L)[0] Ring morphism: From: Finite Field in a of size 2^16 To: Finite Field in b of size 2^32 Defn: a |--> b^29 + b^27 + b^26 + b^23 + b^21 + b^19 + b^18 + b^16 + b^14 + b^13 + b^11 + b^10 + b^9 + b^8 + b^7 + b^6 + b^5 + b^2 + b """ try: return self.__list except AttributeError: pass D = self.domain() C = self.codomain() if D.characteristic() == C.characteristic() and Integer( D.degree()).divides(Integer(C.degree())): f = D.modulus() g = C['x'](f) r = g.roots() v = [self(D.hom(a, C)) for a, _ in r] v = Sequence(v, immutable=True, cr=True) else: v = Sequence([], immutable=True, cr=False) self.__list = v return v
def supersingular_j(FF): r""" Return a supersingular j-invariant over the finite field FF. INPUT: - ``FF`` -- finite field with p^2 elements, where p is a prime number OUTPUT: - finite field element -- a supersingular j-invariant defined over the finite field FF EXAMPLES: The following examples calculate supersingular j-invariants for a few finite fields:: sage: supersingular_j(GF(7^2, 'a')) 6 Observe that in this example the j-invariant is not defined over the prime field:: sage: supersingular_j(GF(15073^2, 'a')) 4443*a + 13964 sage: supersingular_j(GF(83401^2, 'a')) 67977 AUTHORS: - David Kohel -- [email protected] - Iftikhar Burhanuddin -- [email protected] """ if not (FF.is_field()) or not (FF.is_finite()): raise ValueError("%s is not a finite field" % FF) prime = FF.characteristic() if not (Integer(prime).is_prime()): raise ValueError("%s is not a prime" % prime) if not (Integer(FF.cardinality())) == Integer(prime**2): raise ValueError("%s is not a quadratic extension" % FF) if kronecker(-1, prime) != 1: j_invss = 1728 # (2^2 * 3)^3 elif kronecker(-2, prime) != 1: j_invss = 8000 # (2^2 * 5)^3 elif kronecker(-3, prime) != 1: j_invss = 0 # 0^3 elif kronecker(-7, prime) != 1: j_invss = 16581375 # (3 * 5 * 17)^3 elif kronecker(-11, prime) != 1: j_invss = -32768 # -(2^5)^3 elif kronecker(-19, prime) != 1: j_invss = -884736 # -(2^5 * 3)^3 elif kronecker(-43, prime) != 1: j_invss = -884736000 # -(2^6 * 3 * 5)^3 elif kronecker(-67, prime) != 1: j_invss = -147197952000 # -(2^5 * 3 * 5 * 11)^3 elif kronecker(-163, prime) != 1: j_invss = -262537412640768000 # -(2^6 * 3 * 5 * 23 * 29)^3 else: D = supersingular_D(prime) hc_poly = FF['x'](pari(D).polclass()) root_hc_poly_list = list(hc_poly.roots()) j_invss = root_hc_poly_list[0][0] return FF(j_invss)
def pthpowers(self, p, Bound): """ Find the indices of proveably all pth powers in the recurrence sequence bounded by Bound. Let `u_n` be a binary recurrence sequence. A ``p`` th power in `u_n` is a solution to `u_n = y^p` for some integer `y`. There are only finitely many ``p`` th powers in any recurrence sequence [SS1983]_. INPUT: - ``p`` - a rational prime integer (the fixed p in `u_n = y^p`) - ``Bound`` - a natural number (the maximum index `n` in `u_n = y^p` that is checked). OUTPUT: - A list of the indices of all ``p`` th powers less bounded by ``Bound``. If the sequence is degenerate and there are many ``p`` th powers, raises ``ValueError``. EXAMPLES:: sage: R = BinaryRecurrenceSequence(1,1) #the Fibonacci sequence sage: R.pthpowers(2, 10**30) # long time (7 seconds) -- in fact these are all squares, c.f. [BMS2006]_ [0, 1, 2, 12] sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence sage: S.pthpowers(3,10**30) # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^30 [0, 1, 2] sage: Q = BinaryRecurrenceSequence(3,3,2,1) sage: Q.pthpowers(11,10**30) # long time (7.5 seconds) [1] If the sequence is degenerate, and there are no ``p`` th powers, returns `[]`. Otherwise, if there are many ``p`` th powers, raises ``ValueError``. :: sage: T = BinaryRecurrenceSequence(2,0,1,2) sage: T.is_degenerate() True sage: T.is_geometric() True sage: T.pthpowers(7,10**30) Traceback (most recent call last): ... ValueError: The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers. sage: L = BinaryRecurrenceSequence(4,0,2,2) sage: [L(i).factor() for i in range(10)] [2, 2, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15, 2^17] sage: L.is_quasigeometric() True sage: L.pthpowers(2,10**30) [] NOTE: This function is primarily optimized in the range where ``Bound`` is much larger than ``p``. """ #Thanks to Jesse Silliman for helpful conversations! #Reset the dictionary of good primes, as this depends on p self._PGoodness = {} #Starting lower bound on good primes self._ell = 1 #If the sequence is geometric, then the `n`th term is `a*r^n`. Thus the #property of being a ``p`` th power is periodic mod ``p``. So there are either #no ``p`` th powers if there are none in the first ``p`` terms, or many if there #is at least one in the first ``p`` terms. if self.is_geometric() or self.is_quasigeometric(): no_powers = True for i in range(1, 6 * p + 1): if _is_p_power(self(i), p): no_powers = False break if no_powers: if _is_p_power(self.u0, p): return [0] return [] else: raise ValueError( "The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers." ) #If the sequence is degenerate without being geometric or quasigeometric, there #may be many ``p`` th powers or no ``p`` th powers. elif (self.b**2 + 4 * self.c) == 0: #This is the case if the matrix F is not diagonalizable, ie b^2 +4c = 0, and alpha/beta = 1. alpha = self.b / 2 #In this case, u_n = u_0*alpha^n + (u_1 - u_0*alpha)*n*alpha^(n-1) = alpha^(n-1)*(u_0 +n*(u_1 - u_0*alpha)), #that is, it is a geometric term (alpha^(n-1)) times an arithmetic term (u_0 + n*(u_1-u_0*alpha)). #Look at classes n = k mod p, for k = 1,...,p. for k in range(1, p + 1): #The linear equation alpha^(k-1)*u_0 + (k+pm)*(alpha^(k-1)*u1 - u0*alpha^k) #must thus be a pth power. This is a linear equation in m, namely, A + B*m, where A = (alpha**(k - 1) * self.u0 + k * (alpha**(k - 1) * self.u1 - self.u0 * alpha**k)) B = p * (alpha**(k - 1) * self.u1 - self.u0 * alpha**k) #This linear equation represents a pth power iff A is a pth power mod B. if _is_p_power_mod(A, p, B): raise ValueError( "The degenerate binary recurrence sequence has many pth powers." ) return [] #We find ``p`` th powers using an elementary sieve. Term `u_n` is a ``p`` th #power if and only if it is a ``p`` th power modulo every prime `\\ell`. This condition #gives nontrivial information if ``p`` divides the order of the multiplicative group of #`\\Bold(F)_{\\ell}`, i.e. if `\\ell` is ` 1 \mod{p}`, as then only `1/p` terms are ``p`` th #powers modulo `\\ell``. #Thus, given such an `\\ell`, we get a set of necessary congruences for the index modulo the #the period of the sequence mod `\\ell`. Then we intersect these congruences for many primes #to get a tight list modulo a growing modulus. In order to keep this step manageable, we #only use primes `\\ell` that have particularly smooth periods. #Some congruences in the list will remain as the modulus grows. If a congruence remains through #7 rounds of increasing the modulus, then we check if this corresponds to a perfect power (if #it does, we add it to our list of indices corresponding to ``p`` th powers). The rest of the congruences #are transient and grow with the modulus. Once the smallest of these is greater than the bound, #the list of known indices corresponding to ``p`` th powers is complete. else: if Bound < 3 * p: powers = [] ell = p + 1 while not is_prime(ell): ell = ell + p F = GF(ell) a0 = F(self.u0) a1 = F(self.u1) #a0 and a1 are variables for terms in sequence bf, cf = F(self.b), F(self.c) for n in range(Bound): # n is the index of the a0 #Check whether a0 is a perfect power mod ell if _is_p_power_mod(a0, p, ell): #if a0 is a perfect power mod ell, check if nth term is ppower if _is_p_power(self(n), p): powers.append(n) a0, a1 = a1, bf * a1 + cf * a0 #step up the variables else: powers = [ ] #documents the indices of the sequence that provably correspond to pth powers cong = [ 0 ] #list of necessary congruences on the index for it to correspond to pth powers Possible_count = { } #keeps track of the number of rounds a congruence lasts in cong #These parameters are involved in how we choose primes to increase the modulus qqold = 1 #we believe that we know complete information coming from primes good by qqold M1 = 1 #we have congruences modulo M1, this may not be the tightest list M2 = p #we want to move to have congruences mod M2 qq = 1 #the largest prime power divisor of M1 is qq #This loop ups the modulus. while True: #Try to get good data mod M2 #patience of how long we should search for a "good prime" patience = 0.01 * _estimated_time( lcm(M2, p * next_prime_power(qq)), M1, len(cong), p) tries = 0 #This loop uses primes to get a small set of congruences mod M2. while True: #only proceed if took less than patience time to find the next good prime ell = _next_good_prime(p, self, qq, patience, qqold) if ell: #gather congruence data for the sequence mod ell, which will be mod period(ell) = modu cong1, modu = _find_cong1(p, self, ell) CongNew = [ ] #makes a new list from cong that is now mod M = lcm(M1, modu) instead of M1 M = lcm(M1, modu) for k in range(M // M1): for i in cong: CongNew.append(k * M1 + i) cong = set(CongNew) M1 = M killed_something = False #keeps track of when cong1 can rule out a congruence in cong #CRT by hand to gain speed for i in list(cong): if not ( i % modu in cong1 ): #congruence in cong is inconsistent with any in cong1 cong.remove(i) #remove that congruence killed_something = True if M1 == M2: if not killed_something: tries += 1 if tries == 2: #try twice to rule out congruences cong = list(cong) qqold = qq qq = next_prime_power(qq) M2 = lcm(M2, p * qq) break else: qq = next_prime_power(qq) M2 = lcm(M2, p * qq) cong = list(cong) break #Document how long each element of cong has been there for i in cong: if i in Possible_count: Possible_count[i] = Possible_count[i] + 1 else: Possible_count[i] = 1 #Check how long each element has persisted, if it is for at least 7 cycles, #then we check to see if it is actually a perfect power for i in Possible_count: if Possible_count[i] == 7: n = Integer(i) if n < Bound: if _is_p_power(self(n), p): powers.append(n) #check for a contradiction if len(cong) > len(powers): if cong[len(powers)] > Bound: break elif M1 > Bound: break return powers
def _element_constructor_(self, arg1, v=None, perm=None, autom=None, check=True): r""" Coerce ``arg1`` into this permutation group, if ``arg1`` is 0, then we will try to coerce ``(v, perm, autom)``. INPUT: - ``arg1`` (optional) -- either the integers 0, 1 or an element of ``self`` - ``v`` (optional) -- a vector of length ``self.degree()`` - ``perm`` (optional) -- a permutaton of degree ``self.degree()`` - ``autom`` (optional) -- an automorphism of the ring EXAMPLES:: sage: F.<a> = GF(9) sage: S = SemimonomialTransformationGroup(F, 4) sage: S(1) ((1, 1, 1, 1); (), Ring endomorphism of Finite Field in a of size 3^2 Defn: a |--> a) sage: g = S(v=[1,1,1,a]) sage: S(g) ((1, 1, 1, a); (), Ring endomorphism of Finite Field in a of size 3^2 Defn: a |--> a) sage: S(perm=Permutation('(1,2)(3,4)')) ((1, 1, 1, 1); (1,2)(3,4), Ring endomorphism of Finite Field in a of size 3^2 Defn: a |--> a) sage: S(autom=F.hom([a**3])) ((1, 1, 1, 1); (), Ring endomorphism of Finite Field in a of size 3^2 Defn: a |--> 2*a + 1) """ from sage.categories.homset import End R = self.base_ring() if arg1 == 0: if v is None: v = [R.one()] * self.degree() if perm is None: perm = Permutation(range(1, self.degree() + 1)) if autom is None: autom = R.hom(R.gens()) if check: try: v = [R(x) for x in v] except TypeError: raise TypeError('the vector attribute %s ' % v + 'should be iterable') if len(v) != self.degree(): raise ValueError('the length of the vector is %s,' % len(v) + ' should be %s' % self.degree()) if not all(x.parent() is R and x.is_unit() for x in v): raise ValueError('there is at least one element in the ' + 'list %s not lying in %s ' % (v, R) + 'or which is not invertible') try: perm = Permutation(perm) except TypeError: raise TypeError('the permutation attribute %s ' % perm + 'could not be converted to a permutation') if len(perm) != self.degree(): raise ValueError('the permutation length is %s,' % len(perm) + ' should be %s' % self.degree()) try: if autom.parent() != End(R): autom = End(R)(autom) except TypeError: raise TypeError('%s of type %s' % (autom, type(autom)) + ' is not coerceable to an automorphism') return self.Element(self, v, perm, autom) else: try: if arg1.parent() is self: return arg1 except AttributeError: pass try: from sage.rings.integer import Integer if Integer(arg1) == 1: return self() except TypeError: pass raise TypeError('the first argument must be an integer' + ' or an element of this group')
def _derivative_(self, n, z, m, diff_param): """ EXAMPLES:: sage: n,z,m = var('n,z,m') sage: elliptic_pi(n,z,m).diff(n) 1/4*(sqrt(-m*sin(z)^2 + 1)*n*sin(2*z)/(n*sin(z)^2 - 1) + 2*(m - n)*elliptic_f(z, m)/n + 2*(n^2 - m)*elliptic_pi(n, z, m)/n + 2*elliptic_e(z, m))/((m - n)*(n - 1)) sage: elliptic_pi(n,z,m).diff(z) -1/(sqrt(-m*sin(z)^2 + 1)*(n*sin(z)^2 - 1)) sage: elliptic_pi(n,z,m).diff(m) 1/4*(m*sin(2*z)/(sqrt(-m*sin(z)^2 + 1)*(m - 1)) - 2*elliptic_e(z, m)/(m - 1) - 2*elliptic_pi(n, z, m))/(m - n) """ if diff_param == 0: return ((Integer(1) / (Integer(2) * (m - n) * (n - Integer(1)))) * (elliptic_e(z, m) + ((m - n) / n) * elliptic_f(z, m) + ((n**Integer(2) - m) / n) * elliptic_pi(n, z, m) - (n * sqrt(Integer(1) - m * sin(z)**Integer(2)) * sin(Integer(2) * z)) / (Integer(2) * (Integer(1) - n * sin(z)**Integer(2))))) elif diff_param == 1: return (Integer(1) / (sqrt(Integer(1) - m * sin(z)**Integer(Integer(2))) * (Integer(1) - n * sin(z)**Integer(2)))) elif diff_param == 2: return ((Integer(1) / (Integer(2) * (n - m))) * ( elliptic_e(z, m) / (m - Integer(1)) + elliptic_pi(n, z, m) - (m * sin(Integer(2) * z)) / (Integer(2) * (m - Integer(1)) * sqrt(Integer(1) - m * sin(z)**Integer(2)))) )
def _is_a_splitting(S1, S2, n, return_automorphism=False): r""" Check wether ``(S1,S2)`` is a splitting of `\ZZ/n\ZZ`. A splitting of `R = \ZZ/n\ZZ` is a pair of subsets of `R` which is a partition of `R \\backslash \{0\}` and such that there exists an element `r` of `R` such that `r S_1 = S_2` and `r S_2 = S_1` (where `r S` is the point-wise multiplication of the elements of `S` by `r`). Splittings are useful for computing idempotents in the quotient ring `Q = GF(q)[x]/(x^n-1)`. INPUT: - ``S1, S2`` -- disjoint sublists partitioning ``[1, 2, ..., n-1]`` - ``n`` (integer) - ``return_automorphism`` (boolean) -- whether to return the automorphism exchanging `S_1` and `S_2`. OUTPUT: If ``return_automorphism is False`` (default) the function returns boolean values. Otherwise, it returns a pair ``(b, r)`` where ``b`` is a boolean indicating whether `S1`, `S2` is a splitting of `n`, and `r` is such that `r S_1 = S_2` and `r S_2 = S_1` (if `b` is ``False``, `r` is equal to ``None``). EXAMPLES:: sage: from sage.coding.code_constructions import _is_a_splitting sage: _is_a_splitting([1,2],[3,4],5) True sage: _is_a_splitting([1,2],[3,4],5,return_automorphism=True) (True, 4) sage: _is_a_splitting([1,3],[2,4,5,6],7) False sage: _is_a_splitting([1,3,4],[2,5,6],7) False sage: for P in SetPartitions(6,[3,3]): ....: res,aut= _is_a_splitting(P[0],P[1],7,return_automorphism=True) ....: if res: ....: print((aut, P[0], P[1])) (6, {1, 2, 3}, {4, 5, 6}) (3, {1, 2, 4}, {3, 5, 6}) (6, {1, 3, 5}, {2, 4, 6}) (6, {1, 4, 5}, {2, 3, 6}) We illustrate now how to find idempotents in quotient rings:: sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: _is_a_splitting(S1,S2,11) True sage: F = GF(q) sage: P.<x> = PolynomialRing(F,"x") sage: I = Ideal(P,[x^n-1]) sage: Q.<x> = QuotientRing(P,I) sage: i1 = -sum([x^i for i in S1]); i1 2*x^9 + 2*x^5 + 2*x^4 + 2*x^3 + 2*x sage: i2 = -sum([x^i for i in S2]); i2 2*x^10 + 2*x^8 + 2*x^7 + 2*x^6 + 2*x^2 sage: i1^2 == i1 True sage: i2^2 == i2 True sage: (1-i1)^2 == 1-i1 True sage: (1-i2)^2 == 1-i2 True We return to dealing with polynomials (rather than elements of quotient rings), so we can construct cyclic codes:: sage: P.<x> = PolynomialRing(F,"x") sage: i1 = -sum([x^i for i in S1]) sage: i2 = -sum([x^i for i in S2]) sage: i1_sqrd = (i1^2).quo_rem(x^n-1)[1] sage: i1_sqrd == i1 True sage: i2_sqrd = (i2^2).quo_rem(x^n-1)[1] sage: i2_sqrd == i2 True sage: C1 = codes.CyclicCode(length = n, generator_pol = gcd(i1, x^n - 1)) sage: C2 = codes.CyclicCode(length = n, generator_pol = gcd(1-i2, x^n - 1)) sage: C1.dual_code().systematic_generator_matrix() == C2.systematic_generator_matrix() True This is a special case of Theorem 6.4.3 in [HP2003]_. """ R = IntegerModRing(n) S1 = set(R(x) for x in S1) S2 = set(R(x) for x in S2) # we first check whether (S1,S2) is a partition of R - {0} if (len(S1) + len(S2) != n - 1 or len(S1) != len(S2) or R.zero() in S1 or R.zero() in S2 or not S1.isdisjoint(S2)): if return_automorphism: return False, None else: return False # now that we know that (S1,S2) is a partition, we look for an invertible # element b that maps S1 to S2 by multiplication for b in Integer(n).coprime_integers(n): if b != 1 and all(b * x in S2 for x in S1): if return_automorphism: return True, b else: return True if return_automorphism: return False, None else: return False
def PolynomialRing(base_ring, *args, **kwds): r""" Return the globally unique univariate or multivariate polynomial ring with given properties and variable name or names. There are many ways to specify the variables for the polynomial ring: 1. ``PolynomialRing(base_ring, name, ...)`` 2. ``PolynomialRing(base_ring, names, ...)`` 3. ``PolynomialRing(base_ring, n, names, ...)`` 4. ``PolynomialRing(base_ring, n, ..., var_array=var_array, ...)`` The ``...`` at the end of these commands stands for additional keywords, like ``sparse`` or ``order``. INPUT: - ``base_ring`` -- a ring - ``n`` -- an integer - ``name`` -- a string - ``names`` -- a list or tuple of names (strings), or a comma separated string - ``var_array`` -- a list or tuple of names, or a comma separated string - ``sparse`` -- bool: whether or not elements are sparse. The default is a dense representation (``sparse=False``) for univariate rings and a sparse representation (``sparse=True``) for multivariate rings. - ``order`` -- string or :class:`~sage.rings.polynomial.term_order.TermOrder` object, e.g., - ``'degrevlex'`` (default) -- degree reverse lexicographic - ``'lex'`` -- lexicographic - ``'deglex'`` -- degree lexicographic - ``TermOrder('deglex',3) + TermOrder('deglex',3)`` -- block ordering - ``implementation`` -- string or None; selects an implementation in cases where Sage includes multiple choices (currently `\ZZ[x]` can be implemented with ``'NTL'`` or ``'FLINT'``; default is ``'FLINT'``). For many base rings, the ``"singular"`` implementation is available. One can always specify ``implementation="generic"`` for a generic Sage implementation which does not use any specialized library. .. NOTE:: If the given implementation does not exist for rings with the given number of generators and the given sparsity, then an error results. OUTPUT: ``PolynomialRing(base_ring, name, sparse=False)`` returns a univariate polynomial ring; also, PolynomialRing(base_ring, names, sparse=False) yields a univariate polynomial ring, if names is a list or tuple providing exactly one name. All other input formats return a multivariate polynomial ring. UNIQUENESS and IMMUTABILITY: In Sage there is exactly one single-variate polynomial ring over each base ring in each choice of variable, sparseness, and implementation. There is also exactly one multivariate polynomial ring over each base ring for each choice of names of variables and term order. The names of the generators can only be temporarily changed after the ring has been created. Do this using the localvars context: EXAMPLES: **1. PolynomialRing(base_ring, name, ...)** :: sage: PolynomialRing(QQ, 'w') Univariate Polynomial Ring in w over Rational Field sage: PolynomialRing(QQ, name='w') Univariate Polynomial Ring in w over Rational Field Use the diamond brackets notation to make the variable ready for use after you define the ring:: sage: R.<w> = PolynomialRing(QQ) sage: (1 + w)^3 w^3 + 3*w^2 + 3*w + 1 You must specify a name:: sage: PolynomialRing(QQ) Traceback (most recent call last): ... TypeError: you must specify the names of the variables sage: R.<abc> = PolynomialRing(QQ, sparse=True); R Sparse Univariate Polynomial Ring in abc over Rational Field sage: R.<w> = PolynomialRing(PolynomialRing(GF(7),'k')); R Univariate Polynomial Ring in w over Univariate Polynomial Ring in k over Finite Field of size 7 The square bracket notation:: sage: R.<y> = QQ['y']; R Univariate Polynomial Ring in y over Rational Field sage: y^2 + y y^2 + y In fact, since the diamond brackets on the left determine the variable name, you can omit the variable from the square brackets:: sage: R.<zz> = QQ[]; R Univariate Polynomial Ring in zz over Rational Field sage: (zz + 1)^2 zz^2 + 2*zz + 1 This is exactly the same ring as what PolynomialRing returns:: sage: R is PolynomialRing(QQ,'zz') True However, rings with different variables are different:: sage: QQ['x'] == QQ['y'] False Sage has two implementations of univariate polynomials over the integers, one based on NTL and one based on FLINT. The default is FLINT. Note that FLINT uses a "more dense" representation for its polynomials than NTL, so in particular, creating a polynomial like 2^1000000 * x^1000000 in FLINT may be unwise. :: sage: ZxNTL = PolynomialRing(ZZ, 'x', implementation='NTL'); ZxNTL Univariate Polynomial Ring in x over Integer Ring (using NTL) sage: ZxFLINT = PolynomialRing(ZZ, 'x', implementation='FLINT'); ZxFLINT Univariate Polynomial Ring in x over Integer Ring sage: ZxFLINT is ZZ['x'] True sage: ZxFLINT is PolynomialRing(ZZ, 'x') True sage: xNTL = ZxNTL.gen() sage: xFLINT = ZxFLINT.gen() sage: xNTL.parent() Univariate Polynomial Ring in x over Integer Ring (using NTL) sage: xFLINT.parent() Univariate Polynomial Ring in x over Integer Ring There is a coercion from the non-default to the default implementation, so the values can be mixed in a single expression:: sage: (xNTL + xFLINT^2) x^2 + x The result of such an expression will use the default, i.e., the FLINT implementation:: sage: (xNTL + xFLINT^2).parent() Univariate Polynomial Ring in x over Integer Ring The generic implementation uses neither NTL nor FLINT:: sage: Zx = PolynomialRing(ZZ, 'x', implementation='generic'); Zx Univariate Polynomial Ring in x over Integer Ring sage: Zx.element_class <... 'sage.rings.polynomial.polynomial_element.Polynomial_generic_dense'> **2. PolynomialRing(base_ring, names, ...)** :: sage: R = PolynomialRing(QQ, 'a,b,c'); R Multivariate Polynomial Ring in a, b, c over Rational Field sage: S = PolynomialRing(QQ, ['a','b','c']); S Multivariate Polynomial Ring in a, b, c over Rational Field sage: T = PolynomialRing(QQ, ('a','b','c')); T Multivariate Polynomial Ring in a, b, c over Rational Field All three rings are identical:: sage: R is S True sage: S is T True There is a unique polynomial ring with each term order:: sage: R = PolynomialRing(QQ, 'x,y,z', order='degrevlex'); R Multivariate Polynomial Ring in x, y, z over Rational Field sage: S = PolynomialRing(QQ, 'x,y,z', order='invlex'); S Multivariate Polynomial Ring in x, y, z over Rational Field sage: S is PolynomialRing(QQ, 'x,y,z', order='invlex') True sage: R == S False Note that a univariate polynomial ring is returned, if the list of names is of length one. If it is of length zero, a multivariate polynomial ring with no variables is returned. :: sage: PolynomialRing(QQ,["x"]) Univariate Polynomial Ring in x over Rational Field sage: PolynomialRing(QQ,[]) Multivariate Polynomial Ring in no variables over Rational Field The Singular implementation always returns a multivariate ring, even for 1 variable:: sage: PolynomialRing(QQ, "x", implementation="singular") Multivariate Polynomial Ring in x over Rational Field sage: P.<x> = PolynomialRing(QQ, implementation="singular"); P Multivariate Polynomial Ring in x over Rational Field **3. PolynomialRing(base_ring, n, names, ...)** (where the arguments ``n`` and ``names`` may be reversed) If you specify a single name as a string and a number of variables, then variables labeled with numbers are created. :: sage: PolynomialRing(QQ, 'x', 10) Multivariate Polynomial Ring in x0, x1, x2, x3, x4, x5, x6, x7, x8, x9 over Rational Field sage: PolynomialRing(QQ, 2, 'alpha0') Multivariate Polynomial Ring in alpha00, alpha01 over Rational Field sage: PolynomialRing(GF(7), 'y', 5) Multivariate Polynomial Ring in y0, y1, y2, y3, y4 over Finite Field of size 7 sage: PolynomialRing(QQ, 'y', 3, sparse=True) Multivariate Polynomial Ring in y0, y1, y2 over Rational Field Note that a multivariate polynomial ring is returned when an explicit number is given. :: sage: PolynomialRing(QQ,"x",1) Multivariate Polynomial Ring in x over Rational Field sage: PolynomialRing(QQ,"x",0) Multivariate Polynomial Ring in no variables over Rational Field It is easy in Python to create fairly arbitrary variable names. For example, here is a ring with generators labeled by the primes less than 100:: sage: R = PolynomialRing(ZZ, ['x%s'%p for p in primes(100)]); R Multivariate Polynomial Ring in x2, x3, x5, x7, x11, x13, x17, x19, x23, x29, x31, x37, x41, x43, x47, x53, x59, x61, x67, x71, x73, x79, x83, x89, x97 over Integer Ring By calling the :meth:`~sage.structure.category_object.CategoryObject.inject_variables` method, all those variable names are available for interactive use:: sage: R.inject_variables() Defining x2, x3, x5, x7, x11, x13, x17, x19, x23, x29, x31, x37, x41, x43, x47, x53, x59, x61, x67, x71, x73, x79, x83, x89, x97 sage: (x2 + x41 + x71)^2 x2^2 + 2*x2*x41 + x41^2 + 2*x2*x71 + 2*x41*x71 + x71^2 **4. PolynomialRing(base_ring, n, ..., var_array=var_array, ...)** This creates an array of variables where each variables begins with an entry in ``var_array`` and is indexed from 0 to `n-1`. :: sage: PolynomialRing(ZZ, 3, var_array=['x','y']) Multivariate Polynomial Ring in x0, y0, x1, y1, x2, y2 over Integer Ring sage: PolynomialRing(ZZ, 3, var_array='a,b') Multivariate Polynomial Ring in a0, b0, a1, b1, a2, b2 over Integer Ring It is possible to create higher-dimensional arrays:: sage: PolynomialRing(ZZ, 2, 3, var_array=('p', 'q')) Multivariate Polynomial Ring in p00, q00, p01, q01, p02, q02, p10, q10, p11, q11, p12, q12 over Integer Ring sage: PolynomialRing(ZZ, 2, 3, 4, var_array='m') Multivariate Polynomial Ring in m000, m001, m002, m003, m010, m011, m012, m013, m020, m021, m022, m023, m100, m101, m102, m103, m110, m111, m112, m113, m120, m121, m122, m123 over Integer Ring The array is always at least 2-dimensional. So, if ``var_array`` is a single string and only a single number `n` is given, this creates an `n \times n` array of variables:: sage: PolynomialRing(ZZ, 2, var_array='m') Multivariate Polynomial Ring in m00, m01, m10, m11 over Integer Ring **Square brackets notation** You can alternatively create a polynomial ring over a ring `R` with square brackets:: sage: RR["x"] Univariate Polynomial Ring in x over Real Field with 53 bits of precision sage: RR["x,y"] Multivariate Polynomial Ring in x, y over Real Field with 53 bits of precision sage: P.<x,y> = RR[]; P Multivariate Polynomial Ring in x, y over Real Field with 53 bits of precision This notation does not allow to set any of the optional arguments. **Changing variable names** Consider :: sage: R.<x,y> = PolynomialRing(QQ,2); R Multivariate Polynomial Ring in x, y over Rational Field sage: f = x^2 - 2*y^2 You can't just globally change the names of those variables. This is because objects all over Sage could have pointers to that polynomial ring. :: sage: R._assign_names(['z','w']) Traceback (most recent call last): ... ValueError: variable names cannot be changed after object creation. However, you can very easily change the names within a ``with`` block:: sage: with localvars(R, ['z','w']): ....: print(f) z^2 - 2*w^2 After the ``with`` block the names revert to what they were before:: sage: print(f) x^2 - 2*y^2 TESTS: We test here some changes introduced in :trac:`9944`. If there is no dense implementation for the given number of variables, then requesting a dense ring is an error:: sage: S.<x,y> = PolynomialRing(QQ, sparse=False) Traceback (most recent call last): ... NotImplementedError: a dense representation of multivariate polynomials is not supported Check uniqueness if the same implementation is used for different values of the ``"implementation"`` keyword:: sage: R = PolynomialRing(QQbar, 'j', implementation="generic") sage: S = PolynomialRing(QQbar, 'j', implementation=None) sage: R is S True sage: R = PolynomialRing(ZZ['t'], 'j', implementation="generic") sage: S = PolynomialRing(ZZ['t'], 'j', implementation=None) sage: R is S True sage: R = PolynomialRing(QQbar, 'j,k', implementation="generic") sage: S = PolynomialRing(QQbar, 'j,k', implementation=None) sage: R is S True sage: R = PolynomialRing(ZZ, 'j,k', implementation="singular") sage: S = PolynomialRing(ZZ, 'j,k', implementation=None) sage: R is S True sage: R = PolynomialRing(ZZ, 'p', sparse=True, implementation="generic") sage: S = PolynomialRing(ZZ, 'p', sparse=True) sage: R is S True The generic implementation is different in some cases:: sage: R = PolynomialRing(GF(2), 'j', implementation="generic"); type(R) <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field_with_category'> sage: S = PolynomialRing(GF(2), 'j'); type(S) <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_dense_mod_p_with_category'> sage: R = PolynomialRing(ZZ, 'x,y', implementation="generic"); type(R) <class 'sage.rings.polynomial.multi_polynomial_ring.MPolynomialRing_polydict_domain_with_category'> sage: S = PolynomialRing(ZZ, 'x,y'); type(S) <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomialRing_libsingular'> Sparse univariate polynomials only support a generic implementation:: sage: R = PolynomialRing(ZZ, 'j', sparse=True); type(R) <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_integral_domain_with_category'> sage: R = PolynomialRing(GF(49), 'j', sparse=True); type(R) <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field_with_category'> If the requested implementation is not known or not supported for the given arguments, then an error results:: sage: R.<x0> = PolynomialRing(ZZ, implementation='Foo') Traceback (most recent call last): ... ValueError: unknown implementation 'Foo' for dense polynomial rings over Integer Ring sage: R.<x0> = PolynomialRing(GF(2), implementation='GF2X', sparse=True) Traceback (most recent call last): ... ValueError: unknown implementation 'GF2X' for sparse polynomial rings over Finite Field of size 2 sage: R.<x,y> = PolynomialRing(ZZ, implementation='FLINT') Traceback (most recent call last): ... ValueError: unknown implementation 'FLINT' for multivariate polynomial rings sage: R.<x> = PolynomialRing(QQbar, implementation="whatever") Traceback (most recent call last): ... ValueError: unknown implementation 'whatever' for dense polynomial rings over Algebraic Field sage: R.<x> = PolynomialRing(ZZ['t'], implementation="whatever") Traceback (most recent call last): ... ValueError: unknown implementation 'whatever' for dense polynomial rings over Univariate Polynomial Ring in t over Integer Ring sage: PolynomialRing(RR, "x,y", implementation="whatever") Traceback (most recent call last): ... ValueError: unknown implementation 'whatever' for multivariate polynomial rings sage: PolynomialRing(RR, name="x", implementation="singular") Traceback (most recent call last): ... NotImplementedError: polynomials over Real Field with 53 bits of precision are not supported in Singular The following corner case used to result in a warning message from ``libSingular``, and the generators of the resulting polynomial ring were not zero:: sage: R = Integers(1)['x','y'] sage: R.0 == 0 True We verify that :trac:`13187` is fixed:: sage: var('t') t sage: PolynomialRing(ZZ, name=t) == PolynomialRing(ZZ, name='t') True We verify that polynomials with interval coefficients from :trac:`7712` and :trac:`13760` are fixed:: sage: P.<y,z> = PolynomialRing(RealIntervalField(2)) sage: Q.<x> = PolynomialRing(P) sage: C = (y-x)^3 sage: C(y/2) 1.?*y^3 sage: R.<x,y> = PolynomialRing(RIF,2) sage: RIF(-2,1)*x 0.?e1*x For historical reasons, we allow redundant variable names with the angle bracket notation. The names must be consistent though! :: sage: P.<x,y> = PolynomialRing(ZZ, "x,y"); P Multivariate Polynomial Ring in x, y over Integer Ring sage: P.<x,y> = ZZ["x,y"]; P Multivariate Polynomial Ring in x, y over Integer Ring sage: P.<x,y> = PolynomialRing(ZZ, 2, "x"); P Traceback (most recent call last): ... TypeError: variable names specified twice inconsistently: ('x0', 'x1') and ('x', 'y') We test a lot of invalid input:: sage: PolynomialRing(4) Traceback (most recent call last): ... TypeError: base_ring 4 must be a ring sage: PolynomialRing(QQ, -1) Traceback (most recent call last): ... ValueError: number of variables must be non-negative sage: PolynomialRing(QQ, 1) Traceback (most recent call last): ... TypeError: you must specify the names of the variables sage: PolynomialRing(QQ, "x", None) Traceback (most recent call last): ... TypeError: invalid arguments ('x', None) for PolynomialRing sage: PolynomialRing(QQ, "x", "y") Traceback (most recent call last): ... TypeError: variable names specified twice: 'x' and 'y' sage: PolynomialRing(QQ, 1, "x", 2) Traceback (most recent call last): ... TypeError: number of variables specified twice: 1 and 2 sage: PolynomialRing(QQ, "x", names="x") Traceback (most recent call last): ... TypeError: variable names specified twice inconsistently: ('x',) and 'x' sage: PolynomialRing(QQ, name="x", names="x") Traceback (most recent call last): ... TypeError: keyword argument 'name' cannot be combined with 'names' sage: PolynomialRing(QQ, var_array='x') Traceback (most recent call last): ... TypeError: you must specify the number of the variables sage: PolynomialRing(QQ, 2, 'x', var_array='x') Traceback (most recent call last): ... TypeError: unable to convert 'x' to an integer """ if not ring.is_Ring(base_ring): raise TypeError("base_ring {!r} must be a ring".format(base_ring)) n = -1 # Unknown number of variables names = None # Unknown variable names # Use a single-variate ring by default unless the "singular" # implementation is asked. multivariate = kwds.get("implementation") == "singular" # Check specifically for None because it is an easy mistake to # make and Integer(None) returns 0, so we wouldn't catch this # otherwise. if any(arg is None for arg in args): raise TypeError( "invalid arguments {!r} for PolynomialRing".format(args)) if "var_array" in kwds: for forbidden in "name", "names": if forbidden in kwds: raise TypeError( "keyword argument '%s' cannot be combined with 'var_array'" % forbidden) names = kwds.pop("var_array") if isinstance(names, (tuple, list)): # Input is a 1-dimensional array dim = 1 else: # Input is a 0-dimensional (if a single string was given) # or a 1-dimensional array names = normalize_names(-1, names) dim = len(names) > 1 multivariate = True if not args: raise TypeError("you must specify the number of the variables") # The total dimension must be at least 2 if len(args) == 1 and not dim: args = [args[0], args[0]] # All arguments in *args should be a number of variables suffixes = [""] for arg in args: k = Integer(arg) if k < 0: raise ValueError("number of variables must be non-negative") suffixes = [s + str(i) for s in suffixes for i in range(k)] names = [v + s for s in suffixes for v in names] else: # No "var_array" keyword if "name" in kwds: if "names" in kwds: raise TypeError( "keyword argument 'name' cannot be combined with 'names'") names = [kwds.pop("name")] # Interpret remaining arguments in *args as either a number of # variables or as variable names for arg in args: try: k = Integer(arg) except TypeError: # Interpret arg as names if names is not None: raise TypeError( "variable names specified twice: %r and %r" % (names, arg)) names = arg else: # Interpret arg as number of variables if n >= 0: raise TypeError( "number of variables specified twice: %r and %r" % (n, arg)) if k < 0: raise ValueError( "number of variables must be non-negative") n = k # If number of variables was explicitly given, always # return a multivariate ring multivariate = True if names is None: try: names = kwds.pop("names") except KeyError: raise TypeError("you must specify the names of the variables") names = normalize_names(n, names) # At this point, we have only handled the "names" keyword if it was # needed. Since we know the variable names, it would logically be # an error to specify an additional "names" keyword. However, # people often abuse the preparser with # R.<x> = PolynomialRing(QQ, 'x') # and we allow this for historical reasons. However, the names # must be consistent! if "names" in kwds: kwnames = kwds.pop("names") if kwnames != names: raise TypeError( "variable names specified twice inconsistently: %r and %r" % (names, kwnames)) if multivariate or len(names) != 1: return _multi_variate(base_ring, names, **kwds) else: return _single_variate(base_ring, names, **kwds)
def build_alphabet(data=None, names=None, name=None): r""" Return an object representing an ordered alphabet. INPUT: - ``data`` -- the letters of the alphabet; it can be: * a list/tuple/iterable of letters; the iterable may be infinite * an integer `n` to represent `\{1, \ldots, n\}`, or infinity to represent `\NN` - ``names`` -- (optional) a list for the letters (i.e. variable names) or a string for prefix for all letters; if given a list, it must have the same cardinality as the set represented by ``data`` - ``name`` -- (optional) if given, then return a named set and can be equal to : ``'lower', 'upper', 'space', 'underscore', 'punctuation', 'printable', 'binary', 'octal', 'decimal', 'hexadecimal', 'radix64'``. You can use many of them at once, separated by spaces : ``'lower punctuation'`` represents the union of the two alphabets ``'lower'`` and ``'punctuation'``. Alternatively, ``name`` can be set to ``"positive integers"`` (or ``"PP"``) or ``"natural numbers"`` (or ``"NN"``). ``name`` cannot be combined with ``data``. EXAMPLES: If the argument is a Set, it just returns it:: sage: build_alphabet(ZZ) is ZZ True sage: F = FiniteEnumeratedSet('abc') sage: build_alphabet(F) is F True If a list, tuple or string is provided, then it builds a proper Sage class (:class:`~sage.sets.totally_ordered_finite_set.TotallyOrderedFiniteSet`):: sage: build_alphabet([0,1,2]) {0, 1, 2} sage: F = build_alphabet('abc'); F {'a', 'b', 'c'} sage: print(type(F).__name__) TotallyOrderedFiniteSet_with_category If an integer and a set is given, then it constructs a :class:`~sage.sets.totally_ordered_finite_set.TotallyOrderedFiniteSet`:: sage: build_alphabet(3, ['a','b','c']) {'a', 'b', 'c'} If an integer and a string is given, then it considers that string as a prefix:: sage: build_alphabet(3, 'x') {'x0', 'x1', 'x2'} If no data is provided, ``name`` may be a string which describe an alphabet. The available names decompose into two families. The first one are 'positive integers', 'PP', 'natural numbers' or 'NN' which refer to standard set of numbers:: sage: build_alphabet(name="positive integers") Positive integers sage: build_alphabet(name="PP") Positive integers sage: build_alphabet(name="natural numbers") Non negative integers sage: build_alphabet(name="NN") Non negative integers The other families for the option ``name`` are among 'lower', 'upper', 'space', 'underscore', 'punctuation', 'printable', 'binary', 'octal', 'decimal', 'hexadecimal', 'radix64' which refer to standard set of characters. Theses names may be combined by separating them by a space:: sage: build_alphabet(name="lower") {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'} sage: build_alphabet(name="hexadecimal") {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'} sage: build_alphabet(name="decimal punctuation") {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', ',', '.', ';', ':', '!', '?'} In the case the alphabet is built from a list or a tuple, the order on the alphabet is given by the elements themselves:: sage: A = build_alphabet([0,2,1]) sage: A(0) < A(2) True sage: A(2) < A(1) False If a different order is needed, you may use :class:`~sage.sets.totally_ordered_finite_set.TotallyOrderedFiniteSet` and set the option ``facade`` to ``False``. That way, the comparison fits the order of the input:: sage: A = TotallyOrderedFiniteSet([4,2,6,1], facade=False) sage: A(4) < A(2) True sage: A(1) < A(6) False Be careful, the element of the set in the last example are no more integers and do not compare equal with integers:: sage: type(A.an_element()) <class 'sage.sets.totally_ordered_finite_set.TotallyOrderedFiniteSet_with_category.element_class'> sage: A(1) == 1 False sage: 1 == A(1) False We give an example of an infinite alphabet indexed by the positive integers and the prime numbers:: sage: build_alphabet(oo, 'x') Lazy family (x(i))_{i in Non negative integers} sage: build_alphabet(Primes(), 'y') Lazy family (y(i))_{i in Set of all prime numbers: 2, 3, 5, 7, ...} TESTS:: sage: Alphabet(3, name="punctuation") Traceback (most recent call last): ... ValueError: name cannot be specified with any other argument sage: Alphabet(8, ['e']*10) Traceback (most recent call last): ... ValueError: invalid value for names sage: Alphabet(8, x) Traceback (most recent call last): ... ValueError: invalid value for names sage: Alphabet(name=x, names="punctuation") Traceback (most recent call last): ... ValueError: name cannot be specified with any other argument sage: Alphabet(x) Traceback (most recent call last): ... ValueError: unable to construct an alphabet from the given parameters """ # If both 'names' and 'data' are defined if name is not None and (data is not None or names is not None): raise ValueError("name cannot be specified with any other argument") # Swap arguments if we need to try and make sure we have "good" user input if isinstance(names, (int, Integer)) or names == Infinity \ or (data is None and names is not None): data, names = names, data # data is an integer if isinstance(data, (int, Integer)): if names is None: from sage.sets.integer_range import IntegerRange return IntegerRange(Integer(data)) if isinstance(names, str): return TotallyOrderedFiniteSet( [names + '%d' % i for i in range(data)]) if len(names) == data: return TotallyOrderedFiniteSet(names) raise ValueError("invalid value for names") if data == Infinity: data = NonNegativeIntegers() # data is an iterable if isinstance(data, (tuple, list, str, range)) or data in Sets(): if names is not None: if not isinstance(names, str): raise TypeError("names must be a string when data is a set") return Family(data, lambda i: names + str(i), name=names) if data in Sets(): return data return TotallyOrderedFiniteSet(data) # Alphabet defined from a name if name is not None: if not isinstance(name, str): raise TypeError("name must be a string") if name == "positive integers" or name == "PP": from sage.sets.positive_integers import PositiveIntegers return PositiveIntegers() if name == "natural numbers" or name == "NN": return NonNegativeIntegers() data = [] for alpha_name in name.split(' '): try: data.extend(list(set_of_letters[alpha_name])) except KeyError: raise TypeError("name is not recognized") return TotallyOrderedFiniteSet(data) # Alphabet(**nothing**) if data is None: # name is also None from sage.sets.pythonclass import Set_PythonType return Set_PythonType(object) raise ValueError( "unable to construct an alphabet from the given parameters")
def QuadraticResidueCodeEvenPair(n, F): """ Quadratic residue codes of a given odd prime length and base ring either don't exist at all or occur as 4-tuples - a pair of "odd-like" codes and a pair of "even-like" codes. If `n > 2` is prime then (Theorem 6.6.2 in [HP2003]_) a QR code exists over `GF(q)` iff q is a quadratic residue mod `n`. They are constructed as "even-like" duadic codes associated the splitting (Q,N) mod n, where Q is the set of non-zero quadratic residues and N is the non-residues. EXAMPLES:: sage: codes.QuadraticResidueCodeEvenPair(17, GF(13)) # known bug (#25896) ([17, 8] Cyclic Code over GF(13), [17, 8] Cyclic Code over GF(13)) sage: codes.QuadraticResidueCodeEvenPair(17, GF(2)) ([17, 8] Cyclic Code over GF(2), [17, 8] Cyclic Code over GF(2)) sage: codes.QuadraticResidueCodeEvenPair(13,GF(9,"z")) # known bug (#25896) ([13, 6] Cyclic Code over GF(9), [13, 6] Cyclic Code over GF(9)) sage: C1,C2 = codes.QuadraticResidueCodeEvenPair(7,GF(2)) sage: C1.is_self_orthogonal() True sage: C2.is_self_orthogonal() True sage: C3 = codes.QuadraticResidueCodeOddPair(17,GF(2))[0] sage: C4 = codes.QuadraticResidueCodeEvenPair(17,GF(2))[1] sage: C3.systematic_generator_matrix() == C4.dual_code().systematic_generator_matrix() True This is consistent with Theorem 6.6.9 and Exercise 365 in [HP2003]_. TESTS:: sage: codes.QuadraticResidueCodeEvenPair(14,Zmod(4)) Traceback (most recent call last): ... ValueError: the argument F must be a finite field sage: codes.QuadraticResidueCodeEvenPair(14,GF(2)) Traceback (most recent call last): ... ValueError: the argument n must be an odd prime sage: codes.QuadraticResidueCodeEvenPair(5,GF(2)) Traceback (most recent call last): ... ValueError: the order of the finite field must be a quadratic residue modulo n """ from sage.arith.srange import srange from sage.categories.finite_fields import FiniteFields if F not in FiniteFields(): raise ValueError("the argument F must be a finite field") q = F.order() n = Integer(n) if n <= 2 or not n.is_prime(): raise ValueError("the argument n must be an odd prime") Q = quadratic_residues(n) Q.remove(0) # non-zero quad residues N = [x for x in srange(1, n) if x not in Q] # non-zero quad non-residues if q not in Q: raise ValueError( "the order of the finite field must be a quadratic residue modulo n" ) return DuadicCodeEvenPair(F, Q, N)
def __init__(self, _pol, _dm): self.pol = _pol self.dm = Integer(_dm)
def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): """ Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print e.degree_divisor ....: print e.degree_multiple 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import verbose, cputime degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen()._pari_() verbose("Starting field: %s" % Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree // absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s" % [s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd( factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac // 2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq // 2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q / q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([ 4 * a0 * a2 - a1 * a1 - a0 * a3 * a3, a1 * a3 - 4 * a0, -a2, 1 ]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq // 3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort( absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s" % [s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]" % (degree_divisor, degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s" % (f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol * (denom**Mpol.poldegree())).subst( "x", pari([0, 1 / denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s" % Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0]) - 1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod( Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL / Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s" % Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm // f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k * KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd( f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol / Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
def create_object(self, version, key, **kwds): """ EXAMPLES:: sage: K = GF(19) # indirect doctest sage: TestSuite(K).run() We try to create finite fields with various implementations:: sage: k = GF(2, impl='modn') sage: k = GF(2, impl='givaro') sage: k = GF(2, impl='ntl') sage: k = GF(2, impl='pari') Traceback (most recent call last): ... ValueError: the degree must be at least 2 sage: k = GF(2, impl='supercalifragilisticexpialidocious') Traceback (most recent call last): ... ValueError: no such finite field implementation: 'supercalifragilisticexpialidocious' sage: k.<a> = GF(2^15, impl='modn') Traceback (most recent call last): ... ValueError: the 'modn' implementation requires a prime order sage: k.<a> = GF(2^15, impl='givaro') sage: k.<a> = GF(2^15, impl='ntl') sage: k.<a> = GF(2^15, impl='pari') sage: k.<a> = GF(3^60, impl='modn') Traceback (most recent call last): ... ValueError: the 'modn' implementation requires a prime order sage: k.<a> = GF(3^60, impl='givaro') Traceback (most recent call last): ... ValueError: q must be < 2^16 sage: k.<a> = GF(3^60, impl='ntl') Traceback (most recent call last): ... ValueError: q must be a 2-power sage: k.<a> = GF(3^60, impl='pari') """ # IMPORTANT! If you add a new class to the list of classes # that get cached by this factor object, then you *must* add # the following method to that class in order to fully support # pickling: # # def __reduce__(self): # and include good doctests, please! # return self._factory_data[0].reduce_data(self) # # This is not in the base class for finite fields, since some finite # fields need not be created using this factory object, e.g., residue # class fields. if len(key) == 5: # for backward compatibility of pickles (see trac 10975). order, name, modulus, impl, _ = key p, n = Integer(order).factor()[0] proof = True prefix = kwds.get('prefix', None) # We can set the defaults here to be those for givaro # as they are otherwise ignored repr = 'poly' elem_cache = (order < 500) elif len(key) == 8: # For backward compatibility of pickles (see trac #21433) order, name, modulus, impl, _, p, n, proof = key prefix = kwds.get('prefix', None) # We can set the defaults here to be those for givaro # as they are otherwise ignored repr = kwds.get('repr', 'poly') elem_cache = kwds.get('elem_cache', (order < 500)) else: order, name, modulus, impl, p, n, proof, prefix, repr, elem_cache = key if impl == 'modn': if n != 1: raise ValueError( "the 'modn' implementation requires a prime order") from .finite_field_prime_modn import FiniteField_prime_modn # Using a check option here is probably a worthwhile # compromise since this constructor is simple and used a # huge amount. K = FiniteField_prime_modn(order, check=False, modulus=modulus) else: # We have to do this with block so that the finite field # constructors below will use the proof flag that was # passed in when checking for primality, factoring, etc. # Otherwise, we would have to complicate all of their # constructors with check options. from sage.structure.proof.all import WithProof with WithProof('arithmetic', proof): if impl == 'givaro': K = FiniteField_givaro(order, name, modulus, repr, elem_cache) elif impl == 'ntl': from .finite_field_ntl_gf2e import FiniteField_ntl_gf2e K = FiniteField_ntl_gf2e(order, name, modulus) elif impl == 'pari_ffelt' or impl == 'pari': from .finite_field_pari_ffelt import FiniteField_pari_ffelt K = FiniteField_pari_ffelt(p, modulus, name) else: raise ValueError( "no such finite field implementation: %r" % impl) # Temporary; see create_key_and_extra_args() above. if prefix is not None: K._prefix = prefix return K
def _find_scaling_period(self): r""" Uses the integral period map of the modular symbol implementation in sage in order to determine the scaling. The resulting modular symbol is correct only for the `X_0`-optimal curve, at least up to a possible factor +- a power of 2. EXAMPLES:: sage: E = EllipticCurve('11a1') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1/5, 1) sage: E = EllipticCurve('11a2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (1, 5) sage: E = EllipticCurve('121b2') sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period') sage: m._e (0, 11/2, 0, 11/2, 11/2, 0, 0, -3, 2, 1/2, -1, 3/2) TESTS:: sage: E = EllipticCurve('19a1') sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none') sage: m._find_scaling_period() sage: m._scaling 1 sage: E = EllipticCurve('19a2') sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none') sage: m._scaling 1 sage: m._find_scaling_period() sage: m._scaling 3 """ P = self._modsym.integral_period_mapping() self._e = P.matrix().transpose().row(0) self._e /= 2 E = self._E try: crla = parse_cremona_label(E.label()) except RuntimeError: # raised when curve is outside of the table print( "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by a rational number." ) self._scaling = 1 else: cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) if self._sign == 1: q = E0.period_lattice().basis()[0] / E.period_lattice().basis( )[0] else: q = E0.period_lattice().basis()[1].imag() / E.period_lattice( ).basis()[1].imag() if E0.real_components() == 1: q *= 2 if E.real_components() == 1: q /= 2 q = QQ(int(round(q * 200))) / 200 verbose('scale modular symbols by %s' % q) self._scaling = q c = self(0) # required, to change base point from oo to 0 if c < 0: c *= -1 self._scaling *= -1 self._at_zero = c self._e *= self._scaling
def are_hyperplanes_in_projective_geometry_parameters(v, k, lmbda, return_parameters=False): r""" Return ``True`` if the parameters ``(v,k,lmbda)`` are the one of hyperplanes in a (finite Desarguesian) projective space. In other words, test whether there exists a prime power ``q`` and an integer ``d`` greater than two such that: - `v = (q^{d+1}-1)/(q-1) = q^d + q^{d-1} + ... + 1` - `k = (q^d - 1)/(q-1) = q^{d-1} + q^{d-2} + ... + 1` - `lmbda = (q^{d-1}-1)/(q-1) = q^{d-2} + q^{d-3} + ... + 1` If it exists, such a pair ``(q,d)`` is unique. INPUT: - ``v,k,lmbda`` (integers) OUTPUT: - a boolean or, if ``return_parameters`` is set to ``True`` a pair ``(True, (q,d))`` or ``(False, (None,None))``. EXAMPLES:: sage: from sage.combinat.designs.block_design import are_hyperplanes_in_projective_geometry_parameters sage: are_hyperplanes_in_projective_geometry_parameters(40,13,4) True sage: are_hyperplanes_in_projective_geometry_parameters(40,13,4,return_parameters=True) (True, (3, 3)) sage: PG = designs.ProjectiveGeometryDesign(3,2,GF(3)) sage: PG.is_t_design(return_parameters=True) (True, (2, 40, 13, 4)) sage: are_hyperplanes_in_projective_geometry_parameters(15,3,1) False sage: are_hyperplanes_in_projective_geometry_parameters(15,3,1,return_parameters=True) (False, (None, None)) TESTS:: sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1)) sage: for q in [3,4,5,7,8,9,11]: ....: for d in [2,3,4,5]: ....: v,k,l = sgp(q,d) ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l,True) == (True, (q,d)) ....: assert are_hyperplanes_in_projective_geometry_parameters(v+1,k,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v-1,k,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k+1,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k-1,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l+1) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l-1) is False """ import sage.arith.all as arith q1 = Integer(v - k) q2 = Integer(k - lmbda) if (lmbda <= 0 or q1 < 4 or q2 < 2 or not q1.is_prime_power() or not q2.is_prime_power()): return (False, (None, None)) if return_parameters else False p1, e1 = q1.factor()[0] p2, e2 = q2.factor()[0] k = arith.gcd(e1, e2) d = e1 // k q = p1**k if e2 // k != d - 1 or lmbda != (q**(d - 1) - 1) // (q - 1): return (False, (None, None)) if return_parameters else False return (True, (q, d)) if return_parameters else True
def polynomial(self, n): r""" Return the pseudo-Conway polynomial of degree `n` in this lattice. INPUT: - ``n`` -- positive integer OUTPUT: - a pseudo-Conway polynomial of degree `n` for the prime `p`. ALGORITHM: Uses an algorithm described in [HL99]_, modified to find pseudo-Conway polynomials rather than Conway polynomials. The major difference is that we stop as soon as we find a primitive polynomial. REFERENCE: .. [HL99] \L. Heath and N. Loehr (1999). New algorithms for generating Conway polynomials over finite fields. Proceedings of the tenth annual ACM-SIAM symposium on discrete algorithms, pp. 429-437. EXAMPLES:: sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) sage: PCL.polynomial(3) x^3 + x + 1 sage: PCL.polynomial(4) x^4 + x^3 + 1 sage: PCL.polynomial(60) x^60 + x^59 + x^58 + x^55 + x^54 + x^53 + x^52 + x^51 + x^48 + x^46 + x^45 + x^42 + x^41 + x^39 + x^38 + x^37 + x^35 + x^32 + x^31 + x^30 + x^28 + x^24 + x^22 + x^21 + x^18 + x^17 + x^16 + x^15 + x^14 + x^10 + x^8 + x^7 + x^5 + x^3 + x^2 + x + 1 """ if n in self.nodes: return self.nodes[n] p = self.p n = Integer(n) if n == 1: f = self.ring.gen() - FiniteField(p).multiplicative_generator() self.nodes[1] = f return f # Work in an arbitrary field K of order p**n. K = FiniteField(p**n, names='a') # TODO: something like the following # gcds = [n.gcd(d) for d in self.nodes.keys()] # xi = { m: (...) for m in gcds } xi = { q: self.polynomial(n // q).any_root(K, -n // q, assume_squarefree=True) for q in n.prime_divisors() } # The following is needed to ensure that in the concrete instantiation # of the "new" extension all previous choices are compatible. _frobenius_shift(K, xi) # Construct a compatible element having order the lcm of orders q, x = xi.popitem() v = p**(n // q) - 1 for q, xitem in six.iteritems(xi): w = p**(n // q) - 1 g, alpha, beta = v.xgcd(w) x = x**beta * xitem**alpha v = v.lcm(w) r = p**n - 1 # Get the missing part of the order to be primitive g = r // v # Iterate through g-th roots of x until a primitive one is found z = x.nth_root(g) root = K.multiplicative_generator()**v while z.multiplicative_order() != r: z *= root # The following should work but tries to create a huge list # whose length overflows Python's ints for large parameters #Z = x.nth_root(g, all=True) #for z in Z: # if z.multiplicative_order() == r: # break f = z.minimal_polynomial() self.nodes[n] = f return f
def MOLS_table(start,stop=None,compare=False,width=None): r""" Prints the MOLS table that Sage can produce. INPUT: - ``start,stop`` (integers) -- print the table of MOLS for value of `n` such that ``start<=n<stop``. If only one integer is given as input, it is interpreted as the value of ``stop`` with ``start=0`` (same behaviour as ``range``). - ``compare`` (boolean) -- if sets to ``True`` the MOLS displays with `+` and `-` entries its difference with the table from the Handbook of Combinatorial Designs (2ed). - ``width`` (integer) -- the width of each column of the table. By default, it is computed from range of values determined by the parameters ``start`` and ``stop``. EXAMPLES:: sage: from sage.combinat.designs.latin_squares import MOLS_table sage: MOLS_table(100) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 5 18 20| 4 5 3 22 7 24 4 26 5 28 4 30 31 5 4 5 8 36 4 5 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 5 58 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 80| 9 80 8 82 6 6 6 6 7 88 6 7 6 6 6 6 7 96 6 8 sage: MOLS_table(100, width=4) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ____________________________________________________________________________________________________ 0| +oo +oo 1 2 3 4 1 6 7 8 2 10 5 12 4 4 15 16 5 18 20| 4 5 3 22 7 24 4 26 5 28 4 30 31 5 4 5 8 36 4 5 40| 7 40 5 42 5 6 4 46 8 48 6 5 5 52 5 6 7 7 5 58 60| 5 60 5 6 63 7 5 66 5 6 6 70 7 72 5 7 6 6 6 78 80| 9 80 8 82 6 6 6 6 7 88 6 7 6 6 6 6 7 96 6 8 sage: MOLS_table(100, compare=True) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ 0| + + 20| 40| 60| + 80| sage: MOLS_table(50, 100, compare=True) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ________________________________________________________________________________ 40| 60| + 80| """ from .orthogonal_arrays import largest_available_k if stop is None: start,stop = 0,start # make start and stop be congruent to 0 mod 20 start = start - (start%20) stop = stop-1 stop = stop + (20-(stop%20)) assert start%20 == 0 and stop%20 == 0 if stop <= start: return if compare: from sage.env import SAGE_SHARE handbook_file = open(SAGE_SHARE+"/combinatorial_designs/MOLS_table.txt",'r') hb = [int(_) for _ in handbook_file.readlines()[9].split(',')] handbook_file.close() # choose an appropriate width (needs to be >= 3 because "+oo" should fit) if width is None: from sage.rings.integer import Integer width = max(3, Integer(stop-1).ndigits(10)) print(" " * (width + 2) + " ".join("{i:>{width}}".format(i=i,width=width) for i in range(20))) print(" " * (width + 1) + "_" * ((width + 1) * 20), end="") for i in range(start,stop): if i % 20 == 0: print("\n{:>{width}}|".format(i, width=width), end="") k = largest_available_k(i)-2 if compare: if i < 2 or hb[i] == k: c = "" elif hb[i] < k: c = "+" else: c = "-" else: if i < 2: c = "+oo" else: c = k print(' {:>{width}}'.format(c, width=width), end="")
def _derivative_(self, z, m, diff_param): """ EXAMPLES:: sage: x,m = var('x,m') sage: elliptic_f(x,m).diff(x) 1/sqrt(-m*sin(x)^2 + 1) sage: elliptic_f(x,m).diff(m) -1/2*elliptic_f(x, m)/m + 1/4*sin(2*x)/(sqrt(-m*sin(x)^2 + 1)*(m - 1)) - 1/2*elliptic_e(x, m)/((m - 1)*m) """ if diff_param == 0: return Integer(1) / sqrt(Integer(1) - m * sin(z)**Integer(2)) elif diff_param == 1: return (elliptic_e(z, m) / (Integer(2) * (Integer(1) - m) * m) - elliptic_f(z, m) / (Integer(2) * m) - (sin(Integer(2) * z) / (Integer(4) * (Integer(1) - m) * sqrt(Integer(1) - m * sin(z)**Integer(2)))))
def frequency_distribution(self, length=1, prec=0): """ Returns the probability space of character frequencies. The output of this method is different from that of the method :func:`characteristic_frequency() <sage.monoids.string_monoid.AlphabeticStringMonoid.characteristic_frequency>`. One can think of the characteristic frequency probability of an element in an alphabet `A` as the expected probability of that element occurring. Let `S` be a string encoded using elements of `A`. The frequency probability distribution corresponding to `S` provides us with the frequency probability of each element of `A` as observed occurring in `S`. Thus one distribution provides expected probabilities, while the other provides observed probabilities. INPUT: - ``length`` -- (default ``1``) if ``length=1`` then consider the probability space of monogram frequency, i.e. probability distribution of single characters. If ``length=2`` then consider the probability space of digram frequency, i.e. probability distribution of pairs of characters. This method currently supports the generation of probability spaces for monogram frequency (``length=1``) and digram frequency (``length=2``). - ``prec`` -- (default ``0``) a non-negative integer representing the precision (in number of bits) of a floating-point number. The default value ``prec=0`` means that we use 53 bits to represent the mantissa of a floating-point number. For more information on the precision of floating-point numbers, see the function :func:`RealField() <sage.rings.real_mpfr.RealField>` or refer to the module :mod:`real_mpfr <sage.rings.real_mpfr>`. EXAMPLES: Capital letters of the English alphabet:: sage: M = AlphabeticStrings().encoding("abcd") sage: L = M.frequency_distribution().function() sage: sorted(L.items()) <BLANKLINE> [(A, 0.250000000000000), (B, 0.250000000000000), (C, 0.250000000000000), (D, 0.250000000000000)] The binary number system:: sage: M = BinaryStrings().encoding("abcd") sage: L = M.frequency_distribution().function() sage: sorted(L.items()) [(0, 0.593750000000000), (1, 0.406250000000000)] The hexadecimal number system:: sage: M = HexadecimalStrings().encoding("abcd") sage: L = M.frequency_distribution().function() sage: sorted(L.items()) <BLANKLINE> [(1, 0.125000000000000), (2, 0.125000000000000), (3, 0.125000000000000), (4, 0.125000000000000), (6, 0.500000000000000)] Get the observed frequency probability distribution of digrams in the string "ABCD". This string consists of the following digrams: "AB", "BC", and "CD". Now find out the frequency probability of each of these digrams as they occur in the string "ABCD":: sage: M = AlphabeticStrings().encoding("abcd") sage: D = M.frequency_distribution(length=2).function() sage: sorted(D.items()) [(AB, 0.333333333333333), (BC, 0.333333333333333), (CD, 0.333333333333333)] """ if not length in (1, 2): raise NotImplementedError("Not implemented") if prec == 0: RR = RealField() else: RR = RealField(prec) S = self.parent() n = S.ngens() if length == 1: Alph = S.gens() else: Alph = tuple([ x*y for x in S.gens() for y in S.gens() ]) X = {} N = len(self)-length+1 eps = RR(Integer(1)/N) for i in range(N): c = self[i:i+length] if c in X: X[c] += eps else: X[c] = eps # Return a dictionary of probability distribution. This should # allow for easier parsing of the dictionary. from sage.probability.random_variable import DiscreteProbabilitySpace return DiscreteProbabilitySpace(Alph, X, RR)
def carmichael_lambda(n): r""" Return the Carmichael function of a positive integer ``n``. The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k` is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. INPUT: - ``n`` -- a positive integer. OUTPUT: - The Carmichael function of ``n``. ALGORITHM: If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd prime and let `k` be a positive integer. Then `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the prime factorization of `n`. Then .. MATH:: \lambda(n) = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}) = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t})) EXAMPLES: The Carmichael function of all positive integers up to and including 10:: sage: from sage.crypto.util import carmichael_lambda sage: list(map(carmichael_lambda, [1..10])) [1, 1, 2, 2, 4, 2, 6, 2, 6, 4] The Carmichael function of the first ten primes:: sage: list(map(carmichael_lambda, primes_first_n(10))) [1, 2, 4, 6, 10, 12, 16, 18, 22, 28] Cases where the Carmichael function is equivalent to the Euler phi function:: sage: carmichael_lambda(2) == euler_phi(2) True sage: carmichael_lambda(4) == euler_phi(4) True sage: p = random_prime(1000, lbound=3, proof=True) sage: k = randint(1, 1000) sage: carmichael_lambda(p^k) == euler_phi(p^k) True A case where `\lambda(n) \neq \varphi(n)`:: sage: k = randint(1, 1000) sage: carmichael_lambda(2^k) == 2^(k - 2) True sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k) False Verifying the current implementation of the Carmichael function using another implementation. The other implementation that we use for verification is an exhaustive search for the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. :: sage: from sage.crypto.util import carmichael_lambda sage: n = randint(1, 500) sage: c = carmichael_lambda(n) sage: def coprime(n): ....: return [i for i in range(n) if gcd(i, n) == 1] sage: def znpower(n, k): ....: L = coprime(n) ....: return list(map(power_mod, L, [k]*len(L), [n]*len(L))) sage: def my_carmichael(n): ....: for k in range(1, n): ....: L = znpower(n, k) ....: ones = [1] * len(L) ....: T = [L[i] == ones[i] for i in range(len(L))] ....: if all(T): ....: return k sage: c == my_carmichael(n) True Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}` for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. Here, we verify Carmichael's theorem. :: sage: from sage.crypto.util import carmichael_lambda sage: n = randint(1, 1000) sage: c = carmichael_lambda(n) sage: ZnZ = IntegerModRing(n) sage: M = ZnZ.list_of_elements_of_multiplicative_group() sage: ones = [1] * len(M) sage: P = [power_mod(a, c, n) for a in M] sage: P == ones True TESTS: The input ``n`` must be a positive integer:: sage: from sage.crypto.util import carmichael_lambda sage: carmichael_lambda(0) Traceback (most recent call last): ... ValueError: Input n must be a positive integer. sage: carmichael_lambda(randint(-10, 0)) Traceback (most recent call last): ... ValueError: Input n must be a positive integer. Bug reported in :trac:`8283`:: sage: from sage.crypto.util import carmichael_lambda sage: type(carmichael_lambda(16)) <type 'sage.rings.integer.Integer'> REFERENCES: - :wikipedia:`Carmichael_function` """ n = Integer(n) # sanity check if n < 1: raise ValueError("Input n must be a positive integer.") L = n.factor() t = [] # first get rid of the prime factor 2 if n & 1 == 0: e = L[0][1] L = L[1:] # now, n = 2**e * L.value() if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1) e = e - 1 else: # for k >= 3, lambda(2**k) = 2**(k - 2) e = e - 2 t.append(1 << e) # 2**e # then other prime factors t += [p**(k - 1) * (p - 1) for p, k in L] # finish the job return lcm(t)
def __add__(self, Nelt): r""" Concatenate two ascii art object. By default, when two object are concatenated, the new one will be splittable between both. If the baseline is defined, the concatenation is computed such that the new baseline coincidate with the olders. For example, let `T` be a tree with it's baseline ascii art representation in the middle:: o \ o / \ o o and let `M` be a matrix with it's baseline ascii art representation at the middle two:: [1 2 3] [4 5 6] [7 8 9] then the concatenation of both will give:: o \ [1 2 3] o [4 5 6] / \ [7 8 9] o o If one of the objects has not baseline, the concatenation is realized from the top:: o [1 2 3] \ [4 5 6] o [7 8 9] / \ o o TESTS:: sage: from sage.misc.ascii_art import AsciiArt sage: l5 = AsciiArt(lines=['|' for _ in range(5)], baseline=2); l5 | | | | | sage: l3 = AsciiArt(lines=['|' for _ in range(3)], baseline=1); l3 | | | sage: l3 + l5 | || || || | sage: l5 + l3 | || || || | sage: l5._baseline = 0 sage: l5 + l3 | | | || || | sage: l5._baseline = 4 sage: l5 + l3 | || || | | | sage: l3._baseline = 0 sage: l3 + l5 | | || | | | | """ new_matrix = [] new_h = AsciiArt._compute_new_h(self, Nelt) new_baseline = AsciiArt._compute_new_baseline(self, Nelt) if self._baseline != None and Nelt._baseline != None: # left traitement for line in self._matrix: new_matrix.append(line + " "**Integer(self._l - len(line))) if new_h > self._h: # | new_h > self._h # | new_baseline > self._baseline # ||<-- baseline number of white lines at the bottom # | } :: Nelt._baseline - self._baseline # | } if new_baseline > self._baseline: for k in range(new_baseline - self._baseline): new_matrix.append(" "**Integer(self._l)) # | } new_h > self._h # | } new_h - new_baseline > self._h - self._baseline # ||<-- baseline number of white lines at the top # || :: new_h - new_baseline - self._h + self._baseline # || # | # | if new_h - new_baseline > self._h - self._baseline: for _ in range((new_h - new_baseline) - (self._h - self._baseline)): new_matrix.insert(0, " "**Integer(self._l)) # right traitement i = 0 if new_h > Nelt._h: # | } new_h > Nelt._h # | } new_h - new_baseline > Nelt._h - self._baseline # ||<-- baseline number of white lines at the top # || :: new_h - new_baseline - Nelt._h + Nelt._baseline # || # || # | i = max(new_h - new_baseline - Nelt._h + Nelt._baseline, 0) for j in range(Nelt._h): new_matrix[i + j] += Nelt._matrix[j] else: for line in self._matrix: new_matrix.append(line + " "**Integer(self._l - len(line))) for i, line_i in enumerate(Nelt._matrix): if i == len(new_matrix): new_matrix.append(" "**Integer(self._l) + line_i) else: new_matrix[i] += line_i # breakpoint new_breakpoints = list(self._breakpoints) new_breakpoints.append(self._l) for bp in Nelt._breakpoints: new_breakpoints.append(bp + self._l) from sage.misc.misc import uniq return AsciiArt(lines=new_matrix, breakpoints=uniq(new_breakpoints), baseline=new_baseline, atomic=False)
def period(self, m): """ Return the period of the binary recurrence sequence modulo an integer ``m``. If `n_1` is congruent to `n_2` modulo ``period(m)``, then `u_{n_1}` is is congruent to `u_{n_2}` modulo ``m``. INPUT: - ``m`` -- an integer (modulo which the period of the recurrence relation is calculated). OUTPUT: - The integer (the period of the sequence modulo m) EXAMPLES: If `p = \\pm 1 \\mod 5`, then the period of the Fibonacci sequence mod `p` is `p-1` (c.f. Lemma 3.3 of [BMS2006]_). :: sage: R = BinaryRecurrenceSequence(1,1) sage: R.period(31) 30 sage: [R(i) % 4 for i in range(12)] [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1] sage: R.period(4) 6 This function works for degenerate sequences as well. :: sage: S = BinaryRecurrenceSequence(2,0,1,2) sage: S.is_degenerate() True sage: S.is_geometric() True sage: [S(i) % 17 for i in range(16)] [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9] sage: S.period(17) 8 Note: the answer is cached. """ #If we have already computed the period mod m, then we return the stored value. if m in self._period_dict: return self._period_dict[m] else: R = Integers(m) A = matrix(R, [[0, 1], [self.c, self.b]]) w = matrix(R, [[self.u0], [self.u1]]) Fac = list(m.factor()) Periods = {} #To compute the period mod m, we compute the least integer n such that A^n*w == w. This necessarily #divides the order of A as a matrix in GL_2(Z/mZ). #We compute the period modulo all distinct prime powers dividing m, and combine via the lcm. #To compute the period mod p^e, we first compute the order mod p. Then the period mod p^e #must divide p^{4e-4}*period(p), as the subgroup of matrices mod p^e, which reduce to #the identity mod p is of order (p^{e-1})^4. So we compute the period mod p^e by successively #multiplying the period mod p by powers of p. for i in Fac: p = i[0] e = i[1] #first compute the period mod p if p in self._period_dict: perp = self._period_dict[p] else: F = A.change_ring(GF(p)) v = w.change_ring(GF(p)) FF = F**(p - 1) p1fac = list((p - 1).factor()) #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1). #The order divides p-1 if it is diagonalizable. In any case, det(F^(p-1))=1, #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]]. #The order of the subgroup of matrices of this form is p, so the order must divide #p(p-1) -- in fact it must be a multiple of p. If this is not the case, then the #order divides (p-1)(p+1). As the period divides the order of the matrix in GL_2(F_p), #these conditions hold for the period as well. #check if the order divides (p-1) if FF * v == v: M = p - 1 Mfac = p1fac #check if the trace is 2, then the order is a multiple of p dividing p*(p-1) elif (FF).trace() == 2: M = p - 1 Mfac = p1fac F = F**p #replace F by F^p as now we only need to determine the factor dividing (p-1) #otherwise it will divide (p+1)(p-1) else: M = (p + 1) * (p - 1) p2fac = list( (p + 1).factor() ) #factor the (p+1) and (p-1) terms separately and then combine for speed Mfac_dic = {} for i in list(p1fac + p2fac): if i[0] not in Mfac_dic: Mfac_dic[i[0]] = i[1] else: Mfac_dic[i[0]] = Mfac_dic[i[0]] + i[1] Mfac = [(i, Mfac_dic[i]) for i in Mfac_dic] #Now use a fast order algorithm to compute the period. We know that the period divides #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors. As #F^M*v == v, for each i_j, if F^(M/i_j)*v == v, then the period divides (M/i_j). After #all factors have been iterated over, the result is the period mod p. Mfac = list(Mfac) C = [] #expand the list of prime factors so every factor is with multiplicity 1 for i in range(len(Mfac)): for j in range(Mfac[i][1]): C.append(Mfac[i][0]) Mfac = C n = M for i in Mfac: b = Integer(n / i) if F**b * v == v: n = b perp = n #Now compute the period mod p^e by stepping up by multiples of p F = A.change_ring(Integers(p**e)) v = w.change_ring(Integers(p**e)) FF = F**perp if FF * v == v: perpe = perp else: tries = 0 while True: tries += 1 FF = FF**p if FF * v == v: perpe = perp * p**tries break Periods[p] = perpe #take the lcm of the periods mod all distinct primes dividing m period = 1 for p in Periods: period = lcm(Periods[p], period) self._period_dict[m] = period #cache the period mod m return period
def cardinality(self): """ Return the number of linear extensions. EXAMPLES:: sage: N = Poset({0: [2, 3], 1: [3]}) sage: N.linear_extensions().cardinality() 5 TESTS:: sage: Poset().linear_extensions().cardinality() 1 sage: Posets.ChainPoset(1).linear_extensions().cardinality() 1 sage: Posets.BooleanLattice(4).linear_extensions().cardinality() 1680384 """ from sage.rings.integer import Integer n = len(self._poset) if not n: return Integer(1) up = self._poset._hasse_diagram.to_dictionary() # Convert to the Hasse diagram so our poset can be realized on # the set {0,...,n-1} with a nice dictionary of edges for i in range(n): up[n - 1 - i] = sorted( set(up[n - 1 - i] + [item for x in up[n - 1 - i] for item in up[x]])) # Compute the principal order filter for each element. Jup = {1: []} # Jup will be a dictionary giving up edges in J(P) # We will perform a loop where after k loops, we will have a # list of up edges for the lattice of order ideals for P # restricted to entries 0,...,k. loc = [1] * n # This list will be indexed by entries in P. After k loops, # the entry loc[i] will correspond to the element of J(P) that # is the principal order ideal of i, restricted to the # elements 0,...,k . m = 1 # m keeps track of how many elements we currently have in J(P). # We start with just the empty order ideal, and no relations. for x in range(n): # Use the existing Jup table to compute all covering # relations in J(P) for things that are above loc(x). K = [[loc[x]]] j = 0 while K[j]: K.append([b for a in K[j] for b in Jup[a]]) j += 1 K = sorted(set(item for sublist in K for item in sublist)) for j in range(len(K)): i = m + j + 1 Jup[i] = [m + K.index(a) + 1 for a in Jup[K[j]]] # These are copies of the covering relations with # elements from K, but now with the underlying # elements containing x. Jup[K[j]] = Jup[K[j]] + [i] # There are the new covering relations we get between # ideals that don't contain x and those that do. for y in up[x]: loc[y] = K.index(loc[y]) + m + 1 # Updates loc[y] if y is above x. m += len(K) # Now we have a dictionary of covering relations for J(P). The # following shortcut works to count maximal chains, since we # made J(P) naturally labelled, and J(P) has a unique maximal # element and minimum element. Jup[m] = Integer(1) while m > 1: m -= 1 ct = Integer(0) for j in Jup[m]: ct += Jup[j] Jup[m] = ct return ct
def frac(a, b): """ Return the fraction ``a/b``. """ return Integer(a) / Integer(b)
def __init__(self, *l, **d): """ TESTS:: sage: s = AbelianStratum(0) sage: s == loads(dumps(s)) True sage: s = AbelianStratum(1,1,1,1) sage: s == loads(dumps(s)) True sage: AbelianStratum('no','way') Traceback (most recent call last): ... ValueError: input must be a list of integers sage: AbelianStratum([1,1,1,1], marked_separatrix='full') Traceback (most recent call last): ... ValueError: marked_separatrix must be one of 'no', 'in', 'out' """ if l == (): pass elif hasattr(l[0], "__iter__") and len(l) == 1: l = l[0] if not all(isinstance(i, (Integer, int)) for i in l): raise ValueError("input must be a list of integers") if 'marked_separatrix' in d: m = d['marked_separatrix'] if m is None: m = 'no' if (m != 'no' and m != 'in' and m != 'out'): raise ValueError("marked_separatrix must be one of 'no', " "'in', 'out'") self._marked_separatrix = m else: # default value self._marked_separatrix = 'no' self._zeroes = list(l) if not self._marked_separatrix == 'no': self._zeroes[1:] = sorted(self._zeroes[1:], reverse=True) else: self._zeroes.sort(reverse=True) self._genus = sum(l) / 2 + 1 self._genus = Integer(self._genus) zeroes = sorted(x for x in self._zeroes if x > 0) if self._genus == 1: self._cc = (HypCCA, ) elif self._genus == 2: self._cc = (HypCCA, ) elif self._genus == 3: if zeroes == [2, 2] or zeroes == [4]: self._cc = (HypCCA, OddCCA) else: self._cc = (CCA, ) elif len(zeroes) == 1: # just one zeros [2g-2] self._cc = (HypCCA, OddCCA, EvenCCA) elif zeroes == [self._genus - 1, self._genus - 1]: # two similar zeros [g-1, g-1] if self._genus % 2 == 0: self._cc = (HypCCA, NonHypCCA) else: self._cc = (HypCCA, OddCCA, EvenCCA) elif len([x for x in zeroes if x % 2]) == 0: # even zeroes [2 l_1, 2 l_2, ..., 2 l_n] self._cc = (OddCCA, EvenCCA) else: self._cc = (CCA, )
def _render_on_subplot(self, subplot): """ TESTS: A somewhat random plot, but fun to look at:: sage: x,y = var('x,y') sage: contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv') """ from sage.rings.integer import Integer options = self.options() fill = options['fill'] contours = options['contours'] if options.has_key('cmap'): cmap = get_cmap(options['cmap']) elif fill or contours is None: cmap = get_cmap('gray') else: if isinstance(contours, (int, Integer)): cmap = get_cmap([(i, i, i) for i in xsrange(0, 1, 1 / contours)]) else: l = Integer(len(contours)) cmap = get_cmap([(i, i, i) for i in xsrange(0, 1, 1 / l)]) x0, x1 = float(self.xrange[0]), float(self.xrange[1]) y0, y1 = float(self.yrange[0]), float(self.yrange[1]) if isinstance(contours, (int, Integer)): contours = int(contours) CSF = None if fill: if contours is None: CSF = subplot.contourf(self.xy_data_array, cmap=cmap, extent=(x0, x1, y0, y1), label=options['legend_label']) else: CSF = subplot.contourf(self.xy_data_array, contours, cmap=cmap, extent=(x0, x1, y0, y1), extend='both', label=options['legend_label']) linewidths = options.get('linewidths', None) if isinstance(linewidths, (int, Integer)): linewidths = int(linewidths) elif isinstance(linewidths, (list, tuple)): linewidths = tuple(int(x) for x in linewidths) linestyles = options.get('linestyles', None) if contours is None: CS = subplot.contour(self.xy_data_array, cmap=cmap, extent=(x0, x1, y0, y1), linewidths=linewidths, linestyles=linestyles, label=options['legend_label']) else: CS = subplot.contour(self.xy_data_array, contours, cmap=cmap, extent=(x0, x1, y0, y1), linewidths=linewidths, linestyles=linestyles, label=options['legend_label']) if options.get('labels', False): label_options = options['label_options'] label_options['fontsize'] = int(label_options['fontsize']) if fill and label_options is None: label_options['inline'] = False subplot.clabel(CS, **label_options) if options.get('colorbar', False): colorbar_options = options['colorbar_options'] from matplotlib import colorbar cax, kwds = colorbar.make_axes_gridspec(subplot, **colorbar_options) if CSF is None: cb = colorbar.Colorbar(cax, CS, **kwds) else: cb = colorbar.Colorbar(cax, CSF, **kwds) cb.add_lines(CS)
from sage.modular.modsym.all import ModularSymbols from sage.libs.eclib.newforms import ECModularSymbol from sage.databases.cremona import parse_cremona_label from sage.arith.all import next_prime, kronecker_symbol, prime_divisors, valuation from sage.rings.infinity import unsigned_infinity as infinity from sage.rings.integer import Integer from sage.modular.cusps import Cusps from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.misc.all import verbose from sage.schemes.elliptic_curves.constructor import EllipticCurve oo = Cusps(infinity) zero = Integer(0) def modular_symbol_space(E, sign, base_ring, bound=None): r""" Creates the space of modular symbols of a given sign over a give base_ring, attached to the isogeny class of the elliptic curve ``E``. INPUT: - ``E`` - an elliptic curve over `\QQ` - ``sign`` - integer, -1, 0, or 1 - ``base_ring`` - ring - ``bound`` - (default: None) maximum number of Hecke operators to use to cut out modular symbols factor. If None, use enough to provably get the correct answer.
def _cardinality_from_iterator(self, *ignored_args, **ignored_kwds): """ The cardinality of ``self``. OUTPUT: an ``Integer`` This brute force implementation of :meth:`cardinality` iterates through the elements of ``self`` to count them. EXAMPLES:: sage: C = FiniteEnumeratedSets().example(); C An example of a finite enumerated set: {1,2,3} sage: C._cardinality_from_iterator() 3 This is the default implementation of :meth:`cardinality` from the category ``FiniteEnumeratedSet()``. To test this, we need a fresh example:: sage: from sage.categories.examples.finite_enumerated_sets import Example sage: class FreshExample(Example): pass sage: C = FreshExample(); C.rename("FreshExample") sage: C.cardinality <bound method FreshExample_with_category._cardinality_from_iterator of FreshExample> TESTS: This method shall return an ``Integer``; we test this here, because :meth:`_test_enumerated_set_iter_cardinality` does not do it for us:: sage: type(C._cardinality_from_iterator()) <type 'sage.rings.integer.Integer'> We ignore additional inputs since during doctests classes which override ``cardinality()`` call up to the category rather than their own ``cardinality()`` method (see :trac:`13688`):: sage: C = FiniteEnumeratedSets().example() sage: C._cardinality_from_iterator(algorithm='testing') 3 Here is a more complete example:: sage: class TestParent(Parent): ... def __init__(self): ... Parent.__init__(self, category=FiniteEnumeratedSets()) ... def __iter__(self): ... yield 1 ... return ... def cardinality(self, dummy_arg): ... return 1 # we don't want to change the semantics of cardinality() sage: P = TestParent() sage: P.cardinality(-1) 1 sage: v = P.list(); v [1] sage: P.cardinality() 1 sage: P.cardinality('use alt algorithm') # Used to break here: see :trac:`13688` 1 sage: P.cardinality(dummy_arg='use alg algorithm') # Used to break here: see :trac:`13688` 1 """ c = 0 for _ in self: c += 1 return Integer(c)
def defining_set(self, primitive_root=None): r""" Returns the set of exponents of the roots of ``self``'s generator polynomial over the extension field. Of course, it depends on the choice of the primitive root of the splitting field. INPUT: - ``primitive_root`` (optional) -- a primitive root of the extension field EXAMPLES: We provide a defining set at construction time:: sage: F = GF(16, 'a') sage: n = 15 sage: C = codes.CyclicCode(length=n, field=F, D=[1,2]) sage: C.defining_set() [1, 2] If the defining set was provided by the user, it might have been expanded at construction time. In this case, the expanded defining set will be returned:: sage: C = codes.CyclicCode(length=13, field=F, D=[1, 2]) sage: C.defining_set() [1, 2, 3, 5, 6, 9] If a generator polynomial was passed at construction time, the defining set is computed using this polynomial:: sage: R.<x> = F[] sage: n = 7 sage: g = x ** 3 + x + 1 sage: C = codes.CyclicCode(generator_pol=g, length=n) sage: C.defining_set() [1, 2, 4] Both operations give the same result:: sage: C1 = codes.CyclicCode(length=n, field=F, D=[1, 2, 4]) sage: C1.generator_polynomial() == g True Another one, in a reversed order:: sage: n = 13 sage: C1 = codes.CyclicCode(length=n, field=F, D=[1, 2]) sage: g = C1.generator_polynomial() sage: C2 = codes.CyclicCode(generator_pol=g, length=n) sage: C1.defining_set() == C2.defining_set() True """ if (hasattr(self, "_defining_set") and (primitive_root is None or primitive_root == self._primitive_root)): return self._defining_set else: F = self.base_field() n = self.length() q = F.cardinality() g = self.generator_polynomial() s = Zmod(n)(q).multiplicative_order() if primitive_root is None: Fsplit, F_to_Fsplit = F.extension(Integer(s), map=True) FE = RelativeFiniteFieldExtension(Fsplit, F, embedding=F_to_Fsplit) alpha = Fsplit.zeta(n) else: try: alpha = primitive_root Fsplit = alpha.parent() FE = RelativeFiniteFieldExtension(Fsplit, F) F_to_Fsplit = FE.embedding() except ValueError: raise ValueError("primitive_root does not belong to the " "right splitting field") if alpha.multiplicative_order() != n: raise ValueError("primitive_root must have multiplicative " "order equal to the code length") Rsplit = Fsplit['xx'] gsplit = Rsplit([F_to_Fsplit(coeff) for coeff in g]) roots = gsplit.roots(multiplicities=False) D = [root.log(alpha) for root in roots] self._field_embedding = FE self._primitive_root = alpha self._defining_set = sorted(D) return self._defining_set